《算法:C语言实现》_第一部分_用快速合并算法实现解决连通性问题

上一篇文章讲到,快速查找算法只适合开销不大的连通性问题上,现在对此进行改进。
每次合并遍历全部数组效率太低,能不能不要每次遍历数组全部值,优化为每次只遍历数组的部分值,复杂度因此就会降低。

快速合并的补算法
它与快速查找算法都基于同一个数据结构——通过对象名引用数组元素,但数组元素表达的含义不同,具有更复杂的抽象结构。
这里运用到了树结构,在连通性的关系中,

p->k & q->k = p->q

可将k是为根节点,p和q是为子节点,p和q有共同的根结点k,故p和q连通。
数据结构
1.有一个节点数组id[N],其中的id[i]就表示存放i的父节点。
2.i的根结点是id[id[…id[]i]…]],不断向上找父节点的父节点……直到找到根结点。(这里根结点指向的是自身)
3.在一个没有环的结构中,每个对象指向同一集合中的另一个对象。要确定两个对象是否在同一个集合当中,只需要跟随每个对象的指针,直到达到指向自身的一个对象(只有根结点才会指向自身,所以这里说的是直到找到根结点)。
4.当且仅当这个过程使两个对象到达同一个对象时,这两个对象在同一个集合中。
5.如果两者不在同一个集合中,最终一定到达不同对象(这两个不同的对象是两个不同的根结点,分别都指向的是自身)。
6.当我们在进行合并操作时,只需将一个对象链接到另一个对象。因此命名为快速合并。

快速合并算法的树形表示
《算法:C语言实现》_第一部分_用快速合并算法实现解决连通性问题_第1张图片图中描述的结构就是树,是基本的组合结构。

对于合并和查找操作,图中的树是有用的。它们可以快速建立。
性质
1.当且仅当两个对象在输入中是连通时,这两个对象在树中连通。
2.沿着树向上可以很快找到包含每个对象的树的根。
3.每棵树只有一个对象指向自己——根结点。
快速合并算法示例(不是太快的查找)
《算法:C语言实现》_第一部分_用快速合并算法实现解决连通性问题_第2张图片快速查找和快速合并的区别
1.快速查找在查找树根时,只需要一个链接就可以达到树根,快速合并则可能需要经过几个链接才能达到树根。
2.快速查找对N个对象执行M次合并操作,程序至少需要MN条指令。快速合并则不需要遍历整个数组,即少于MN条指令。
代码实现
替换快速查找算法中while循环体里的内容。

for(i = p; i != id[i]; i = id[i]);//向上找到p的树根
for(i = q;i != id[q];i = id[q]);//向上找到q的树根
if(i == j) continue;//如果p和q的树根相同则进行下一次while循环
id[i] = j;//不同则将id[q]赋值给id[i],表示连通,实现合并操作。
printf("%d %d",p,q);

快速合并算法相对于快速查找算法来说,是一种改进,但有其自身的局限性。
我们不能保证每种情况下,它都会比快速查找算法快。

快速合并次数
对于M>N,快速合并算法求解N个对象,M个对的连通问题,需要执行MN/2条指令。
一种极端情况
假设输入的连通对,是1-2,2-3,3-4,……的次序。N-1个这样的连通对输入之后,可得到N个对象在同一个集合之中。且用快速合并算法生成的树是一条直线。第N个对象指向第N-1个对象,第N-1个对象指向第N-2个对象,以此类推。
如果此时通过第N个对象来查找树根,则需要遍历前N-1个对象。
因此前N个连通对遍历的对象数量就是
(0+1+2+……+(N-1))/N = (N-1)/2。

你可能感兴趣的:(算法:C语言实现,算法,数据结构)