Python數據結構之圖基礎
什麼是圖?
- 表示多對多的關係
- 一組頂點,通常用V(Vertex)表示頂點集合
- 一組邊,通常用E(Edge)表示邊的集合,邊是頂點對,分爲有向邊和無向邊
圖的創建
鄰接矩陣
-
代碼
class Graph: """創建圖""" def __init__(self, n): self.vertex_list = [] self.edges = [[0 for i in range(n)] for j in range(n)] # 初始化圖 self.num_of_edges = 0 # 記錄有效邊數目 def get_num_of_vertex(self): """獲取頂點數目""" return len(self.vertex_list) def get_val_by_index(self, index): """返回結點的下標""" return self.vertex_list[index] def get_wight(self, v1, v2): """獲取邊的權重""" return self.edges[v1][v2] def show_graph(self): for col in self.edges: print(col) def insert_edge(self, v1, v2, weight): """插入邊""" self.edges[v1][v2] = weight self.edges[v2][v1] = weight self.num_of_edges += 1 def insert_vertex(self, vertex): """插入頂點""" self.vertex_list.append(vertex) if __name__ == "__main__": graph = Graph(5) print("原始圖結構:") vertex_val = ["A", "B", "C", "D", "E"] for vertex in vertex_val: graph.insert_vertex(vertex) graph.show_graph() graph.insert_edge(1, 2, 5) graph.insert_edge(2, 4, 6) graph.insert_edge(3, 1, 4) graph.insert_edge(2, 2, 5) print("增加邊的結構:") graph.show_graph()
-
結果
原始圖結構: [0, 0, 0, 0, 0] [0, 0, 0, 0, 0] [0, 0, 0, 0, 0] [0, 0, 0, 0, 0] [0, 0, 0, 0, 0] 增加邊的結構: [0, 0, 0, 0, 0] [0, 0, 5, 4, 0] [0, 5, 5, 0, 6] [0, 4, 0, 0, 0] [0, 0, 6, 0, 0]
-
結論
使用鄰接矩陣的形式實現圖結構,能夠更加直觀的理解頂點之間的關係,也能夠很好的計算相應頂點的出度和入度,但是,對於無向圖而言,鄰接矩陣的使用,會同時保存兩個頂點之間的的兩條邊關係,造成存儲浪費,所以,可以使用一維列表之間進行存儲,減少一半的存儲關係或者使用鄰接表的方式。
鄰接表
-
代碼
class Vertex: """創建頂點類""" def __init__(self, key): self.id = key self.connectedTo = {} def add_neighbor(self, nbr, weight): self.connectedTo[nbr] = weight def __str__(self): return str(self.id) + " connected to " + str([x.id for x in self.connectedTo]) def get_connections(self): return self.connectedTo.keys() def get_id(self): return self.id def get_weight(self, nbr): return self.connectedTo[nbr] class Graph: """創建圖""" def __init__(self): self.vertList = {} self.numVertice = 0 def add_vertex(self, key): self.numVertice += 1 newVertex = Vertex(key) self.vertList[key] = newVertex # 添加新的頂點 return newVertex def get_vertex(self, n): if n in self.vertList: return self.vertList else: return None def __contains__(self, item): return n in self.vertList def add_edge(self, f, t, cost=0): if f not in self.vertList: nv = self.add_vertex(f) if t not in self.vertList: nv = self.add_vertex(t) self.vertList[f].add_neighbor(self.vertList[t], cost) def get_vertics(self): return self.vertList.keys() def __iter__(self): return iter(self.vertList.values()) if __name__ == "__main__": g = Graph() for i in range(6): g.add_vertex(i) g.add_edge(0,1,5) g.add_edge(0,5,2) g.add_edge(1,2,4) g.add_edge(2,3,9) g.add_edge(3,4,7) g.add_edge(3,5,3) g.add_edge(4,0,1) g.add_edge(5,4,8) g.add_edge(5,2,1) for x in g: print(x)
-
結果
0 connected to [1, 5] 1 connected to [2] 2 connected to [3] 3 connected to [4, 5] 4 connected to [0] 5 connected to [4, 2]
-
結論
在python中可以使用字典來實現圖鄰接表的形式,對其而言,能較爲方便的查找任一頂點的所有鄰接點,減少了稀疏圖(點多邊少)佔存儲空間的麻煩,可以直觀的計算出任一頂點的出度(有向圖),但是,入度無法很好計算得出。
圖的遍歷
DFS(深度優先搜索)
DFS的實現過程與樹的先序號遍歷相同,DFS的實現過程中要設置相應的頂點訪問標識,已經訪問過的結點不在訪問,實現過程最重要的是狀態的回溯,可以使用遞歸。算法通俗理解:一條道走到黑(選擇),不行就撤(回溯),已走過不在重複(狀態標識)。
-
代碼
def get_first_neighbor(self, index): """查詢第一關係邊頂點""" for j in range(self.get_num_of_vertexs()): if self.edges[index][j] > 0: return j return -1 def get_next_neighbor(self, v1, v2): """查詢第二個結點""" for j in range(v2 + 1, self.get_num_of_vertexs()): if self.edges[v1][j] > 0: return j return -1 def dfs(self, is_visited, i): """深度優先查找""" print(self.get_val_by_index(i), "->", end=" ") self.is_visited[i] = True first = self.get_first_neighbor(i) while first != -1: if not self.is_visited[first]: self.dfs(is_visited, first) # 遞歸遍歷 first = self.get_next_neighbor(i, first) def dfs_override(self): """遍歷每一個子圖""" for i in range(self.get_num_of_vertexs()): if not self.is_visited[i]: self.dfs(self.is_visited, i)
BFS(廣度優先搜素)
BFS相當於樹的層序遍歷,可以使用隊列對頂點進行存儲搜索,讓後先進後出的對頂點的有效邊頂點進行搜索,已搜索過的便不再搜素,所以,要標識頂點的搜索狀態。
-
代碼
def get_first_neighbor(self, index): """查詢第一關係邊頂點""" for j in range(self.get_num_of_vertexs()): if self.edges[index][j] > 0: return j return -1 def get_next_neighbor(self, v1, v2): """查詢第二個結點""" for j in range(v2 + 1, self.get_num_of_vertexs()): if self.edges[v1][j] > 0: return j return -1 def bfs(self, is_visited, i): """寬度優先搜索""" queue = [] print(self.get_val_by_index(i) + "->", end=" ") self.is_visited[i] = True queue.append(i) while queue: u = queue.pop(0) w = self.get_first_neighbor(u) while w != -1: if not is_visited[w]: print(self.get_val_by_index(w), "->", end=" ") self.is_visited[w] = True queue.append(w) w = self.get_next_neighbor(u, w) def bsf_override(self): for j in range(self.get_num_of_vertexs()): if not self.is_visited[j]: self.bfs(self.is_visited, j)
參考
數據結構與算法–圖 一步一步帶你用Python實現圖的深度遍歷和廣度優先遍歷 Python實現圖的深度遍歷和廣度優先遍歷 Python詳解DFS和BFS過程