连通性问题
问题提出:
假定给定整数对的一个序列,其中每个整数表示某种类型的一个对象,我们想要说明对p-q表示“p连接到q”。我们假设前提条件是这种连通性是可传递的:同理就是如果p和q之间连通,q和r之间连通,那么我们可以认为p和r是连通的。
我们的目标是通过写一个程序过滤集合中的无关对的程序。程序的输入为对p-q,如果已经看到那点的数对并不隐含着p连通到q,那么输出该对。如果前面的对确实隐含这p连通到q,那么程序应该忽略p-q,并应该继续输入下一对。
}
#define N 10
#include<stdio.h>
int main() {
int i, p, q,id[N],j;
for (i = 0; i < N; i++)
i d[i] = i;
while (scanf("%d %d\n", &p, &q) == 2) {
for (i = p; i != id[i];i=id[i]);
for (j = q; j != id[j];j=id[j]);
if (i == j)
continue;
id[i] = j;
printf("%d %d\n", p, q);
}
return 0;
}
快速合并算法与快速查找算法之间的差异确实代表这一种改进,但是快速合并算法仍然具有局限性,我们不能保证每种情况下,它都会比快速查找算法要快,因为输入数据可能使查找操作变慢。
性质:对于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。
现如今假设其余对都把N连接到某个对象。每对进行查找操作至少访问N-1个指针。因而在这个输入对序列上执行M个查找操作访问的指针总数必定大于MN/2。
不过对于以上的那种糟糕情况,只需要进行简单的修改就就不会出现那种情况。在合并操作中,不是任意地把第二颗树连接到第一颗树上,而是记录每棵树的节点数,总是把较小的树连接到连接到较大的树上。下面给出改良算法:
#include<stdio.h>
#define N 100
int main() {
int i, j, p, q, id[N], a[N];
for (i = 0; i < N; i++) {
id[i] = i;
a[i] = 1;
}
while (scanf("%d %d\n", &p, &q) == 2) {
for (i = p; i != id[i]; i = id[i]);
for (j = q; j != id[j]; j = id[j]);
if (i == j)continue;
//只关注本节点上挂载的节点数与上一节点不需要考虑
//这一段最好画个图动态思考一下
if (a[i] < a[j]) {
id[i] = j;
a[j] += a[i];
}
else {
id[j] = i;
a[i] += a[j];
}
printf("%d %d\n", p, q);
}
return 0;
}
得到以下结论:对于N个对象,加权快速合并算法在判断两个对象是否是连通的,最多需要遍历2lgN个指针对象。
对于上述问题还有改良方法,敬请期待!