并查集的定义及实现

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。

代码实现:

 /*
并查集方法:Union(i,j) 把i,j所在集合合并(没考虑路径,把后面来的结点全设置为第一个root的子节点,find效率高)
           Union_R(i,j)  注重路径的存储方法,哪两个结点并操作就怎么连。需要根据层次count小树连到大树上
           find(x)    返回x所在集合的编号
           getsetcount() 返回集合个数
           getnnodecount() 返回结点个数
           Print()  在此函数中我用二链链表的形式存储了并遍历了集合,那样写复杂了感觉....有点感觉违背初衷。。。应该有更简单的方法

注释:用双亲表示法
*/
#include<iostream>
#define MAX 100

class Union_find{

private:
struct Node
{
    int data;    //数据
    int parent;  //作指针用,同一集合中,parent所指向的最终值一定一样
    int rank;
}node[MAX];

unsigned setcount;   //初始化所添加的元素开始都为一个集合
unsigned nodecount;  //此并查集不删减,元素使用从低到高,因此用整型变量即可表示当前使用了多少结点,剩余多少结点

public:

Union_find(){
for(int i = 0;i<MAX;i++)
{
  node[i].parent=i;//初始化集合的parent为自己的标号。
  node[i].rank = 0;
}
setcount = 0;
nodecount = 0;
}

void addnode(int data)
{
    if(nodecount>MAX){std::cout<<"已满"<<std::endl;return;}
    node[nodecount++].data = data;
    setcount++;
}

void refix(int path,int data)
{
  if(path>=nodecount){std::cout<<"溢出"<<std::endl;return;}
   node[path].data = data;
}

void Union(int p,int q)
{
    if(nodecount==0) return;

    int pRoot = find(p);
    int qRoot = find(q);
    if (pRoot == qRoot)
        return;  //属于同一集合
    if(node[pRoot].rank>node[qRoot].rank) // 将小树(即一个集合)连到大树根上
    {
        node[qRoot].parent = pRoot;
        setcount--;
    }
     else
    {
      node[pRoot].parent = qRoot;
      if(node[pRoot].rank==node[qRoot].rank)
        node[qRoot].rank++;
        setcount--;
    }
}

int find(int x) //查找根节点
{
    if(nodecount==0) return -1;

    int r=x;
    while (node[r].parent!= r)  //返回x的根节点 r
          r=node[r].parent;

    int i=x,temp;
    while(i!= r) //路径压缩
    {
         temp=node[r].parent;
         node[r].parent= r ;   
         i=temp;
    }

    return r ;
}

void Print()
{
  if(nodecount==0) return;

typedef struct Root_P{
  int tab;               //存下标
  struct Root_P *next;   //下一个根
  struct Root_P *sub;    //和根同一个集合的元素
  Root_P():next(NULL),sub(NULL){}
  Root_P(int t):tab(t),next(NULL),sub(NULL){}
}Root_P; //用结构体存储集合


//std::cout<<"共有集合"<<setcount<<"个,元素"<<nodecount<<"个."<<std::endl;
Root_P *T = NULL;

for(int i= 0;i<nodecount;i++) //遍历所有元素
{
       int r = find(i);   //获取当前结点的root值

    //   std::cout<<"当前root值:"<<r<<"当前元素值:"<<node[i].data<<"当前下标值:"<<i<<std::endl;

       Root_P *j;
       for(j=T;j;j=j->next)  //遍历root,看是否存在同样的root值
       {
         if(find(j->tab) == r)     //找到了同样的root值
         {
      //       std::cout<<" 找到同样root的Root_P:"<<j->tab<<std::endl;
             Root_P *temp = new Root_P(i);  //存储当前结点
              Root_P *k;
              for(k=j;k->sub;k=k->sub); // 找到当前root值的最后一个子节点位置
               k->sub=temp;
               break;
         }
       }

       if(j!=NULL) continue;  //代表结点已经找到同样的root了
       else
       {  //新root
     //   std::cout<<"  创建新root:"<<r<<std::endl;
        Root_P *temp = new Root_P(i);
        if(!T)  //头节点
         {
          T = temp;
         }
         else
         {
            Root_P *R = T;
            while(R->next)
                R=R->next;
            R->next = temp;
        }
       }
}

Root_P *T1 = T;
while(T1)
{
 std::cout<<"{";
 for(Root_P *i=T1;i;i=i->sub)
    std::cout<<i->tab<<",";
  std::cout<<"\b},";
for(Root_P *i=T1->sub;i;)  //析构T的sub子链表
{
     Root_P *temp=i;
     i=i->sub;
     delete temp;
}
  T1=T1->next;
}

T1 = T;
while(T1)  //析构T的next正链表
{
     Root_P *temp=T1;
     T1=T1->next;
     delete temp;
}


}

unsigned getnnodecount(){return nodecount;}
unsigned getsetcount(){return setcount;}
};

int main(void)
{
    /* 因为并查集实现方式和使用方式多种多样,在这里我给出了自己使用的一个小例子 */
    Union_find A;
    for(int i = 10;i>0;i--)
    A.addnode(i);

    A.Union(1,2);  // {10,9,8,7,6,5,4,3,2,1} ,{9,8,5}{2,7,3},{1},{4},{6},{10}
    A.Union(2,3);
    A.Union(1,3);
    A.Union(3,7);

    A.Print();

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值