面向 | 鄰接矩陣 (n×n) | 鄰接表 (n 個向量) |
---|---|---|
空間 | O(n²)(稠密圖好用) | O(n+m)(稀疏圖首選) |
查邊 u→v | O(1) | O(deg(u)) |
列出 u 的鄰居 | O(n) 掃一列 | O(deg(u)) |
遍歷整圖 | O(n²) | O(n+m) |
實務上預設用鄰接表:
int n;
vector<vector<int>> g(n+1);
// 無向邊 (u,v):
g[u].push_back(v); g[v].push_back(u);
// 有向邊 u→v:
g[u].push_back(v);
原題:
https://cses.fi/problemset/task/1666
題意:
給 n 個城市與 m 條道路(無向圖),請輸出最少需要新建幾條道路,並給出一組可行的道路清單,使整張圖變成連通。
解題思路(連通分量 + 代表點接成鏈):
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<int> > g(n+1);
for (int i = 0; i < m; i++) {
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
vector<char> vis(n + 1, 0);
vector<int> reps;
queue<int> q;
for (int s = 1; s <= n; s++) if (!vis[s]) {
reps.push_back(s);
vis[s] = 1;
q.push(s);
while (!q.empty()) {
int u = q.front(); q.pop();
for (int i = 0; i < g[u].size(); ++i) {
int v = g[u][i];
if (!vis[v]) {
vis[v] = 1;
q.push(v);
}
}
}
}
int k = (int)reps.size();
cout << k-1 << "\n";
for (int i = 1; i < k; i++) {
cout << reps[i-1] << " " << reps[i] << "\n";
}
return 0;
}
原題:
https://cses.fi/problemset/task/1678
題意:
給 n 點、m 條有向邊,若存在有向環,輸出一個環上的節點序列(首尾相同封環);否則輸出 IMPOSSIBLE。
解題思路(顏色標記 + 疊代 DFS):
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> g(n+1);
for (int i = 0; i < m; i++) {
int a, b; cin >> a >> b;
g[a].push_back(b);
}
vector<int> color(n+1, 0), parent(n+1, 0), idx(n+1, 0);
for (int s = 1; s <= n; s++) {
if (color[s] != 0) continue;
stack<int> st;
st.push(s);
color[s] = 1; parent[s] = 0; idx[s] = 0;
while (!st.empty()) {
int u = st.top();
if (idx[u] >= (int)g[u].size()) {
color[u] = 2; st.pop();
continue;
}
int v = g[u][ idx[u]++ ];
if (color[v] == 0) {
color[v] = 1; parent[v] = u; idx[v] = 0;
st.push(v);
} else if (color[v] == 1) {
// 回邊 u -> v,v 是祖先
vector<int> cyc;
int x = u;
// 先收集 u -> ... -> v(沿 parent 往上)
while (x != v) {
cyc.push_back(x);
x = parent[x];
}
cyc.push_back(v);
// 反轉成 v -> ... -> u
reverse(cyc.begin(), cyc.end());
// 用回邊 u -> v 關閉環
cyc.push_back(v);
cout << (int)cyc.size() << "\n";
for (int i = 0; i < (int)cyc.size(); ++i) {
if (i) cout << ' ';
cout << cyc[i];
}
cout << "\n";
return 0;
}
// color[v] == 2:已完成,忽略
}
}
cout << "IMPOSSIBLE\n";
return 0;
}
原題:
https://leetcode.com/problems/find-if-path-exists-in-graph/
題意:
給 n、無向邊 edges、起點 source、終點 destination,判斷是否存在一條路徑從 source 到 destination。
解題思路(BFS):
建鄰接表,從 source 做 BFS;能走到 destination 就回 true,否則 false。
class Solution {
public:
bool validPath(int n, vector<vector<int>>& edges, int source, int destination) {
vector<vector<int> > g(n);
for (int i = 0; i < edges.size(); i++) {
int u = edges[i][0], v = edges[i][1];
g[u].push_back(v); g[v].push_back(u);
}
vector<char> vis(n, 0);
queue<int> q;
q.push(source); vis[source] = 1;
while (!q.empty()) {
int u = q.front(); q.pop();
if (u == destination) return true;
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if (!vis[v]) { vis[v] = 1; q.push(v); }
}
}
return false;
}
};
原題:
https://leetcode.com/problems/island-perimeter/
題意:
grid 中 1 是陸地、0 是水。只有 1 塊島(可能有湖),四向相鄰視為連通。求島的周長。
解題思路(線性掃描):
class Solution {
public:
int islandPerimeter(vector<vector<int>>& grid) {
int n = (int)grid.size();
if (n == 0) return 0;
int m = (int)grid[0].size();
int peri = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; ++j) {
if (grid[i][j] != 1) continue;
peri += 4;
if (i+1 < n && grid[i+1][j] == 1) peri -= 2; // 與下方共享
if (j+1 < m && grid[i][j+1] == 1) peri -= 2; // 與右方共享
}
}
return peri;
}
};