图的连通性判断方法主要有:并查集、DFS、BFS、WARSHALL
一、并查集
使用并查集维护所有边,如果 parent 数组中只有一个 根节点 那么,此图是联通图。
若不是一个根节点,那么连通分支数为 根节点个数
代码:
1 int parent[maxn]; 2 int find_root (int n) { 3 return parent[n] == -1 ? n : parent[n] = find_root(parent[n]); 4 } 5 6 7 //连通分支数 = parent 中 -1 个数 8 bool union_solve() { 9 memset(parent, -1, sizeof(parent)); 10 for (int i = 0; i < n; i++) { 11 for (int j = 0; j < n; j++) { 12 if (mapp[i][j]) { 13 int r1 = find_root(i), r2 = find_root(j); 14 if (r1 != r2) 15 parent[j] = r1; 16 } 17 } 18 } 19 20 //如果parent 中只有一个 根节点 那么就连通 21 int cnt = 0; 22 for (int i = 0; i < n; i++) 23 if (parent[i] == -1) 24 cnt++; 25 return cnt == 1 ? true : false; 26 }
二、DFS
如果 vis 数组都是 true 说明是一个连通图. 否则 连通分支数 = 对图中所有点进行 dfs,运行了几次,说明就有几个连通分支数
代码:
1 bool vis[maxn]; 2 void dfs(int x) { 3 vis[x] = true; 4 for (int i = 0; i < n; i++) { 5 if (mapp[x][i] && !vis[i]) 6 dfs(i); 7 } 8 } 9 11 bool dfs_solve() { 12 memset(vis, 0, sizeof(vis)); 13 dfs(0); 14 for (int i = 0; i < n; i++) 15 if (!vis[i]) 16 return false; 17 return true; 18 }
三、BFS
如果 cnt 等于 n 那么就是一个连通图,否则连通分支数 = 对图中所有点进行 dfs,运行了几次,说明就有几个连通分支数
代码:
1 bool bfs_solve() { 2 int cnt = 0; 3 memset(vis, 0, sizeof(vis)); 4 queue<int> q; 5 q.push(0); 6 while (!q.empty()) { 7 int x = q.front(); q.pop(); 8 vis[x] = true; 9 cnt++; 10 for (int i = 0; i < n; i++) 11 if (mapp[x][i] && !vis[i]) { 12 q.push(i); 13 vis[i] = true; //保证cnt==n时访问全部点,防止一个节点被加入队列两次 14 } 15 } 16 return cnt == n; 17 }
四、WARSHALL 算法
这个算法主要是利用求解传递闭包的思想,如果图是连通图那么 这个连通矩阵是一个 全1 矩阵。如果不是连通图,那么在主对角线上,有几个 全1 矩阵那么就是几个连通分支
例如:
1. 连通图的连通矩阵为,例如 4 个点
1 1 1 1
1 1 1 1
1 1 1 1
1 1 1 1
2. 两个连通分支 连通矩阵为: 例如 4 个点
1 1 0 0
1 1 0 0
0 0 1 1
0 0 1 1
代码:
1 bool warshall_solve() { 2 for (int k = 0; k < n; k++) { 3 for (int i = 0; i < n; i++) { 4 for (int j = 0; j < n; j++) 5 mapp[i][j] = mapp[i][j] || (mapp[i][k] && mapp[k][j]); 6 mapp[i][i] = 1; //自己和自己连通 7 } 8 } 9 10 //矩阵中全为 1, 即表示连通图 11 for (int i = 0; i < n; i++) 12 for (int j = 0; j < n; j++) 13 if (!mapp[i][j]) 14 return false; 15 return true; 16 }
五、测试代码:
1 #include <algorithm> 2 #include <cstdio> 3 #include <cstring> 4 #include <string> 5 #include <cmath> 6 #include <iostream> 7 #include <map> 8 #include <queue> 9 #include <set> 10 #include <vector> 11 12 using namespace std; 13 14 15 #define max3(x, y, z) max(max((x), (y)), (z)) 16 #define min3(x, y, z) min(mix((x), (y)), (z)) 17 #define pb push_back 18 #define ppb pop_back 19 #define mk make_pair 20 #define pii pair<int, int> 21 #define pll pair<long long, long long> 22 23 24 #define debug_l(a) cout << #a << " " << (a) << endl 25 #define debug_b(a) cout << #a << " " << (a) << " " 26 #define testin(filename) freopen((filename) ,"r",stdin) 27 #define testout(filename) freopen((filename) ,"w",stdout) 28 29 30 #define close_sync (ios::sync_with_stdio(false)) 31 32 33 34 typedef long long ll; 35 typedef unsigned long long ull; 36 37 38 const double PI = 3.14159265358979323846264338327; 39 const double E = exp(1); 40 const double eps = 1e-6; 41 42 const int INF = 0x3f3f3f3f; 43 const int NINF = 0xc0c0c0c0; 44 // int maxn = 3e3 + 5; 45 // int MOD = 1e9 + 7; 46 47 48 template <typename T> 49 void print_arr(T *arr, int arr_len) 50 { 51 for (int i = 0; i < arr_len; i++) 52 cout << arr[i] << " "; 53 cout << endl; 54 } 55 56 const int maxn = 1e3 + 5; 57 //存图 58 int mapp[maxn][maxn]; 59 int n, m; //定点数,边数 60 61 62 int parent[maxn]; 63 int find_root (int n) { 64 return parent[n] == -1 ? n : parent[n] = find_root(parent[n]); 65 } 66 67 68 //连通分支数 = parent 中 -1 个数 69 bool union_solve() { 70 memset(parent, -1, sizeof(parent)); 71 for (int i = 0; i < n; i++) { 72 for (int j = 0; j < n; j++) { 73 if (mapp[i][j]) { 74 int r1 = find_root(i), r2 = find_root(j); 75 if (r1 != r2) 76 parent[j] = r1; 77 } 78 } 79 } 80 81 //如果parent 中只有一个 根节点 那么就连通 82 int cnt = 0; 83 for (int i = 0; i < n; i++) 84 if (parent[i] == -1) 85 cnt++; 86 return cnt == 1 ? true : false; 87 } 88 89 90 91 92 bool vis[maxn]; 93 void dfs(int x) { 94 vis[x] = true; 95 for (int i = 0; i < n; i++) { 96 if (mapp[x][i] && !vis[i]) 97 dfs(i); 98 } 99 } 100 101 //连通分支数 = 对图中所有点进行 dfs,运行了几次,说明就有几个连通分支数 102 bool dfs_solve() { 103 memset(vis, 0, sizeof(vis)); 104 dfs(0); 105 for (int i = 0; i < n; i++) 106 if (!vis[i]) 107 return false; 108 return true; 109 } 110 111 112 //连通分支数 = 对图中所有点进行 bfs,运行了几次,说明就有几个连通分支数 113 bool bfs_solve() { 114 int cnt = 0; 115 memset(vis, 0, sizeof(vis)); 116 queue<int> q; 117 q.push(0); 118 while (!q.empty()) { 119 int x = q.front(); q.pop(); 120 vis[x] = true; 121 cnt++; 122 for (int i = 0; i < n; i++) 123 if (mapp[x][i] && !vis[i]) { 124 q.push(i); 125 vis[i] = true; //保证cnt==n时访问全部点,防止一个节点被加入队列两次 126 } 127 } 128 return cnt == n; 129 } 130 131 132 //连通分支数为主对角线上 单位阵的个数 133 // 利用传递闭包求解 134 bool warshall_solve() { 135 for (int k = 0; k < n; k++) { 136 for (int i = 0; i < n; i++) { 137 for (int j = 0; j < n; j++) 138 mapp[i][j] = mapp[i][j] || (mapp[i][k] && mapp[k][j]); 139 mapp[i][i] = 1; //自己和自己连通 140 } 141 } 142 143 144 for (int i = 0; i < n; i++) { 145 print_arr(mapp[i], n); 146 cout << endl; 147 } 148 149 //矩阵中全为 1, 即表示连通图 150 for (int i = 0; i < n; i++) 151 for (int j = 0; j < n; j++) 152 if (!mapp[i][j]) 153 return false; 154 return true; 155 } 156 157 158 159 //读入图,假定为无向图 160 void input() { 161 memset(mapp, 0, sizeof(mapp)); 162 cout << "输入顶点数、边数:"; 163 cin >> n >> m; 164 cout << "输入(u,v) 代表有一条边,编号从1开始" << endl; 165 int u, v; 166 for (int i = 0; i < m; i++) { 167 cin >> u >> v; 168 mapp[u - 1][v - 1] = mapp[v - 1][u - 1] = 1; 169 } 170 } 171 172 int main(int argc, char const *argv[]) 173 { 174 175 //testin("../data.in"); 176 input(); 177 debug_l(union_solve()); 178 debug_l(dfs_solve()); 179 debug_l(bfs_solve()); 180 debug_l(warshall_solve()); 181 return 0; 182 }