【算法與數據結構】並查集 Disjoint Set

並查集(Disjoint Set)用來判斷已有的數據是否構成環。web

在構造圖的最小生成樹(Minimum Spanning Tree)時,若是採用 Kruskal 算法,每次添加最短路徑前,須要先用並查集來判斷一下這個路徑是否會構成環。算法

思路

遍歷圖的每一條邊,按照下面的原則將對應的兩個頂點添加到集合中:數組

  • 若是兩個頂點都不屬於任一集合,則建立新的集合,並將這兩個頂點放入
  • 若是兩個頂點都已經屬於某個集合,則已經構成環,退出
  • 若是有一個頂點已經屬於某個集合,則將另外一個頂點也加入這個集合

爲了代碼上的統一性,能夠在開始前,把全部頂點都當作只有一個元素的集合,而後就是不停的合併集合。svg

集合能夠用樹的雙親表示法來表示,只須要額外建立一個數組便可。爲了簡化合並操做,能夠每次都只操做兩顆樹的根結點。spa

int parent[n];

// 查找樹的根結點
int findRoot(int parent[], int key) {
    int root = key;
    while (parent[root] != -1) {
        root = parent[root];
    }
    return root;
}
// 合併樹
int unionVertex(int parent[], int x, int y) {
    int lRoot = findRoot(parent, x);
    int rRoot = findRoot(parent, y);
    // 兩個結點的根結點爲同一個,則這兩個結點屬於同一棵樹
    if (lRoot == rRoot) {
        return 0;
    }
    // 不然,合併樹,這裏直接把左樹做爲右樹的子樹,可能會致使不平衡
    parent[lRoot] = rRoot;
}

代碼

爲了在每次合併時,儘量保證樹的平衡,再建立一個數組保存樹的高度,合併時將高度低的樹做爲子樹便可。code

#include <stdio.h>

void init(int parent[], int height[], int count) {
    int i;
    for (i = 0; i < count; i++)  {
        parent[i] = -1;
        height[i] = 0;
    }
}

int findRoot(int parent[], int key) {
    int root = key;
    while (parent[root] != -1) {
        root = parent[root];
    }
    return root;
}

int unionVertex(int parent[], int height[], int x, int y) {
    int lRoot = findRoot(parent, x);
    int rRoot = findRoot(parent, y);
    if (lRoot == rRoot) {
        return 0;
    }
    // parent[lRoot] = rRoot;
    if (height[lRoot] < height[rRoot]) {
        parent[lRoot] = rRoot;
    } else if (height[rRoot] < height[lRoot]) {
        parent[rRoot] = lRoot;
    } else {
        parent[lRoot] = rRoot;
        height[rRoot]++;
    }
    return 1;
}

int main(void) {
    int edgeCount = 6, vertexCount = 5;
    int i;
    // 圖中的邊
    int graph[5][2] = {
        {0, 1}, {2, 4}, {1, 2}, {1, 3}, 
        {2, 5}
    };
    int parent[edgeCount];
    int height[edgeCount];
    
    init(parent, height, edgeCount);

    for (i = 0; i < vertexCount; i++) {
        int ret = unionVertex(parent, height, graph[i][0], graph[i][1]);
        if (ret == 0) {
            printf("%d, %d\n", graph[i][0], graph[i][1]);
            printf("find cycle!\n");
            return 0;
        }
    }
    
    printf("no find cycle!\n");
    for (i = 0; i < vertexCount; i++)  {
        printf("%d's parent is: %d\n", i, parent[i]);
    }
    for (i = 0; i < vertexCount; i++)  {
        printf("%d's height is: %d\n", i, height[i]);
    }
    
    return 0;
}

執行結果:xml

no find cycle!
0's parent is: 1
1's parent is: 4
2's parent is: 4
3's parent is: 4
4's parent is: -1
0's height is: 0
1's height is: 1
2's height is: 0
3's height is: 0
4's height is: 2
相關文章
相關標籤/搜索