并查集实现等价类

等价关系:集合或类(以集合为例)上的等价关系R指一个具有自反, 对称, 传递性的二元关系。
等价类: 在一个定义了等价关系的集合中可以按该等价关系分成等价类(即两个元素只要有xRy, 则它们属于同一等价类), 即集合的一些子集组成的集,。容易证明这些等价类两两不交且其并等于原集合.
 假设集合S有n个元素,m个形如(x,y) (x,y 属于 S)的等价偶对确定了等价关系R,如何求S的划分,即该如何求S的等价类?? 
 1)利用链表结构实现
  2)利用并查集实现(我么这里将讨论的)

并查集:并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。

并查集方式求等价类的方法如下:

    (1)令集合S中每个元素个子形成一个只包含单个成员的子集,记为S0,S1,...Sn-1.

      (2)  重复读入m个偶对,对每个读入的偶对(x,y),判定x和y所属子集。假设x属于Si,y属于Sj,如果Si != Sj,侧将Si 并于 Sj并将Si置空(或将Sj并于Si,                      并将Sj置空)。则当m个偶对都被处理过后,S0,S1,...,Sn-1中所有非空子集即为S的R等价类.


由此可见并查集方式求子集关键是1)查找一个元素所属的集合.2)求二个子集的合集3)求非空集合

并查集的定义

[cpp]  view plain copy
  1. const int Max_TreeNode=100;//树的节点的最大值  
  2. //利用双亲表示法表示树  
  3.   
  4. template <typename T>   
  5. class UFSET  
  6. {  
  7. public:  
  8.     //树的节点定义  
  9.    typedef struct TreeNode  
  10.    {  
  11.      T data;//树的节点的值  
  12.      int parent;//节点的双亲所在的位置(如果该节点为根节点侧该值为以该节点为根的树的节点个数的负值)  
  13.    } TreeNode, *PTreeNode ;  
  14.       
  15.     TreeNode nodes[Max_TreeNode];  
  16.     int n;//节点个数(集合元素的个数)  
  17.       
  18.     UFSET(T * datas,int size):n(size)//datas为集合中所有元素值的集合,size为集合元素的个数  
  19.     {  
  20.         for(int i=0;i<size;i++)  
  21.         {  
  22.             nodes[i].data=datas[i];  
  23.             //最开始令每个元素各自形成只包含该元素的子集  
  24.             nodes[i].parent=-1;  
  25.         }  
  26.     }  
  27. };  

      查找元素所属的集合
[cpp]  view plain copy
  1. //查找元素e所属的集合(用根节点所在位置表示集合)  
  2. template <typename T>  
  3. int find(UFSET<T> & t,T e)  
  4. {  
  5.    //先找到值为e的节点  
  6.    for(int i=0;i<t.n;++i)  
  7.    {  
  8.     if(t.nodes[i].data==e)  
  9.     {    
  10.        int j;  
  11.        for(j=i;t.nodes[j].parent>=0;j=t.nodes[j].parent);  
  12.        return j;  
  13.     }  
  14.    }  
  15.    return -1;//表示值为e的节点不存在  
  16. }  

   当所查元素i不在树的第二层时,在算法中增加一个“压缩路劲的功能”,将所有从根到元素路径上的元素都变成树根的孩子。

  查找元素所属的集合的改进型

[cpp]  view plain copy
  1. //find函数的一种改进型  
  2. template <typename T>  
  3. int mix_find(UFSET<T> & t,T e)  
  4. {  
  5.    //先找到值为e的节点  
  6.    for(int i=0;i<t.n;++i)  
  7.    {  
  8.     if(t.nodes[i].data==e)  
  9.     {    
  10.        int j;  
  11.        for(j=i;t.nodes[j].parent>=0;j=t.nodes[j].parent);  
  12.        //将所有从节点t.nodes[i]到根t.nodes[j]的路径上的节点都变成根t.nodes[j]的孩子  
  13.        while(i!=j)  
  14.        {  
  15.           int temp=t.nodes[i].parent;  
  16.           t.nodes[i].parent=j;  
  17.           i=temp;  
  18.        }  
  19.        return j;  
  20.     }  
  21.    }  
  22.    return -1;//表示值为e的节点不存在  
  23. }  

合并子集

[cpp]  view plain copy
  1. //合并子集  
  2. //将节点少的子集合并到节点多的子集上  
  3. template <typename T>  
  4. void merger(UFSET<T> & t,int i,int j)  
  5. {  
  6.    // t.nodes[i]和 t.nodes[j] 分别表示集合的两个互不相交的子集Si和Sj的根节点  
  7.    if(i<0 || i>t.n || j<0 || j>t.n)  
  8.        return ;  
  9.    if(t.nodes[i].parent>t.nodes[j].parent)//t.nodes[i]的节点个数少于t.nodes[j]的节点个数  
  10.    {  
  11.       t.nodes[j].parent+=t.nodes[i].parent;  
  12.       t.nodes[i].parent=j;  
  13.    }  
  14.    else  
  15.    {  
  16.       t.nodes[i].parent+=t.nodes[j].parent;  
  17.       t.nodes[j].parent=i;  
  18.    }  
  19. }  
    计算等价类

 
 
  1. //计算等价类(即最后非空子集的个数)  
  2. template <typename T>  
  3. int EquClassCount(UFSET<T> & t)  
  4. {  
  5.     int count=0;  
  6.     for(int i=0;i<t.n;i++)  
  7.         if(t.nodes[i].parent<0)  
  8.             count++;  
  9.     return count;  
  10. }  

下面求解小米公司2013笔试题:

朋友圈:假如已知有n个人和m对好友关系,如果两个人是直接或者间接有好友关系,则认为他们属于同一个朋友圈。写程序判断里面有多少朋友圈。
例如 
n = 5, m = 3  r = {(1,2), (2, 3), (4, 5)}  1 2 3 是一个朋友圈, 4 5 是一个朋友圈。
所以输出是2. 

完整代码余下所示:

[cpp]  view plain copy
  1. #include "stdafx.h"  
  2. #include <iostream>  
  3.    
  4. const int Max_TreeNode=100;//树的节点的最大值  
  5. //利用双亲表示法表示树  
  6.   
  7. template <typename T>   
  8. class UFSET  
  9. {  
  10. public:  
  11.     //树的节点定义  
  12.    typedef struct TreeNode  
  13.    {  
  14.      T data;//树的节点的值  
  15.      int parent;//节点的双亲所在的位置(如果该节点为根节点侧该值为以该节点为根的树的节点个数的负值)  
  16.    } TreeNode, *PTreeNode ;  
  17.       
  18.     TreeNode nodes[Max_TreeNode];  
  19.     int n;//节点个数(集合元素的个数)  
  20.       
  21.     UFSET(T * datas,int size):n(size)//datas为集合中所有元素值的集合,size为集合元素的个数  
  22.     {  
  23.         for(int i=0;i<size;i++)  
  24.         {  
  25.             nodes[i].data=datas[i];  
  26.             //最开始令每个元素各自形成只包含该元素的子集  
  27.             nodes[i].parent=-1;  
  28.         }  
  29.     }  
  30. };  
  31.   
  32. //查找元素e所属的集合(用根节点所在位置表示集合)  
  33. template <typename T>  
  34. int find(UFSET<T> & t,T e)  
  35. {  
  36.    //先找到值为e的节点  
  37.    for(int i=0;i<t.n;++i)  
  38.    {  
  39.     if(t.nodes[i].data==e)  
  40.     {    
  41.        int j;  
  42.        for(j=i;t.nodes[j].parent>=0;j=t.nodes[j].parent);  
  43.        return j;  
  44.     }  
  45.    }  
  46.    return -1;//表示值为e的节点不存在  
  47. }  
  48.   
  49. //find函数的一种改进型  
  50. template <typename T>  
  51. int mix_find(UFSET<T> & t,T e)  
  52. {  
  53.    //先找到值为e的节点  
  54.    for(int i=0;i<t.n;++i)  
  55.    {  
  56.     if(t.nodes[i].data==e)  
  57.     {    
  58.        int j;  
  59.        for(j=i;t.nodes[j].parent>=0;j=t.nodes[j].parent);  
  60.        //将所有从节点t.nodes[i]到根t.nodes[j]的路径上的节点都变成根t.nodes[j]的孩子  
  61.        while(i!=j)  
  62.        {  
  63.           int temp=t.nodes[i].parent;  
  64.           t.nodes[i].parent=j;  
  65.           i=temp;  
  66.        }  
  67.        return j;  
  68.     }  
  69.    }  
  70.    return -1;//表示值为e的节点不存在  
  71. }  
  72.   
  73. //合并子集  
  74. //将节点少的子集合并到节点多的子集上  
  75. template <typename T>  
  76. void merger(UFSET<T> & t,int i,int j)  
  77. {  
  78.    // t.nodes[i]和 t.nodes[j] 分别表示集合的两个互不相交的子集Si和Sj的根节点  
  79.    if(i<0 || i>t.n || j<0 || j>t.n)  
  80.        return ;  
  81.    if(t.nodes[i].parent>t.nodes[j].parent)//t.nodes[i]的节点个数少于t.nodes[j]的节点个数  
  82.    {  
  83.       t.nodes[j].parent+=t.nodes[i].parent;  
  84.       t.nodes[i].parent=j;  
  85.    }  
  86.    else  
  87.    {  
  88.       t.nodes[i].parent+=t.nodes[j].parent;  
  89.       t.nodes[j].parent=i;  
  90.    }  
  91. }  
  92.   
  93. //计算等价类(即最后非空子集的个数)  
  94. template <typename T>  
  95. int EquClassCount(UFSET<T> & t)  
  96. {  
  97.     int count=0;  
  98.     for(int i=0;i<t.n;i++)  
  99.         if(t.nodes[i].parent<0)  
  100.             count++;  
  101.     return count;  
  102. }  
  103.   
  104. //将元素值为e1和e2所在集合合并  
  105. template <typename T>  
  106. void mix_merger(UFSET<T> & t,T e1,T e2)  
  107. {  
  108.     int i=mix_find(t,e1);  
  109.     int j=mix_find(t,e2);  
  110.     if(i>=0 && j>=0 && i!=j)  
  111.         merger(t,i,j);  
  112. }  
  113.   
  114. int _tmain(int argc, _TCHAR* argv[])  
  115. {  
  116.     int friends[]={1,2,3,4,5};  
  117.     UFSET<int> friend_set(friends,sizeof(friends)/sizeof(int));  
  118.     mix_merger(friend_set,1,2);  
  119.     mix_merger(friend_set,2,3);  
  120.     mix_merger(friend_set,4,5);  
  121.     std::cout<<"朋友串个数为:"<<EquClassCount(friend_set)<<std::endl;  
  122.     system("PAUSE");  
  123.     return 0;  
  124. }  

本文主要参考资料:数据结构(C语言本 严蔚敏版) P139——P143
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值