WiwiHo
Graph
❌圖片(picture) ⭕圖(graph)
由一些節點和邊組成的東西
記作 \(G=(V,E)\)
連接兩個節點
(undirected edge)
雙向的邊
連接節點 \(u\)、\(v\) 的無向邊
記作 \((u,v)\)、\((v,u)\)
且 \((u,v)=(v,u)\)
i.e. 記作無序對
(directed edge)
單向的邊
從 \(u\) 到 \(v\) 的有向邊
記作 \((u,v)\)
i.e. 記作有序對
邊或點上可能有權重
有就稱為帶權
沒有環的連通圖
無根樹上的任何一個節點都可以當作根
變成有根樹
一個 \(|V| \times |V|\) 的矩陣
記作 \(M\)
\(M_{u,v}\) 是邊 \((u,v)\) 的資訊
e.g. 存不存在、權重
空間複雜度:\(O(|V|^2)\)
時間複雜度:
\(M=\begin{bmatrix} \text{X} & 10 & \text{X} & 2 & 5 \\ 10 & \text{X} & 7 & 3 & \text{X} \\ \text{X} & 7 & \text{X} & \text{X} & \text{X} \\ 2 & 3 & \text{X} & \text{X} & \text{X} \\ 5 & \text{X} & \text{X} & \text{X} & \text{X} \end{bmatrix}\)
開 \(|V|\) 個大小可變的容器
第 \(i\) 個存節點 \(i\) 的鄰邊或出邊資訊
空間複雜度:\(O(|V|+|E|)\)
時間複雜度:
鄰接矩陣:\(O(|V|^2)\)
鄰接串列:\(O(|V|+|E|)\)
Depth-First Search
盡量往深處走
某種遞迴
vector<vector<int>> g; //鄰接串列
vector<bool> vst; //用來記錄每個點走過了沒
void dfs(int now){
vst[now] = true;
//do something
for(int i : g[now]){
if(vst[i]) continue;
dfs(i);
}
//do something
}
呼叫 \(dfs(v)\) \(\Rightarrow\) \(v\) 入堆
\(\Rightarrow\) \(v\) 入點
\(dfs(v)\) 結束 \(\Rightarrow\) \(v\) 出堆
\(\Rightarrow\) \(v\) 出點
入點順序:前序
出點順序:後序
時間複雜度:\(O(|V|+|E|)\)
Breadth-First Search
先把所有知道可以走的點走完
vector<vector<int>> g; //鄰接串列
vector<bool> vst; //某個點有沒有走過或有沒有在 queue 裡
queue<int> q;
q.push(...); //把起點放進去
while(!q.empty()){
int now = q.front();
q.pop();
for(int i : g[now]){
if(vst[i]) continue;
q.push(i);
vst[i] = true;
}
}
離起點進的先、離起點遠的後
\(\Rightarrow\) 可以拿來做不帶權圖最短路徑
時間複雜度:\(O(|V|+|E|)\)
更經典的下一堂課才會講 (?
\(N \times M\) 的二維表格可以視為
有 \(N+M\) 個節點的圖
如果某兩個格子可以互通
那就在它們兩個的節點之間連邊
小技巧:
通常能互通的格子相對位置是固定的
所以直接去看相對位置就好
不帶權最短路徑
\(\Rightarrow\) BFS
Euler path
一條經過所有的邊
且不經過重複的邊
但可以經過重複的點的路徑
無向圖:
恰有 0 個或 2 個度數是奇數的點
有向圖:
所有點的入度等於出度
或有恰一個點的入度比出度多 1
和恰一個點的出度比入度多 1
對邊 DFS
再把出點順序倒過來
Hamiltonian path
經過所有點恰一次的路徑
(起終點相同不算重複)
位元 DP
有時候題目裡不會出現任何關於圖的關鍵字
看起來也沒有類似點、邊的東西
但解法卻和圖論有關
在樹上最長的簡單路徑
先隨便找一個點 \(u\)
找到離它最遠的點 \(v\)
再找到離 \(v\) 最遠的點 \(w\)
\(v\) 到 \(w\) 的簡單路徑就是樹直徑
兩次 DFS
以它為根時
樹的深度會最小的點
把一個節點 \(v\) 從樹上拔掉
出現的連通塊大小都不超過本來點數的一半
\(v\) 就是樹重心
每個節點最多只有兩個子節點的有根樹
有時候子節點會有左右之分
//l[i]=i的左子節點,r[i]=i的右子節點,-1表示沒有
vector<int> l, r;
void dfs(int now){
//preorder
if(l[i] != -1) dfs(l[i]);
//inorder
if(r[i] != -1) dfs(r[i]);
//postorder
}