这个概念参考中国大学MOOC浙大的数据结构,时间复杂度比它给的源码大,是因为代码中没有使用索引值作为数据值。
#include<bits/stdc++.h>
using namespace std;
/*
并查集:
并查集就是能进行合并和查找根运算的集合。
集合就是数学上所指的集合。
*/
/*
分析:
集合可以直接使用数组表示,即把数据存入数组中就行。但是这样会有一个问题,就是时间复杂度是线性的。
了解BST就知道,BST可以做到log2n级的时间复杂度,线性时间复杂度太大。所以我们把数组元素按树构建,
也就是说在保存元素的同时保存它的父节点(注意:不是完全二叉树那样使用索引,而是同时保存它的父节点)。
初始化:
父节点全部初始化为 -1。
查找根:
查找某结点的父元素就是递归查找它的根结点的父元素,直至无父节点。
看起来这已经是最简单的方法了。但是注意一下,在递归路径上的点都是同一个根,所以在递归退栈的时候
可以把路径上结点的父结点都设置为本次返回的值,这样之后查询此路径上的点的根时递归操作只需要做一次就可以。
合并:
直接把一个结点的父节点设置为另一个结点的索引值就可以。但是需要注意的是每次应该把元素个数少的
挂在元素个数多的下面,这样是为了防止这棵树变成线性的。
*/
/*
结点结构:
数据;
父结点索引; //根的父结点为设置为此树规模的负数
*/
class Node
{
public:
int val;
int parent;
Node(){ parent = -1; }
};
//二分法获得数据的索引,时间复杂度:logN
int getIndex(Node *data, int d)
{
int i = 0, j = 9;
while(i <= j)
{
int middle = (i + j) / 2;
if(data[middle].val < d)
i = middle + 1;
else if(data[middle].val > d)
j = middle - 1;
else
return middle;
}
return -1;
}
//查找并返回根结点的值索引
int findRoot(Node *data, int x)
{
int index = getIndex(data, x);
if(data[index].parent < 0)
return data[index].parent;
return data[index].parent = findRoot(data, data[index].val);
}
//合并集合
void unionSet(Node *data, int n1, int n2)
{
int root1 = findRoot(data, n1), root2 = findRoot(data, n2);
int index1 = getIndex(data, n1), index2 = getIndex(data, n2);
if(root1 < root2) //root1树的规模大于root2树
{
data[index1].parent += data[index2].parent;
data[index2].parent = index1;
}else{
data[index2].parent += data[index1].parent;
data[index1].parent = index2;
}
}
bool cmp(Node e1, Node e2){ return e1.val < e2.val; }
int main()
{
srand(unsigned(time(NULL)));
Node data[10];
for(int i = 0; i < 10; i++)
data[i].val = rand() % 100;
sort(data, data + 10, cmp); //NlogN 级复杂度
unionSet(data, data[1].val, data[2].val);
for(int i = 0; i < 10; i++)
cout << data[i].parent << " " << data[i].val << endl;
return 0;
}