欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

圖論基礎及BFS/DFS

程序员文章站 2024-03-16 20:54:22
...

Graph

在介紹BFS與DFS的定義前,首先應對圖(Graph)的概念有所了解,故本文會首先說明資料結構中的圖形結構與其基本表達方式,可從目錄直接閱讀圖的訪尋部分

Content

Graphs

Some Concepts

Data Structures for Graphs

     Adjacency Matrix(鄰接矩陣)

     Adjacency List(鄰接表)

     Incidence Matrix(關聯矩陣)

Graph Traversal

     Breadth-First Search(廣度優先搜尋)

     Depth-First Search(深度優先搜尋)

     BFS V.S. DFS

Code

Graphs(圖)

A graph G = (V, E) is simply a set V of vertices(頂點) and a collection E of pairs of vertices from V, called edges(邊). Thus, a graph is a way of represnting connections or relationships between pairs of objects from some set V

Undirected Graph(無向圖)

An edge (u,v) is said to be directed from u to v if the pair (u,v) is ordered, with u preceding v. If all the edges in a graph are undirected , then we say the graph is an undirected graph.

Directed Graph(有向圖)

An edge (u,v) is said to be undirected if the pair (u,v) is not ordered. If all the edges in a graph are undirected, then we say the graph is an undirected graph.

Ex.

圖論基礎及BFS/DFS
G1(undirected graph)

V(G1) = {1,2,3,4,5}

E(G1) = {(1,2),(1,3),(1,5),(2,3),(2,5),(3,4)}

G2(directed raph)

V(G2) = {1,2,3,4,5}

E(G1) = {<1,2>,<1,5>,<5,1>,<2,3>, < 3,1>,< 3,4>,<4,3>}

Mixed Graph

A graph that has both directed and undirected edges is often called a mixed graph. Note that an undirected or mixed graph can be converted into a directed graph by replacing every undirected edge (u,v) by the pair of directed edges (u,v) and (v,u).

Some Concepts

1.End Vertices: The two vertices joined by an edge are called the end vertices of the edge. If an edge is directed, its first endpoints is its origin and other is the destination of the edge.

2.Adjacent(鄰接): Two vertices u and v are said to be adjacent if there is an edge whose end vertices are u and v.

3.Incident(關聯): An edge is said to be incident to a vertex if the vertex is one of the edge’s endpoints.

4.Outcoming/Incoming Edge(出邊/入邊): The outgoing edges of a vertex are the directed edges whose origion/destination is that vertex.

5.In-degree/Out-degree(入支度/出支度): The in-degree and out-degree of a vertex v are the number of incoming and outgoing edges of v.

6.Degree(分支度): The degree of vertex v, is the number of incident edges of v.

7.path(路徑): A path is a sequence of alternating vertices and edges that starts at a vertex and ends at a vertex such that each edge is incident to its predecessor and successor vertex.

8.Cycle(循環): A cycle is a path that starts and ends at the same vertex, and that includes at least one edge.

9.Simple: A path is simple if each vertex in the path is distinct, and we say that a cycle is simple if each vertex in the cycle is distinct, except for the first and last one.

10.Connected Graph(相連圖形): A graph is connected if, for any two vertices, there is a path between them.

11.Subgrapg(子圖): A subgraph of a graph G is a graph H whose vertices and edges are subsets of the vertices and edges of G, respectivly.

Data Structures for Graphs

Adjacency Matrix(鄰接矩陣)

The adjacency matrix A(G) for a graph G = (E, V) with n vertices is a n × n matrix whose A(i,j) entry is 1 if the ith vertex and jth vertex are connected, and 0 if they are not. Note that matrix A is symmetric if graph G is undirected, as A(i,j) = A(j,i) for all pairs of i and j.

A[i,j]={1,if(Vi,Vj)E(G),0,if(Vi,Vj)E(G) A[i,j]= \begin{cases} 1, & if (V_i,V_j)\in E(G),\\ 0, & if (V_i,V_j)\notin E(G) \end{cases}

Ex:

圖論基礎及BFS/DFS
A(G1)=[0110110101110100010011000] A(G_1) = \left[ \begin{matrix} 0 & 1 & 1 & 0 & 1 \\ 1 & 0 & 1 & 0 & 1 \\ 1 & 1 & 0 & 1 & 0 \\ 0 & 0 & 1 & 0 & 0 \\ 1 & 1 & 0 & 0 & 0 \\ \end{matrix}\right]

A(G2)=[0100100101100100010010000] A(G_2)= \left[ \begin{matrix} 0 & 1 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 & 1 \\ 1 & 0 & 0 & 1 & 0 \\ 0 & 0 & 1 & 0 & 0 \\ 1 & 0 & 0 & 0 & 0 \\ \end{matrix}\right]

Adjacency List(鄰接表)

The adjacency list structure groups the edges of a graph by storing them in smaller, secondary containers that are associated with each individual vertex.

當頂點個數很多而邊數較少時,鄰接矩陣的儲存方式將造成儲存空間的浪費,而鄰接表使用數組與鏈結串列結合的方式,將頂點儲存在一維數組中,並將頂點的鄰接點儲存在鏈結串列中

Ex: undirected graph(cont.)

[1]→[2]→[3]→[5]

[2]→[1]→[3]→[5]

[3]→[1]→[2]→[4]

[4]→[3]

[5]→[1]→[2]

Ex: directed graph(cont.)

[1]→[2]→[5]

[2]→[5]→[3]

[3]→[1]→[4]

[4]→[3]

[5]→[1]

Incidence Matrix(關聯矩陣)

The (vertex-edge) incidence matrix I(G) of a grapg G = (E, V), is a n × m matrix defined as follows. The rows and columns of I(G) are indexed by V(G) and E(G), respectively. The A(i,j) entry of I(G) is 0 if vertex vi and and edge ej are not incident, and otherwise it is 1 or -1 according as ej originates or terminates at i, respectively.

I[i,j]={1,if vi is the origin of ej,1,if vi is the destination of ej,0,if ei and vi are not incident I[i,j]= \begin{cases} 1, & if \ v_i \ is \ the \ origin \ of \ e_j,\\ -1, & if \ v_i \ is \ the \ destination \ of \ e_j,\\ 0, & if \ e_i \ and \ v_i \ are \ not \ incident \end{cases}

Ex.

圖論基礎及BFS/DFS
I(G)=[111000100100010010001001000111] I(G)= \left[ \begin{matrix} -1 & 1 & -1 & 0 & 0 & 0 \\ 1 & 0 & 0 & -1 & 0 & 0 \\ 0 & -1 & 0 & 0 & 1 & 0 \\ 0 & 0 & 1 & 0 & 0 & -1 \\ 0 & 0 & 0 & 1 & -1 & 1 \end{matrix}\right]

Graph Traversal

Breadth-First Search(廣度優先搜尋)

廣度優先搜尋從起始頂點v開始走訪,接著依序訪尋所有與起始頂點v相鄰但未走訪的頂點,依照此法不斷擴展,走訪與起始頂點的鄰接點相鄰接的所有頂點,直至訪尋完全部頂點,簡而言之為先廣後深

解法

BFS的實作需要使用**佇列(queue)**的資料結構,且在訪尋的過程中需要記錄頂點的狀態,示意圖中使用1表示尚未走訪且尚未放入queue中的頂點,2表示尚未走訪但已放入queue中的頂點,3表示已訪尋過的頂點。首先,假設頂點v1為起始頂點,將v1放入queue,訪尋v1後,將v1從queue中刪除,並根據鄰接表將v1的鄰接點依序加入queue,但放入的鄰接點需為未訪尋過也尚未在queue中的頂點,即狀態為1的頂點,故將v2v5加入queue。依據queue先進先出的原則,下一個訪尋的頂點為v2,同理,將v2從queue中刪除,並將其鄰接點v3v4依序加入queue。以此類推,直至queue為空,則完成BFS走訪。

圖示

圖論基礎及BFS/DFS

Depth-First Search(深度優先搜尋)

深度優先搜尋先訪問圖中某個起始頂點v,然後訪問與v鄰接且未被訪問的某一頂點,再訪問與此頂點鄰接且未被訪尋的頂點,當無法繼續向下訪問時,退回至最近被訪問的頂點,若其還有鄰接頂點未被訪尋過,則從該點繼續上述過程,直至訪尋完所有頂點。

解法

DFS的實作則需要使用**堆疊(stack)**的資料結構,同時也需記錄頂點的狀態。假設v1為起始頂點,將v1放入stack,訪尋v1後,將v1從stack中刪除,並根據鄰接表將v1的鄰接點依序加入stack,同樣放入的鄰接點之狀態需為1,故將v2v5加入stack。依據stack後進先出的原則,下一個訪尋的頂點為v5,同理,將v5從stack中移除,並將其鄰接點v6放入stack。以此類推,直至stack為空,則完成DFS走訪。

圖示

圖論基礎及BFS/DFS

BFS V.S. DFS

Time Complexity

BFS: O(V+E)

DFS: O(V+E)

事實上,BFS和DFS的時間複雜度與儲存圖的所資料結構有關,如果使用鄰接表(adjacency list)儲存,則時間複雜度為O(V+E),如果使用鄰接矩陣(adjacency matrix)儲存,則時間複雜度為O(V2)

Space Complexity

Application

  • BFS

    Shortest path in an unweighted graph

    Garbage collection

    Web crawler
  • DFS

    Detecting a cycle

    Topology sort(拓撲排序) for DAG(directed acyclic graph)

    Strongly connected component(強連通元件): Kosaraju’s algorithm/Tarjan’s algorithm

    Path finding

Code

from collections import defaultdict

class Graph():
    def __init__(self):
        self.graph = defaultdict(list)
    
    def addEdge(self, u, v):
        self.graph[u].append(v)
    
    def BFS(self, s):
        #adjacency list使用dictionary的結構實現,故其keys為頂點
		vertices = list(self.graph.keys())  
		#初始化所有的頂點狀態為1,以dictionary的形式記錄
        status_dict = dict.fromkeys(vertices,1) 
        
        queue = []           #建立queue以記錄待訪尋的頂點
        result = []          #建立result以記錄已訪尋的頂點
        
        queue.append(s)      #將起始頂點加入queue
        status_dict[s] = 2   #更改起始頂點狀態為2

        while queue != []:
            vertex = queue.pop(0)   #依照queue先進先出的特性,移除第一個元素
            result.append(vertex)   #將其放入result
            status_dict[vertex] = 3 #更改其狀態為3
            
            adj_vertices = self.graph[vertex]  #adj_vertices為移除頂點的鄰接點
            for item in adj_vertices:
                if status_dict[item] == 1:     #如果鄰接點的狀態為1
                    queue.append(item)         #則放入queue中
                    status_dict[item] = 2      #更改其狀態為2
                    
        return result
	
	def DFS(self, s):
        vertices = list(self.graph.keys())
        status_dict = dict.fromkeys(vertices,1) 
        
        stack = []    #建立stack以記錄待訪尋的頂點  
        result = []
        
        stack.append(s)
        status_dict[s] = 2

        while stack != []:
            vertex = stack.pop()    #依照stack後進先出的特性,移除最後一個元素             
            result.append(vertex)
            status_dict[vertex] = 3
            
            adj_vertices = self.graph[vertex]
            for item in adj_vertices:
                if status_dict[item] == 1:
                    stack.append(item)
                    status_dict[item] = 2
                    
        return result

Reference

1.Michael T. Goodrich & Roberto Tamassia &Michael H. Goldwasser. Data Structures and Algorithms in Python.

2.http://wayne.cif.takming.edu.tw/datastru/graphs.pdf

3.https://www.quora.com/Why-is-the-complexity-of-DFS-O-V+E

4.https://www.geeksforgeeks.org/applications-of-depth-first-search/