并差集的链表、路径压缩两种方法

来源:互联网 发布:淘宝认证复核 包过 编辑:程序博客网 时间:2024/05/18 01:51

Discription

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。 规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。

Input

第一行:三个整数n,m,p,(n< =5000,m< =5000,p< =5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。 以下m行:每行两个数Mi,Mj,1< =Mi,Mj< =N,表示Mi和Mj具有亲戚关系。 接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。

Output

P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。

算法

算法需要以下几个子过程:
(1) 开始时,为每个人建立一个集合SUB-Make-Set(x);
(2) 得到一个关系后a,b,合并相应集合SUB-Union(a,b);
(3) 此外我们还需要判断两个人是否在同一个集合中,这就涉及到如何标识集合的问题。我们可以在每个集合中选一个代表标识集合,因此我们需要一个子过程给出每个集合的代表元SUB-Find-Set(a)。于是判断两个人是否在同一个集合中,即两个人是否为亲戚,等价于判断SUB-Find-Set(a)=SUB-Find-Set(b)。
有了以上子过程的支持,我们就有如下算法。

PROBLEM-Relations(N, M, a1,…,aM, b1,…,bM, Q, c1,…,cQ, d1,…,dQ)
1 for i←1 to N
2 do SUB-Make-Set(i)
3 for i←1 to M
4 do if SUB-Find-Set(ai) != SUB-Find-Set(bi)
5 then SUB-Union(ai, bi)
6 for i←1 to Q
7 do if SUB-Find-Set(ci)=SUB-Find-Set(di)
8 then output “Yes?”
9 else output “No?”

  • 单链表实现
    一个节点对应一个人,在同一个集合中的节点串成一条链表就得到了单链表的实现。在集合中我们以单链表的第一个节点作为集合的代表元。于是每个节点x(x也是人的编号)应包含这些信息:指向代表元即表首的指针head[x],指向表尾的指针tail[x],下一个节点的指针next[x]。
    SUB-Make-Set(x)过程设计如下:
    SUB-Make-Set(x)
    10 head[x]←x
    11 tail[x]←x
    12 next[x]←NIL
    求代表元的SUB-Find-Set(x)过程设计如下:
    SUB-Find-Set(x)
    13 return head[x]
    前两个过程比较简单,SUB-Union(a,b)稍微复杂一点。我们要做的是将b所在链表加到a所在链表尾,然后b所在链表中的所有节点的代表元指针改指a所在链表的表首节点,如图所示。
    图0-0-2
    过程的伪代码如下:
    SUB-Union(a,b)
    14 next[tail[head[a]]]←head[b]
    15 tail[head[a]]←tail[head[b]]
    16 p←head[b]
    17 while p != NIL
    18 do head[p]←head[a]
    19 p←next[p]

  • 优化——路径压缩
    思想
    每次查找的时候,如果路径较长,则修改信息,以便下次查找的时候速度更快。
    实现
    第一步,找到根结点。
    第二步,修改查找路径上的所有节点,将它们都指向根结点。

    #include <iostream>using namespace std;int father[50002],a,b,m,n,p;int find(int x){   if(father[x] != x)      father[x] = find(father[x]);      /*x代表例题中的人,都与一个人有亲戚关系,相当于例题中第一个集合所有的元素都与第一个元素有亲戚关系,搜索时只要找元素所指向的father[x]=x的元素(即父元素),然后比较两个元素的父元素是否相同就可以判断其关系*/   return father[x];   //也可以修改成 return father[x] == x ? x : father[x] = find(father[x]);}int main(){   int i;   scanf("%d%d%d",&n,&m,&b);   for(int i = 1;i <= n;i++)       father[i] = i; for(i=1;i<=m;i++){  scanf("%d%d",&a,&b);  a=find(a);  b=find(b);  father[a]=b;}for(i = 1;i <= p;i++){        scanf("%d%d",&a,&b);        a = find(a);        b = find(b);        if(a == b)           printf("Yes\n");        else           printf("No\n");}    return 0;}
0 0
原创粉丝点击