小凡慢慢学
慢慢学,更有趣

1.问题示例

201206181513342042 (1)

如果新输入的对,可以由以前的输入对连通,就不会输出;如果不能由以前的对连通,就输出这个对。例如2-9不在输出之列,因为前面的对存在2-3-4-9的连通。

2.算法实现

抽象操作:

·查找已有的数据项集合

·将结果合并到数据项集合中

2.1 快速查找算法

#include <stdio.h>
#define N 10000
void main()
{
    int i, p, q, t, id[N];
    for(i = 0; i < N; i++)
        id[i] = i;
    while (scanf("%d-%d\n", &p, &q) == 2)
    {
        if(id[p] == id[q])
            continue;
        //遍历数组,把所有值等于id[q]的元素的值改为id[p]。
        for(t = id[p], i = 0; i < N; i++)
            if(id[i] == t)
                id[i] = id[q];
        printf(" %d-%d\n", p, q);
    }
}

 

未命名

          图2-1 快速查找示例(慢速合并)

当id[p] = id[q]时,表示p和q连通。

 

 

2.2  快速合并算法

#include <stdio.h>
#define N 10000
main()
{
    int i, j, p, q, id[N];
    for(i = 0; i < N; i++)
        id[i] = i;
    while (scanf("%d-%d\n", &p, &q) == 2)
    {
        for(i = p; i != id[i]; i = id[i]) ;   //查找p所在树1的根节点
        for(j = q; j != id[j]; j = id[j]) ;   //查找q所在树2的根节点 
        if(i == j)
            continue;
        id[i] = j;   //树2连入树1
        printf(" %d-%d\n", p, q);
    }
}

 

 

201206181514002274201206181514106429

 

 

 

 

      图2-2 快速合并算法示例(不是太快的查找)

2.3 加权快速合并算法

利用数组sz[N]记录了树的节点数,保证每次合并时将较小的树根连到较大的树根上面,避免产生太深的树,减少查找遍历次数。

#include <stdio.h>
#define N 10000
void main()
{
    int i, j, p, q, id[N], sz[N];
    for(i = 0; i < N; i++)
    {
        id[i] = i;
        sz[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(sz[i] < sz[j])
        {
            id[i] = j;
            sz[j] += sz[i];
        }
        else
        {
            id[j] = i;
            sz[i] += sz[j];
        }
        printf(" %d %d\n", p, q);
    }
}

201206181514153892

 图2-3 加权快速合并算法的树型表示

这颗树中每个节点到根节点的距离变小,因而查找更高效。

2.3 等分路径压缩

代替2.2中的for循环,通过使每条链接跳跃到树中向上的路径的下一个节点实现压缩。

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]];

 

 

 

201206181514256410

  图2-3  等分路径压缩

当处理1和6的时候,让1、5、6都指向3,得到的树比上面的算法更扁平。

2.4 完全路径压缩

在合并操作的过程中,添加经过每条路径的另一个指针,使沿路遇见的每个顶点对应的id元素指向树的根节点。

 

 

#include <stdio.h>
#include <stdlib.h>
#define BUFFER_MAX_SIZE 16
int main(int argc, char *argv[]) 
{
    int con[BUFFER_MAX_SIZE];
    int con_cnt[BUFFER_MAX_SIZE];
    int elem[BUFFER_MAX_SIZE][BUFFER_MAX_SIZE];
    int elem_cnt[BUFFER_MAX_SIZE];
    int p, q, r, i, j, k;
    for(r=0; r<BUFFER_MAX_SIZE; r++) 
    {
        con[r]=r;
        con_cnt[r]=1;
        elem[r][0]=r;
        elem_cnt[r]=1;
    }
    
    while(scanf("%d-%d", &p, &q)==2) 
    {
        if(p>=BUFFER_MAX_SIZE||q>=BUFFER_MAX_SIZE)
            exit(EXIT_FAILURE);
        for(i=p; i!=con[i]; i=con[i]);
        for(j=q; j!=con[j]; j=con[j]);
        if(i==j)
            continue;
        if(con_cnt[i]<con_cnt[j]) 
        {
            k=elem_cnt[j];
            for(r=0; r<elem_cnt[i]; r++)
                elem[j][elem_cnt[j]++]=elem[i][r];
            for(r=k; r<elem_cnt[j]; r++)
                con[elem[j][r]]=j;
            con_cnt[j]+=con_cnt[i];
        }
        else 
        {
            k=elem_cnt[i];
            for(r=0; r<elem_cnt[j]; r++)
                elem[i][elem_cnt[i]++]=elem[j][r];
            for(r=k; r<elem_cnt[i]; r++)
                con[elem[i][r]]=i;
            con_cnt[i]+=con_cnt[j];
        }
        printf("%d-%d\n", p, q);
        for(r=0; r<BUFFER_MAX_SIZE; r++)
            printf("%d\t", con[r]);
        printf("\n");
    }
    exit(EXIT_SUCCESS);
}
posted on 2012-09-01 20:48  小凡慢慢学  阅读(447)  评论(0编辑  收藏  举报