出题:并查集(Union-Find Sets)
分析:
- 一种树型数据结构,用于处理不相交集合(Disjoint
Sets)的合并以及查询;一开始让所有元素独立成树,也就是只有根节点的树;然后根据需要将关联的元素(树)进行合并;合并的方式仅仅是将一棵树最原始的节点的父亲索引指向另一棵树; - 优化:加入一个rank数组存储节点深度的下界(从当前节点到其最远子节点的距离),从而可以启发式的对树进行合并,从而减少树的深度,防止树的退化;使
得包含较少节点的树根指向包含较多节点的树根,具体指代为树的高度;另一个优化就是路径压缩,尽可能将子节点都直接连接到根节点之后; - 并查集的空间复杂度为O(N),构建一个集合的时间复杂度为O(N);压缩后的查找复杂度是一个很小的常数;应用:Kruskal算法求最小生成树中判断新加入的边是否在同一棵树内部;两个节点的最近公共祖先(Least
Common Ancestors); - 初始化father:各个节点独立成树,并且其father[i]=i,也就是其父节点就是其自身;
father[i]
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
初始化rank:各个节点为根节点,所以高度都为1;
rank[i]
0 1 2 3 4 5 6 7 8 9
1 1 1 1 1 1 1 1 1 1
合并2和6:由于rank[2]=rank[6],所以将2的父亲索引指向6,这样2和6就在同一棵树;并需将6的rank值增加1;
father[i]
0 1 2 3 4 5 6 7 8 9
0 1 6 3 4 5 6 7 8 9
rank[i]
0 1 2 3 4 5 6 7 8 9
1 1 1 1 1 1 2 1 1 1
解题:
1 int *father;
2 int *rank;
3
4 /**
5 * 并查集的初始化:
6 * 数组father中的元素在最开始ide时候都是独立的树,也就是只有根节点
7 * 的树,数组father的下标i表示节点,而father[i]的值表示i节点的父亲
8 * 节点;rank[i]=1表示一开始所有树节点的高度都为1
9 * */
10 void init(int cap) {
11 father=new int[cap];
12 rank=new int[cap];
13 /**
14 * 时间复杂度为O(N)
15 * */
16 for(int i=0;i<10;i++) {
17 father[i]=i;
18 rank[i]=1;
19 }
20 }
21
22 void clean() {
23 delete [] father;
24 delete [] rank;
25 }
26 /**
27 * 查找元素所在的集合并进行路劲压缩:
28 * 由于需要频繁使用GetFather()函数,并且其时间复杂度受树结构影响;
29 * 当元素较多的时候,集合退化成链表,则GetFather()需要O(N),所以
30 * 需要对其进行优化,每次调用GetFather()的时候都将输入元素压缩成
31 * 最原始父亲节点的直接子节点
32 * */
33 int GetFather(int son) {
34 if(father[son]==son)
35 return son;
36 else {
37 father[son]=GetFather(father[son]);
38 return father[son];
39 }
40 }
41
42 /**
43 * 合并两个不相交的集合:
44 * 输入元素x和y来自两个不相交的集合,找到其最原始的父亲节点
45 * 并将一个原始父亲节点设置为另一个原始父亲节点的父亲节点
46 * */
47 void Union1(int x, int y) {
48 /**
49 * GetFather()为递归寻找输入节点的最原始的父亲节点
50 * */
51 int fx=GetFather(x);
52 int fy=GetFather(y);
53 /**
54 * 判断x和y是否来自同一棵树,如果不是才进行赋值;其实可以
55 * 不同进行判断(省去if语句)
56 * 注意最原始父亲节点的father[i]=i;
57 * */
58 if(fx!=fy)
59 father[fx]=fy;
60 }
61
62 /**
63 * 利用rank加权数组启发式进行合并
64 * */
65 void Union2(int x, int y) {
66 int fx=GetFather(x);
67 int fy=GetFather(y);
68
69 if(fx==fy) return;
70 /**
71 * rank[fx]较大,说明其越靠近根节点,则将
72 * fy连接到其后面可以压缩路径
73 * */
74 if(rank[fx]>rank[fy])
75 father[fy]=fx;
76 else {
77 if(rank[fx]==rank[fy])
78 rank[fy]++;
79 father[fx]=fy;
80 }
81 }
82
83 /**
84 * 判断两个元素是否属于同一个集合:
85 * 利用GetFather()函数判断其最原始父亲节点是否相同
86 * */
87 bool IsSameSet(int x, int y) {
88 return GetFather(x)==GetFather(y);
89 }
笔试算法题(38):并查集(Union-Find Sets),布布扣,bubuko.com
时间: 2024-10-28 22:42:09