快速合并算法仍然有他的局限性,因为查找根节点的次数并不是固定的,如果出现极端情况那么任然会出现计算较多的次数。假定输入对按照1-2, 2-3, 3-4, 4-5......的次序出现,然么N-1个数对形成的树为一条直线。其中N指向N-1,N-1指向N-2以此类推。要查找N最下面的节点,则需要N-1次遍历。那么查找M个对象的查找操作次数必定大于MN/2。
优化:使用另一组数组保存节点的计数,则会使效率获得巨大提升,称之为加权快速合并算法。
#include#define N (10) int main() { int i, p, q, j; int id[N]; int sz[N];//表示前N的对象,所保存的节点 //初始化对象集合中元素的初始值 for (i = 0; i < N; i++) { id[i] = i; sz[i] = 1; } //循环读入整数对 while (scanf("%d-%d", &p, &q) == 2) { //读取p根节点 for (i = p; i != id[i]; i = id[i]) { } for (j = q; j != id[j]; j = id[j]); if (i == j) { //p和q的根节点相同 continue; } if (sz[i] < sz[j]) { id[i] = j; sz[j] += sz[i]; } else { id[j] = i; sz[i] += sz[j]; } //因为p-q是新对,所以输出这个对 printf("New pair: %d-%dn", p, q); } return 0; }
加权合并中的sz[N]保存的连接节点的数量,通过使用这个新的数据结构完成原先的算法的优化。
1.2.4 压缩路径加权合并法 —— 等分路径压缩#include#define N (10) int main() { int i, p, q, j; int id[N]; int sz[N];//表示前N的对象,所保存的节点 //初始化对象集合中元素的初始值 for (i = 0; i < N; i++) { id[i] = i; sz[i] = 1; } //循环读入整数对 while (scanf("%d-%d", &p, &q) == 2) { //读取p根节点 for (i = p; i != id[i]; i = id[i]) { id[i] = id[id[i]]; } for (j = q; j != id[j]; j = id[j]) { id[j] = id[id[j]]; } if (i == j) { //p和q的根节点相同 continue; } if (sz[i] < sz[j]) { id[i] = j; sz[j] += sz[i]; } else { id[j] = i; sz[i] += sz[j]; } //因为p-q是新对,所以输出这个对 printf("New pair: %d-%dn", p, q); } return 0; }
再次优化,使得每棵树都是尽量扁平的。
看似是和快速查找法类似的数据变化过程,但是由于是找根节点的方式判断是否有链接,所以效率变高。
通俗理解:就是利用查找根节点这一相对简单的判断方式,但是通过扁平化的树分支来代替原先的较长的分支。提高执行效率。



