圖論基礎 OwO

204

What is graph

  • 也許在上星期五你們在數讀聽過了QQ不過還是再複習一次
  • 和一般所說的圖稍微不同,通常不關心大小、長度或位置
  • 兩大要素:邊、頂點
  • 可表示元素之間關係,例如淘汰賽的樹狀圖或親戚關係圖

What is graph

  • 圖(Graph):一些代表元素們以及其之間關係的集合,通常用 \(G=(V,E)\) 代表
  • 頂點(Vertex):\(V\)代表頂點的集合,競程中幾乎都會用整數標號
  • 邊(Edge):\(E\)代表點與點關係的集合,可粗分為有向與無向兩種
  • 度數(Degree):記為\(\deg(v)\),代表有多少邊的端點是頂點\(v\)
  • 入度(In-degree):記為\(\deg^+(v)\),代表頂點\(v\)有幾條往自己的有向邊
  • 出度(Out-degree):記為\(\deg^-(v)\),代表頂點\(v\)有幾條往外的有向邊

What is graph

  • 無向圖(Undirected Graph):只含無向邊的圖
  • 有向圖(Directed Graph):只含有向邊的圖
  • 混合圖(Mixed Graph):同時含無向及有向邊的圖,競程中少見

What is graph

 

  • 重邊(Multiple Edges):\(E\)裡面有兩個或以上連接相同頂點的邊
  • 自環(Self-Loop):\(E\)裡面有邊的兩個端點相同

不包含以上兩種邊的稱為簡單圖,反之可能稱為偽圖或多重圖,一般來說競程中很少出現後者,今天先預設我們講的都是簡單圖

右圖中\(\{4,4\}\)為一自環,\(\{1,2\}\)為兩條重邊

What is graph

  • 相鄰(Adjacent):兩頂點有邊相連即是相鄰,互為鄰居
  • 路徑(Simple Path / Path / Trail / Walk):從某個頂點\(v\)經過一些邊到終點\(u\),一些術語的差異在於經過的點或邊可不可以重複
  • 環(Cycle / Simple Cycle):起點和終點相同的Path或Simple Path
  • 連通(Connected):在無向圖上,若以\(u\)為起點有路徑可以走到\(v\),則稱\(u,v\)兩點連通;整張圖為連通的代表任兩點皆連通

What is graph

What is graph

一些特殊的圖

  • 完全圖(Complete Graph):每對相異頂點間都有邊的圖,n個頂點的完全圖有\(\frac{n(n-1)}{2}\)條邊
  • 二分圖(Bipartite Graph):可以把所有頂點分成左右兩部分,使得沒有邊的兩個端點在同一部分;另一個等價的定義是沒有奇環(奇數長度的環)
  • 森林(Forest)、樹(Tree):森林是沒有環的圖,而樹則是連通的森林。另外樹還有很多等價的定義,例如兩點之間恰有一條簡單路徑、使用最少邊數讓整張圖連通

Storing the Graph

1. 鄰接矩陣(Adjacency Matrix)

用一個01矩陣\(A_{n\times n}\)儲存,\(a_{ij}=1\)代表有邊由\(i\)指向\(j\),反之則沒有

需要\(O(|V|^2)\)的空間

無向圖的情況通常會寫成\(a_{ij}=a_{ji}=1\)代表\(i,j\)兩頂點之間有邊

\begin{bmatrix} 0 & 1 & 0 & 0\\ 0 & 0 & 1 & 1 \\ 1 & 0 & 0 & 1 \\ 0 & 0 & 0 & 0 \end{bmatrix}

Storing the Graph

const int N = 1000;
int g[N][N];
void addEdge(int a, int b) { // 加上一條a到b的邊
    g[a][b] = 1;
}
void print_neighbors(int u) { // 印出u走得到的鄰居
    for(int v = 1; v <= n; v++)
        if(g[u][v])
            cout << v << ' ';
    cout << '\n';
}

very simple!

Storing the Graph

2. 鄰接串列(Adjacency List)

對於每個頂點,我們都開一個list紀錄他可以走到的鄰居有哪些

空間\(O(|V|+|E|)\),競程中最常用

通常會使用\(\texttt{std::vector}\)或是自己寫偽指標型list,而不會用\(\texttt{std::list}\)

\begin{matrix} 1 & \Rightarrow & 2 \\ 2 & \Rightarrow & 3 & 4 \\ 3 & \Rightarrow & 1 & 4 \\ 4 & \Rightarrow \end{matrix}

Storing the Graph

const int N = 200000;
vector<int> g[N];
void addEdge(int a, int b) { // 加上一條a到b的邊
    g[a].push_back(b);
}
void print_neighbors(int u) { // 印出u走得到的鄰居
    for(int v:g[u]) cout << v << ' ';
    cout << '\n';
}

very simple, too

Storing the Graph

const int N = 200000, M = 1000000; // 點數、邊數
// 大陸人超愛這樣寫 常數比較小的樣子
int tot,head[N],nxt[M],to[M];
void addEdge(int a, int b) { // 加上一條a到b的邊
    ++tot;
    nxt[tot] = head[a];
    to[tot] = b;
    head[a] = tot;
}
void print_neighbors(int u) { // 印出u走得到的鄰居
    for(int id = head[u]; id; id = nxt[id]) //以0當作結尾編號
        cout << to[id] << ' ';
    cout << '\n';
}

偽指標型list

Storing the Graph

3. 邊集(Edge List)

呃...就是開一個陣列只存邊的兩個端點

通常用在演算法只需枚舉邊,如Kruskal's Algorithm

E=\{ (2,3), (1,2), (3,1), (2,4), (3,4) \}

Storing the Graph

4. 前向星(Forward Star)

好像已經算舊時代的產品

和邊集一樣用陣列儲存,但是把起點相同的邊放在連續的區間,並對所有\(v\)記錄由\(v\)往外的邊所在的區間\([s_v,t_v)\)

通常直接以字典序排列邊集即可,可以使用counting sort維持時間複雜度在線性時間內

\left\{\begin{matrix} E &= & \{ (1,2), (2,3), (2,4), (3,1), (3,4) \} \\ I &= & \{ [0,1), [1,3), [3,5), [5,5), \} \end{matrix}\right.

Storing the Graph

const int N = 200000, M = 1000000; // 點數、邊數
struct Edge {
    int a,b;
} E[M];
int cnt[N],L[N],R[N],tot;
void addEdge(int a,int b) {
    E[tot].a = a, E[tot].b = b, tot++;
}
void arrange() { // 排序邊
    vector<Edge> tmp(tot);
    vector<int> cnt(n+1);
    for(int i = 0; i < tot; i++) cnt[E[i].a]++;
    for(int i = 1; i <= n; i++) cnt[i] += cnt[i-1];
    for(int i = 1; i <= n; i++) L[i] = cnt[i-1], R[i] = cnt[i];
    for(int i = 0; i < tot; i++) {
        int pos = --cnt[E[i].a];
        tmp[pos] = E[i];
    }
    for(int i = 0; i < tot; i++) E[i] = tmp[i];
}
void print_neighbors(int u) { // 印出u走得到的鄰居
    for(int i = L[u]; i < R[u]; i++)
        cout << E[i].b << ' ';
    cout << '\n';
}

Exercise

很廢的練習,如果上星期你有去數讀...

  • 請說明邊數與度數和以及入度和與出度和的關係
  • 證明一張圖及其補圖至少有一為連通圖
  • 給你一個有\(n\)個點的度數的序列\(\deg(v_1), \deg(v_2), \dots, \deg(v_n)\),請判斷是否存在一個簡單圖使得圖中點的度數序列恰為此序列。
  • 請給出一個儲存有向圖的方法,空間\(O(|V|+|E|)\),並且查詢一個有向邊的反邊只需要 \(O(1)\) 的時間

Exercise

很廢的練習,如果上星期你有去數讀...

  • 今天有座村莊,村裡的有些人是好朋友。某天有個感冒病毒感 染了村裡的幾個人,接下來的每一天,如果有朋友生病,他就會去探望他的好朋友。去探望的人若沒有免疫力隔天則會生病,而生病的人隔天會好起來並獲得免 疫力,有免疫力的人隔天會失去免疫力。證明: (A) 若一開始有人有免疫力,則病情可能不會結束。 (B) 若一開始沒有人有免疫力,則病情一定會結束。
  • 有 \(n > 4\) 個人,每個人都握有不同的訊息。每次可以找出兩個人進行交流,讓他們得到對方手上目前的所有訊息,證明要讓所有人知道所有訊息, 至少要做 \(2n − 4\) 次交流。

Traversal

圖的遍歷 (Traversal) 是指一張圖從某個點 v 開始,依照某種順序拜訪與已拜訪的點相鄰的點,最終拜訪過圖內所有頂點。而如果我們令 T 為包含拜訪過的節點和拜訪節點所經過的邊的子圖,可以知道 T 會形成一棵 (有向) 樹,我們把樹 T 稱作搜索生成樹。主要的遍歷方法有DFS、BFS等。

Disjoint Sets

-- 並查集 --

Made with Slides.com