GRAPH[0]
Graph Theory
Ian Wen
INDEX
基本定義 & 名詞介紹
存圖
圖的遍歷 (Traversal)
基本定義 & 名詞介紹
1. 圖 (Graph)
2. 點 (Vertex) & 邊 (Edge)
3. 路徑 (Path)
4. 圖的分類
基本定義 & 名詞介紹
圖 (Graph)
你聽過的 "圖" ?
- 教育部國語辭典重編本
基本定義 & 名詞介紹
圖 (Graph)
Definition
在離散數學中,圖(英語:graph)是用於表示物體與物體之間存在某種關係的結構。
- 維基百科

基本定義 & 名詞介紹
圖 (Graph)
一張圖是由點 (Vertex) 與邊 (Edge) 構成的集合
一張圖
Definition
基本定義 & 名詞介紹
邊 (Edge)
以 (u, v) 表示,代表有一條邊連接 點u 與 點v
邊可以有/無方向,有方向時用箭頭表示
邊權:邊上帶的權重
10
7
5
重邊:多條邊 (u, v) 相同
自環:u = v
基本定義 & 名詞介紹
度數
- 無向圖:點上接有的邊數
- 有向圖
- 入度:指向該點的邊數
- 出度:離開該點的邊數
點權
- 點上帶有的權重
點 (Vertex)
1(20)
2(15)
3(9)
4(7)
度數為 1,權重 9
基本定義 & 名詞介紹
路徑 (Path)
- 點與點之間由邊、點交錯構成的集合 {E, V, ..... V, E}
- 簡單路徑:一條不重複經過同一點的路徑
- 迴路
(腦迴路):起點終點相同的路徑 - 環:迴路同時又是簡單路徑
基本定義 & 名詞介紹
一些圖的分類
- 簡單圖 (Simple Graph):沒有重邊、沒有自環的圖
- 有向圖 (Directed Graph):邊有方向
- 強連通:所有點必能走向所有其他的點
- 弱連通:所有點跟點之間有一條邊 (不一定能走)
- 有向無環圖 (DAG):顧名思義 有向無環
- 無向圖 (Undirected Graph):邊沒有方向
- 連通圖:所有點跟點之間必有一條邊
基本定義 & 名詞介紹
一些圖的分類
4. 完全圖:所有點跟點之間都有邊,且為簡單圖
基本定義 & 名詞介紹
一些圖的分類
5. 子圖:所有點 & 邊都存在於原圖中
基本定義 & 名詞介紹
一些圖的分類
6. 補圖:兩圖點相同,聯集兩圖的邊會變成完全圖
基本定義 & 名詞介紹
一些圖的分類
7. 樹:無環無向連通圖


基本定義 & 名詞介紹
一些圖的分類
8. 稀疏圖: ex:樹
9. 稠密圖: ex:完全圖
$$|E| > |V|log|V|$$
$$|E| \leq |V|log|V|$$
基本定義 & 名詞介紹
啊哈,想不到會有題目吧
存圖
1. 鄰接矩陣
2. 鄰接陣列
3. 網格座標
存圖
Adjacency Matrix
鄰接矩陣
0
2
3
1
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 0 | 0 | 1 | 0 |
1 | 0 | 0 | 0 | 1 |
2 | 1 | 0 | 0 | 1 |
3 | 0 | 1 | 1 | 0 |
建一個 大小的表,儲存 (u, v) 有沒有邊/邊權
$$|V|^2$$
無向圖記得 (u, v) (v, u) 要存兩次
存圖
int n, m;
cin >> n >> m;
vector<vector<bool>> graph(n, vector<bool>(n, 0));
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
graph[u][v] = 1;
graph[v][u] = 1;
}
Adjacency Matrix
實作
存圖
Adjacency List
鄰接陣列
0
2
3
1
Graph = {
{2},
{3},
{0, 3},
{1, 2}
}
graph[u] 中存的是 u 點連到的點集
無向圖記得 (u, v) (v, u) 要存兩次
存圖
int n, m;
cin >> n >> m;
vector<vector<int>> graph(n);
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
graph[u].push_back(v);
graph[v].push_back(u);
}
Adjacency List
實作
存圖
鄰接矩陣
鄰接陣列
- 快速查看 u, v 間的邊
- 浪費空間
- 不好處理重邊
- 處理稠密圖較有效率
- 不能直接查看 u, v 間的邊
- 對於相鄰點的操作方便
- 比較不浪費空間
- 處理稀疏圖、稠密圖都很好
存圖
網格座標
就是一個二維的網格,格子是點,與四邊的格子連通
就數學題目上會看到的那種

存圖
網格座標
實作?
開個二維陣列就好了 =-=
圖的遍歷 (Traversal)
1. 深度優先搜尋 (DFS)
2. 廣度優先搜尋 (BFS)
3. 網格座標
4. 例題
圖的遍歷 (Traversal)
還記得枚舉的決策🌲嗎?
🌲?圖論!
把一條路走到底再走其他的路
圖的遍歷 (Traversal)
深度優先搜尋 (DFS)
0
2
3
1
5
4
6
DFS實作
遞迴
vector<vector<int>> graph;
vector<bool> visited;
void dfs(int cur) {
visited[cur] = true;
for (int i = 0; i < graph[cur].size(); i++) {
int nxt = graph[cur][i];
if (!visited[nxt]) dfs(nxt);
}
}
圖的遍歷 (Traversal)
存圖 & 記錄哪些點走過
DFS實作
遞迴
vector<vector<int>> graph;
vector<bool> visited;
void dfs(int cur) {
visited[cur] = true;
for (int i = 0; i < graph[cur].size(); i++) {
int nxt = graph[cur][i];
if (!visited[nxt]) dfs(nxt);
}
}
圖的遍歷 (Traversal)
存圖 & 記錄哪些點走過
記錄走過的點
DFS實作
遞迴
vector<vector<int>> graph;
vector<bool> visited;
void dfs(int cur) {
visited[cur] = true;
for (int i = 0; i < graph[cur].size(); i++) {
int nxt = graph[cur][i];
if (!visited[nxt]) dfs(nxt);
}
}
圖的遍歷 (Traversal)
存圖 & 記錄哪些點走過
記錄走過的點
往其他還沒走過點遞迴下去
圖的遍歷 (Traversal)

- 一種方式走過所有連通的點
- 可以處理相鄰節點的問題
- 可以處理兩點連通的問題
DFS 可以幹嘛?
圖的遍歷 (Traversal)
0
2
3
1
5
4
6
淹水問題
水從2開始淹,會怎麼淹到各個點
圖的遍歷 (Traversal)
從起點開始,越近的點先走
廣度優先搜尋 (BFS)
實作?迴圈就可以寫了
vector<vector<int>> graph;
void bfs(int origin) {
queue<int> nexts;
vector<bool> visited;
while(!nexts.empty()) {
int cur = nexts.front();
nexts.pop();
for (int i = 0; i < graph[cur].size(); i++) {
int nxt = graph[cur][i];
if (!visited[nxt]) {
visited[nxt] = true;
nexts.push(nxt);
}
}
}
}
圖的遍歷 (Traversal)
廣度優先搜尋 (BFS)
- 存圖
- nexts: 同一輪遍歷的點集,visited 紀錄走過的點
- 當本輪點集還沒遍歷完,繼續遍歷
- 遍歷到的點丟出點集
- 把遍歷到的點旁邊的點都丟入下一輪要遍歷的點集,紀錄為已走過
vector<vector<int>> graph;
void bfs(int origin) {
queue<int> nexts;
vector<bool> visited;
while(!nexts.empty()) {
int cur = nexts.front();
nexts.pop();
for (int i = 0; i < graph[cur].size(); i++) {
int nxt = graph[cur][i];
if (!visited[nxt]) {
visited[nxt] = true;
nexts.push(nxt);
}
}
}
}
圖的遍歷 (Traversal)
廣度優先搜尋 (BFS)
小觀察:把 queue 換成 stack 就變成 dfs 了 :D
但沒人會這樣幹,遞迴香
vector<vector<int>> graph;
void bfs(int origin) {
queue<int> nexts;
vector<bool> visited;
while(!nexts.empty()) {
int cur = nexts.front();
nexts.pop();
for (int i = 0; i < graph[cur].size(); i++) {
int nxt = graph[cur][i];
if (!visited[nxt]) {
visited[nxt] = true;
nexts.push(nxt);
}
}
}
}
圖的遍歷 (Traversal)
BFS 可以幹嘛?
- 一種遍歷圖的方法
- 處理相鄰節點的問題
-
不帶權時處理最短路問題
- BFS 直到走到目標點
- 紀錄好路徑,就是最短
圖的遍歷 (Traversal)
但這樣 DFS 很廢的感覺?
- 實作好寫
- code 短
- 還蠻常用的
圖的遍歷 (Traversal)
網格座標?
#include <iostream>
#include <vector>
using namespace std;
void dfs(vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {
// 記得處理好邊界
if (x < 0 || y < 0 || x >= grid.size() || y >= grid[0].size() || grid[x][y] == 1 || visited[x][y]) {
return;
}
// 走到哪個點
visited[x][y] = true;
cout << "Visited: (" << x << ", " << y << ")" << endl;
// 上下左右走
vector<pair<int,int>> dir = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
for (int i=0;i<4;i++) {
int newX = x + dir[i].first;
int newY = y + dir[i].second;
dfs(grid, visited, newX, newY);
}
}
int main() {
// 隨便唬爛一個網格
vector<vector<int>> grid = {
{0, 0, 1},
{0, 1, 0},
{0, 0, 0}
};
int m = grid.size();
int n = grid[0].size();
vector<vector<bool>> visited(m, vector<bool>(n, false));
// 開始 DFS
dfs(grid, visited, 0, 0);
}
DFS 走一個二維陣列:
圖的遍歷 (Traversal)
好欸例題
講完了
去好好刷題
圖論好玩的la
基礎圖論
By wen Ian
基礎圖論
- 264