并查集是一种树型的数据结构,用于处理一些不相交集合(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;
}