用DFS和BFS求連通分量(鄰接表的實現與應用)
本文內容改編自《挑戰程序設計競賽(第2彈)》12.5節
問題描述
給出朋友關係,判斷從指定人物出發能否通過雙向朋友鏈抵達目標人物。
輸入
第一行輸入總人數n以及朋友關係數m
人的編號從0到n-1。
接下來m行輸入朋友關係,每個朋友關係佔一行。
1個朋友關係包含s、t兩個整數,表示s和t爲朋友。
接下來一行輸入問題數q。再接下來q行輸入問題
各問題爲兩個整數s、t。表示“從s能否抵達t?”
輸出
如果從s出發能抵達t就輸出yes,否則輸出no,每個問題的回答佔1行。
限制
1 < n < 100000, 0 < m < 100000, 1 < q < 10000
樣例輸入
10 9
0 1
0 2
3 4
5 7
5 6
6 7
6 8
樣例輸出
yes
yes
no
解題思路
這道題就是求給定的圖的連通分量問題,可以用“種子填充”的思想來解決此題。
對圖中每一個結點進行深度優先搜索,並在此過程中對還沒有填色的結點填填色。
如果兩個結點有相同的顏色,那麼這兩個結點就是連通的。
於是O(1)時間就可以判斷指定兩個結點是否連通。
核心算法就是DFS。主要數據結構就是鄰接表,用來存儲圖的信息。
在鄰接表上的DFS需要需要對每個頂點,每個邊都要訪問一次,算法複雜度爲
O(|V|+|E|)
鄰接表的實現
在C++中,用vector可以輕鬆的實現鄰接表。
vector<int> G[100]; //表示100個頂點的鄰接表
G[u].push_back(v); //從頂點u向頂點v畫邊
//搜索與頂點相鄰的頂點v
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
//...
}
鄰接表的優缺點
優點:只需要與邊數成正比的空間
缺點:難以有效地刪除邊
若u的相鄰頂點數量爲n,需要消耗O(n)來搜索鄰接表。
代碼實現
#include <bits/stdc++.h>
using namespace std;
const int MAX_N = 100000;
int n;
vector<int> G[MAX_N];
int color[MAX_N];
//從結點r開始DFS,並填充顏色c
void dfs(int r, int c)
{
color[r] = c;
for (int i = 0; i < G[r].size(); i++) {
int v = G[r][i];
if (color[v] == -1) {
dfs(v, c); //相鄰結點v未填色,則繼續搜索
}
}
}
void AssignColor()
{
int id = 0;
memset(color, -1, sizeof(color)); //初始化color數組,-1表示未填色
for (int u = 0; u < n; u++) {
if (color[u] == -1)
dfs(u, id++); //對未填色的結點開始DFS
}
}
int main()
{
int m, s, t;
while (cin >> n >> m) {
//創建鄰接表
for (int i = 0; i < m; i++) {
cin >> s >> t;
G[s].push_back(t);
G[t].push_back(s);
}
//填色開始
AssignColor();
int q;
cin >> q;
for (int i = 0; i < q; i++) {
cin >> s >> t;
if (color[s] == color[t]) {
cout << "yes" << endl;
} else {
cout << "no" << endl;
}
}
}
return 0;
}
其中void dfs()還有另外一種實現方法,就是顯式地使用棧來實現DFS。
當然這道題也可以用廣度優先搜索(BFS),部分代碼如下
void bfs(int r, int c)
{
queue<int> Q;
Q.push(r);
color[r] = c;
while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (color[v] == -1) {
color[v] = c;
Q.push(v);
}
}
}
}
void AssignColor()
{
int id = 0;
memset(color, -1, sizeof(color)); //初始化color數組,-1表示未填色
for (int u = 0; u < n; u++) {
if (color[u] == -1)
bfs(u, id++); //對未填色的結點開始BFS
}
}