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 | // T is a BFS tree rooted s |
1 | BFS(s) |
對於 \(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 | DFS(s) |
- 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)\)