图连通性判断


使用并查集判断图连通性

并查集,英文名称Disjoint-set data structure

在計算機科學中,併查集(英文:Disjoint-set data structure,直譯為不交集數據結構)是一種數據結構,用於處理一些不交集(Disjoint sets,一系列沒有重複元素的集合)的合併及查詢問題。併查集支持如下操作:

判断依据

图联通,则所有节点具有相同的根节点

题目

使用并查集判断图连通性

输入

本问题有多组测试数据,每组测试数据有两部分,第一部分只有一行,是两个正整数,分别表示图的节点数N(节点编号从1到N,1<=N<=100)和图的边数E;第二部分共有E行,每行也是两个整数N1,N2(1<=N1,N2<=N),分别表示N1和N2之间有一条边。

6 10
1 2
1 3
1 4
1 5
1 6
2 3
2 4
3 4
3 5
3 6
4 3
1 2
1 3
2 3

输出

Yes
No

数据结构

class UnionFind {
private:
    vector<int> parent;
    vector<int> rank;

初始化

让所有节点的根节点都为她自己

UnionFind(int n) {
        parent.resize(n + 1);
        rank.resize(n + 1, 0);
        // 使用 std::iota 初始化 parent 数组
        iota(parent.begin(), parent.end(), 0);
}

并查集find操作

int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]);
        }
        return parent[x];
}

这里用到了路径压缩,将所有根节点下的节点变成兄弟节点,树呈两层

并查集的合并操作

void unite(int x, int y) {
  int rootX = find(x);
  int rootY = find(y);

  // 这里用到了按秩合并,优化查询的时间开销
  if (rootX != rootY) {
      if (rank[rootX] < rank[rootY]) {
          parent[rootX] = rootY;
      }
      else if (rank[rootX] > rank[rootY]) {
          parent[rootY] = rootX;
      }
      else {
          parent[rootY] = rootX;
          rank[rootX]++;
      }
  }
}

判断是否联通

bool isConnected(int n, const vector<pair<int, int>>&edges) {
    UnionFind uf(n);
    for (int i = 1; i <= edges.size() - 1; i++) {
        int a = edges[i].first;
        int b = edges[i].second;
        uf.unite(a, b);
    }
    int root = uf.find(1);
    for (int i = 1; i <= n; i++) {
        if (uf.find(i) != root) {
            return false;
        }
    }
    return true;
}

完整代码(better c++ 14 and than)

#ifndef STDHPP
#define STDHPP

#ifdef compiler_directive
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3, "ofast", "inline")
#endif

#ifndef headfile
#include <algorithm>
#include <bitset>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#include <unordered_set>
#include <regex>
#endif

#ifndef macro_definition
#define _SILENCE_CXX20_CISO646_REMOED_WARNING
#define fspr(n) fixed << setprecision(n)
#define spr(n) setprecision(n)
#define sci setiosflags(ios::scientific)
#define siosf setiosflags
#define fspr(n) fixed << setprecision(n)
#define spr(n) setprecision(n)
#define sci setiosflags(ios::scientific)
#define siosf setiosflags
#define ifor(i, l, r) for (int i = l; i <= r; ++i)
#define rfor(i, r, l) for (int i = r; i >= l; --i)
#define npos string::npos
#define fir first
#define sec second
#define pb push_back
#define pf push_front
#define inlast(a) a.size() - 1
#define itlast(a) prev(a.end(), 1)
#define all(v) v.begin(), v.end()
#endif

#ifndef usenamespace
// using namespace std;
#endif

#ifndef type_alias
typedef long long i64;
typedef long double f80;
typedef unsigned int u32;
typedef unsigned long long u64;
typedef unsigned long long ull;
typedef long double doubleL;
template<class T, class Container = std::vector<T>,
    class Compare = std::less<typename Container::value_type>>
using priqueue = std::priority_queue<T, Container, Compare>;
#endif

#ifndef customized_namespace
namespace cus {
    // standard output
    template<typename container>
    void print(container&a2) {
        for (auto i = a2.begin(); i != a2.end(); i++) {
            std::cout << *i << " \n"[i == prev(a2.end(), 1)];
        }
    }

    template<typename T1>
    auto print(T1 a, i64 l, i64 r) -> void {
        for (long i = l; i <= r; ++i) {
            std::cout << a[i] << " /n"[i == r];
        }
    }

    //__int128 write
    template<typename T>
    void read(T&w) {
        w = 0;
        T f = 1;
        char ch = getchar();
        while (ch < '0' || ch > '9') {
            if (ch == '-')
                f = -1;
            ch = getchar();
        }
        while (ch <= '9' && ch >= '0') {
            w = w * 10 + ch - '0';
            ch = getchar();
        }
    }

    template<typename T>
    void print128(T x) {
        if (x < 0) {
            putchar('-');
        }
        x = -x;
        if (x > 9)
            print128(x / 10);
        putchar(x % 10 + '0');
    }
} // namespace cus
#endif

#ifndef constant
constexpr int I_INF = 0x3f3f3f3f;
constexpr long L_INF = 0x3f3f3f3f3f3f3f3f;
constexpr double EPS = 1.0e-9;
constexpr long MOD = 1e9 + 7;
constexpr int N = 1e2 + 10;
constexpr double d_INF = std::numeric_limits<double>::infinity();
constexpr long long LL_INF = std::numeric_limits<i64>::infinity();
#endif

#ifndef global_variable
#endif

#endif
using namespace std;
class UnionFind {
private:
    vector<int> parent;
    vector<int> rank;

public:
    UnionFind(int n) {
        parent.resize(n + 1);
        rank.resize(n + 1, 0);
        for (int i = 1; i <= n; i++) {
            parent[i] = i;
        }
    }

    int find(int x) {
        // 路径压缩,将根节点下的所有节点都变成兄弟节点两层
        if (parent[x] != x) {
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }


    void unite(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX != rootY) {
            if (rank[rootX] < rank[rootY]) {
                parent[rootX] = rootY;
            }
            else if (rank[rootX] > rank[rootY]) {
                parent[rootY] = rootX;
            }
            else {
                parent[rootY] = rootX;
                rank[rootX]++;
            }
        }
    }
};

bool isConnected(int n, const vector<pair<int, int>>&edges) {
    UnionFind uf(n);
    for (int i = 1; i <= edges.size() - 1; i++) {
        int a = edges[i].first;
        int b = edges[i].second;
        uf.unite(a, b);
    }
    int root = uf.find(1);
    for (int i = 1; i <= n; i++) {
        if (uf.find(i) != root) {
            return false;
        }
    }
    return true;
}

vector<pair<int, int>> edges;

int main() {
    int n, m;
    while (cin >> n >> m) {
        edges.resize(m + 1);
        for (int i = 1; i <= m; i++) {
            cin >> edges[i].first >> edges[i].second;
        }
        bool connected = isConnected(n, edges);
        if (connected) {
            cout << "Yes" << endl;
        }
        else {
            cout << "No" << endl;
        }
        edges.clear();
    }
    return 0;
}

Author: Acaibird
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Acaibird !
  TOC