Python數據結構之圖基礎

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)
    

參考

搜索思想——DFS & BFS(基礎基礎篇

鄰接表

數據結構與算法–圖 一步一步帶你用Python實現圖的深度遍歷和廣度優先遍歷 Python實現圖的深度遍歷和廣度優先遍歷 Python詳解DFS和BFS過程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章