暢通工程(並查集)

zxc123e發表於2015-10-10

【題目】某省調查城鎮交通狀況,得到現有城鎮道路統計表,表中列出了每條道路直接連通的城鎮。省政府“暢通工程”的目標是使全省任何兩個城鎮間都可以實現交通(但不一定有直接的道路相連,只要互相間接通過道路可達即可)。問最少還需要建設多少條道路?
【解答】並查集基本演算法。定義陣列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

相關文章