Chapter 3 -- Graph ( 2 )

Representing Graph

$G=(V,E)$ is a graph

  • $\mid V\mid=$ the number of nodes $\overset{let}{=} n$
    $\mid E\mid=$ the number of edges $\overset{let}{=} m$
    $\Longrightarrow n-1\leq m\leq \displaystyle{\binom{n}{r}} \leq n^2$
    $\Longrightarrow$ 我們會希望整個複雜度可以控制在 $O(m+n)$

Adjacency Matrix

The $adjacency\ matrix$ of $G$ 是一個 $n\times n$ 矩陣 $A$
$$
A=\begin{bmatrix}a_{11}a_{12}\cdots a_{1n}\a_{21}a_{22}\cdots a_{2n}\\vdots\a_{n1}a_{n2}\cdots a_{nn}\end{bmatrix}
$$

  • $a_{ij}=1$ , if $(i,j)\in E$
  • $a_{ij}=0$ , if $(i,j)\not\in E$
  • If $G$ is undirected graph, then $A$ is symmetric.
  • The time complexity for checking if $(i,j)\in E=O(1)$
    ( 我們只要找第 $i$ 列第 $j$ 行即可知道是否存在 $(i,j)$ )
  • The time complexity for finding out all neighbors of $i\in V=O(n)$
    ( 需要遍歷第 $i$ 行(列),才能找出所有 neighbors )
  • The space complexity $=O(n^2)$

我們可以從這個矩陣中 0 的數量跟密集程度來看看這是 Sparse Graph 還是 Dense Graph,如果今天是一個 Sparse Graph,其矩陣也是 Sparse,那麼相當於我們必須利用許多的空間來儲存 0 這樣無意義的資料,由此我們可以知道, 使用 adjacency matrix 來表示 Sparse Graph 是不太適合的。

Adjacency List

$Adjacency\ List$ 中我們會先以一個 array 儲存每一個 vertex,之後再用 Linked List 來表示 vertex 與 vertex 之間的相鄰關係。

$$
Adj[i]={j\mid (i,j)\in E}\
\mid Adj[i]\mid=degree(i)=deg(i)=the\ number\ of\ neighbors\ of\ i
$$

  • The time complexity for checking if $(i,j)\in E=O(n)$
    ( the worse case 就是要遍歷完整串 Linked List 才會確認兩者是否存在 edge )
  • The space complexity $=O(n+m)$
  • in-degree : 指的就是指向自己的 edge 數
    out-degree : 指的就是指向別人的 edge 數


( Adjacency Mtrix v.s. Adjacency List,圖片來源 : Graph: Intro(簡介) )

Implementation

在介紹 BFS 與 DFS 的 implementation 之前,我們要先了解所使用的資料結構 : Queue and Stack

Queue

Queue 我們可以看作是一個水管,資料進出的型態是 「先進先出」 (First in, First out. FIFO),而這樣的資料結構伴隨兩個 Pointer : front 以及 rear,這兩個指標分別指出目前資料的頭尾,其實也是指出接下來要輸出、輸入的資料位置。

Stack

而 Stack 比較像是一個水瓶,資料型態則是 「後進先出」 (Last in, First out. LIFO),值得注意的是,在 Stack 這樣的資料型態中,我們只需要一個 Pointer : Top ,用來指出最後進來的資料是誰,也相當於指出接下來要輸出的資料是哪一筆。

不論是 Queue 還是 Stack,我們都可以利用 Linked List 來進行 implement。

Implementing BFS

在要進行 BFS 的 implementation 以前,我們得先決定 Garaph representation,在 BFS 裡面我們使用的是 Adjacency List 來表現一個 Graph,其原因是 Adjacency List 的儲存方式跟 BFS 的搜索方式是吻合的,如此一來可以有效降低 Complexity。

1
2
3
// T is a BFS tree rooted s
// Layers count i
// L[i] is Layer_list
1
2
3
4
5
6
7
8
9
10
11
BFS(s)
1. init i=0, L[0]={s}, T={} and mark[s]=True, marked[others] are False
# 設定各項初始值
2. while L[i] is not empty then L[i+1]={}
for each u in L[i]
for each (u,v) in E :
if marked[v] = false then
marked[v] = true
T = T + {(u,v)}
L[i+1] = L[i+1] + {v}
i+=1
  • 對於 $L[i]$ ,我們應該要用什麼資料結構來處理 ?
    事實上,Queue / Stack 均可,這兩者差異在於資料處理的順序,但在 BFS 中,每個 Layer 內部的順序性並非那麼重要。( 但若我們今天只想要用一條 List 儲存所有的 Layer 那就必須使用 Queue,因為層與層之間的順序性必須被考慮進去。 )

  • Time Complexity : $O(m+n)$
    在 BFS 的過程中,每一個 Vertex 都必須 check 每一個 neighbor,如果我們選用的是 Adjacency List ,便可把複雜度控制在 $O(m+n)$ 之中。

但倘若我們選擇的是 Adjacency Matrix,則複雜度會趨近於 $n^2$ 。

Implementing DFS

之前我們給的 DFS 是一個 recursive code,也可以將其譯為一個 iterative 的版本。

這裡我們使用的是 stack 的型態來處理 vertex set S。

1
// S is a stack of vertice its neighbors haven't been marked entirely.
1
2
3
4
5
6
7
8
DFS(s)
1. init S={s}
2. while S is not empty then
remove a vertex u from S
if marked[u] = false then
marked[u] = true
for each (u,v) in E
S+={v}
  • iterative code 使用的是 stack 形式做處理,而 recursive code 其實也是 stack,因為 recursive 本身就是一個 stack 形式。
  • Time Complexity : $O(m+n)$ …. Adjacency List

Summary

Adjacency matrix VS. Adjacency list for graph

Scenario Choice Complexity
Find an edge Adjacency matrix $O(1)$
Find degree Adjacency list $<O(n)$
Traverse the graph Adjacency list $O(m+n)$
Storage for sparse graph Adjacency list $<O(n^2)$
Storage for dense graph Adjacency matrix $O(n^2)$
Edge insertion/deletion Adjacency matrix $O(1)$
Most application Adjacency list -

Graph traversal

  • Data structure of BFS : queue / stack
  • Data structure of DFS : stack
  • Implementation of BFS/DFS with Adjacency list : $O(n+m)$