1. 查找-合并算法
查找:检查两个对象是否属于同一个集合
合并:用一个集合替代两个对象分别对应的集合。
1.1. 快速查找
数据结构:使用一个大小为N的数组id[],p和q是连通的如果他们对应的id值一样。
例如:
节点: i 0 1 2 3 4 5 6 7 8 9
初始: id[i] 0 1 2 3 4 5 6 7 8 9
当前值:id[i] 0 1 9 9 9 6 6 7 8 9
根据当前值,可以看出0是一个集合,1是一个集合,2,3,4,9属于一个集合,5,6属于一个集合,7是一个集合,8是一个集合。
可以看出,5,6是连通的,2,3,4,9是连通的。
问题:节点3,6连通么?
查找操作:因为id[3]=9;id[6]=6,不相同,所以3,6是非连通的。
合并操作:对3,6节点对,.把所有和id[3]相同的id数组里元素值设为id[6].
结果:
i 0 1 2 3 4 5 6 7 8 9
id[i] 0 1 6 6 6 6 6 7 8 6
快速查找的方法:查找可在线性时间内完成,但是合并操作需要O(N)操作
因此,若执行M对节点的连通性的检查,需要O(MN)的操作。
1.2 快速合并
数据结构:使用一个大小为N的数组id[],id[i]表示的是节点i的父节点,节点i的根节点是id[id[id[…id[i]…]]],直到值不变,其实,每一个根节点都是自连接的,即id[i]=i。
例如:
节点: i 0 1 2 3 4 5 6 7 8 9
初始: id[i] 0 1 2 3 4 5 6 7 8 9
当前值:id[i] 0 1 9 4 9 6 6 7 8 9
例如,根据当前值可以看出,3的根节点是9,5的根节点是6
查找操作:就是检查p和q的根节点是否相同,因此,3,5是不连通的。
合并操作:为了合并p和q分别所在的集合,设置节点p的根节点的id值为q节点的根节点。
例如:
i 0 1 2 3 4 5 6 7 8 9
id[i] 0 1 9 4 9 6 6 7 8 6
快速合并的方法:查找可能最坏情况下需要O(N)操作
合并操作,只需要常数操作。
总的来说:执行M对N个节点的查找合并操作,需要O(MN)时间。
1.3 改进操作啊
在快速合并的基础上改进。因为快速合并仅仅是把一个节点的根设置为另一个节点的根。这样构成的树可能就比较高(极端情况是一个线性表的形式),因此,要设法减少树的高度,其实很简单,在合并的时候,把树的数目小的合并到数目大的上去就可以了。因此,需要增加一个数组sz来记录这个节点为根的树的大小。
查找操作:和快速合并一样。
合并操作:把小树合并到大树,更新sz数组相应的元素。
部分代码:
int i=root(p),j=root(q);
if(sz[i]<sz[j]) {id[i]=j;sz[j]+=sz[i]};
else {id[j]=I;sz[i]+=sz[j]};
改进的操作:查找操作O(lgN)时间可以;合并操作O(1);
总的来说:执行M对N个节点的查找合并操作,需要O(MlgN)时间。
还可以继续改进:路径压缩。在快速合并的基础上采用路径合并,即再查找p节点的过程中,把相应的路径上的节点的父节点设置为root(p)。一种简单的替换是仅仅把父节点设置为它的祖父节点就可以。
public int root(int i){
while(i!=id[i]){
id[i]=id[id[i]];
i=id[i];
}
return i;
}
改进2需要的操作:O(MlgN),实际过程中,是线性的,因为lgN是个常数。
1.4 应用
1)网络连通性的检查;
2)Kruskal’s的最小生成树算法。
查找:检查两个对象是否属于同一个集合
合并:用一个集合替代两个对象分别对应的集合。
1.1. 快速查找
数据结构:使用一个大小为N的数组id[],p和q是连通的如果他们对应的id值一样。
例如:
节点: i 0 1 2 3 4 5 6 7 8 9
初始: id[i] 0 1 2 3 4 5 6 7 8 9
当前值:id[i] 0 1 9 9 9 6 6 7 8 9
根据当前值,可以看出0是一个集合,1是一个集合,2,3,4,9属于一个集合,5,6属于一个集合,7是一个集合,8是一个集合。
可以看出,5,6是连通的,2,3,4,9是连通的。
问题:节点3,6连通么?
查找操作:因为id[3]=9;id[6]=6,不相同,所以3,6是非连通的。
合并操作:对3,6节点对,.把所有和id[3]相同的id数组里元素值设为id[6].
结果:
i 0 1 2 3 4 5 6 7 8 9
id[i] 0 1 6 6 6 6 6 7 8 6
快速查找的方法:查找可在线性时间内完成,但是合并操作需要O(N)操作
因此,若执行M对节点的连通性的检查,需要O(MN)的操作。
1.2 快速合并
数据结构:使用一个大小为N的数组id[],id[i]表示的是节点i的父节点,节点i的根节点是id[id[id[…id[i]…]]],直到值不变,其实,每一个根节点都是自连接的,即id[i]=i。
例如:
节点: i 0 1 2 3 4 5 6 7 8 9
初始: id[i] 0 1 2 3 4 5 6 7 8 9
当前值:id[i] 0 1 9 4 9 6 6 7 8 9
例如,根据当前值可以看出,3的根节点是9,5的根节点是6
查找操作:就是检查p和q的根节点是否相同,因此,3,5是不连通的。
合并操作:为了合并p和q分别所在的集合,设置节点p的根节点的id值为q节点的根节点。
例如:
i 0 1 2 3 4 5 6 7 8 9
id[i] 0 1 9 4 9 6 6 7 8 6
快速合并的方法:查找可能最坏情况下需要O(N)操作
合并操作,只需要常数操作。
总的来说:执行M对N个节点的查找合并操作,需要O(MN)时间。
1.3 改进操作啊
在快速合并的基础上改进。因为快速合并仅仅是把一个节点的根设置为另一个节点的根。这样构成的树可能就比较高(极端情况是一个线性表的形式),因此,要设法减少树的高度,其实很简单,在合并的时候,把树的数目小的合并到数目大的上去就可以了。因此,需要增加一个数组sz来记录这个节点为根的树的大小。
查找操作:和快速合并一样。
合并操作:把小树合并到大树,更新sz数组相应的元素。
部分代码:
int i=root(p),j=root(q);
if(sz[i]<sz[j]) {id[i]=j;sz[j]+=sz[i]};
else {id[j]=I;sz[i]+=sz[j]};
改进的操作:查找操作O(lgN)时间可以;合并操作O(1);
总的来说:执行M对N个节点的查找合并操作,需要O(MlgN)时间。
还可以继续改进:路径压缩。在快速合并的基础上采用路径合并,即再查找p节点的过程中,把相应的路径上的节点的父节点设置为root(p)。一种简单的替换是仅仅把父节点设置为它的祖父节点就可以。
public int root(int i){
while(i!=id[i]){
id[i]=id[id[i]];
i=id[i];
}
return i;
}
改进2需要的操作:O(MlgN),实际过程中,是线性的,因为lgN是个常数。
1.4 应用
1)网络连通性的检查;
2)Kruskal’s的最小生成树算法。