並查集-按秩合並


並查集有兩種優化。第一種是直接連根——雖然是O(n)但是會破壞樹形結構。

按秩合並

UVA11354(莫得原地址洛谷的湊合一下)

大意:求最小生成樹的兩個點間的最大路徑。

帶邊權的並查集?多組數據?

我們按秩合並。

基本思想是使包含較少結點的樹的根指向包含較多結點的樹的根。

我們存邊時,用結構體存邊。但不用前向星。因為如果要kruskal的話需要改動一下。本來我們連接最短的邊時,如果兩端的端點的父親不一樣的話直接連上就可以了。但是按秩排序不一樣。他既要優化又要不破壞樹形結構,我們就造一個秩rank,rank[i]表示的相當於子樹大小或者深度的東西,每次查詢就把小的連到大的上並直接把邊權直接連到父親結點上,那么就能保留原來的樹形結構並做到優化。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<ctype.h>
#include<cstring>
using namespace std;
const int maxn=50010;
inline int read()
{
    int x=0,w=1;char c=getchar();
    while(!isdigit(c)){
        if(c=='-')w=-1;
        c=getchar();
    }
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*w;
}
int n,m,t;

int fa[maxn],e[maxn],rank[maxn];
struct data{ //這是邊 
    int x,y,z;
}a[maxn];

inline bool cmp(data a,data b){return a.z<b.z;}
inline int find(int x){return fa[x]==x?x:find(fa[x]);}
inline void kruskal()
{
    int s=0,f1,f2;
    for(int i=1;i<=n;i++)fa[i]=i,rank[i]=1;
    for(int i=1;i<=m;i++)
    {
        f1=find(a[i].x);
        f2=find(a[i].y);
        if(f1!=f2)
        {
            if(rank[f1]<rank[f2])
            {
                fa[f1]=f2;
                e[f1]=a[i].z;
                rank[f2]=max(rank[f2],rank[f1]+1);
            }//高度大的做小的的根 
            else{
                fa[f2]=f1;
                e[f2]=a[i].z;
                rank[f1]=max(rank[f1],rank[f2]+1);
            }//相等或者相反就相反來 
            s++;
        }
        if(s==n-1)break;
    }
}
int c[maxn];
int query(int x,int y)
{
    for(int i=1;i<=n;i++)c[i]=-1;
    int tmp=0,ans=0;
    while(1)
    {
        c[x]=tmp;//c是從x開始向上邊的最大值。
        if(fa[x]==x)break;
        tmp=max(tmp,e[x]);//e代表向上連接的邊權 
        x=fa[x];//向上爬 
    }
    while(1)
    {
        if(c[y]>=0){ans=max(ans,c[y]);break;}//找到LCA就break 
        if(fa[y]==y)break;//或者找到根 
        ans=max(ans,e[y]);
        y=fa[y];
    }//因為一定會集中到LCA就直接用ans 
    return ans;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        for(int i=1;i<=m;i++)
        {
            a[i]=(data){read(),read(),read()};
        }
        sort(a+1,a+1+m,cmp);
        kruskal();
        t=read();
        for(int x,y,i=1;i<=t;i++){
            x=read(),y=read();
            printf("%d\n",query(x,y));
        }
    }
    return 0;
}

 但是按秩合並並不僅僅能夠處理最小生成樹問題,也可以處理一些大規模數據的動態加邊和查詢問題,每次連邊后可以記錄兩個秩的大小,非常的方便。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM