畅通工程(并查集)

【题目】某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
【解答】并查集基本算法。定义数组parent,每个城市可以用数组下标指示,parent[index]的值代表与index联通的城市。

代码:

    int[] parent = new int[5];

    public int findRoot(int x)
    {
        int r = x;
        //根节点性质root == parent[root]
        while (r != parent[r])
            r = parent[r];
        return r;
    }
    public void Merge(int x, int y)
    {
        int p , q;
        p = findRoot(x);
        q = findRoot(y);
        if (p != q) { //随便指定
                parent[x] = q;
        }
    }

以上代码现实了基本功能,我们可以做一些优化。需要优化的是这个算法中使用频率最高的方法,即对根节点的查找的方法(findRoot)。

1.将高度小的树合并到高度大的树
上面代码中两颗树是随意合并的,为了是查找速率更高,就要使整个树尽量保持平衡。假设两颗树的高度是h1,h2,则合并后的高度h是:

  • if(h1 != h2) h = max(h1, h2)
  • if(h1 == h2) h = h1+1

2.路径压缩
查找根节点(r = parent[r]),修改查找路径上的所有节点,将他们都指向根节点。

优化后的代码:

    int[] parent = new int[5]; //值与下标一致,初始值就是父节点就是自身
    int[] height = new int[5]; //记录树的高度

    public int findRoot(int x)
    {

        int r = x;
        while (r != parent[r])
            r = parent[r];

        int i = x, j;
        //压缩路径
        while (i != r)
        {
            j = parent[i];
            parent[i] = r;
            i = j;
        }

        return r;
    }
    public void Merge(int x, int y)
    {
        int p , q;
        p = findRoot(x);
        q = findRoot(y);
        if (p != q) {
            //使高度小的树合并到高度大的树
            if (height[p] == height[q])
            {
                parent[p] = q;
                height[q]++;
            }else if (height[p] > height[q]){
                parent[q] = p;
            }else {
                parent[p] = q;
            }
        }
    }

关于并查集的详细介绍,请参看这篇文章:http://blog.csdn.net/dm_vincent/article/details/7655764

你可能感兴趣的:(算法,树,并查集)