算法導論(22.1):圖的表示

對圖算法進行討論需要引入一些約定。
給定 圖G = (V, E)
當對此圖上的算法的運行時間進行描述時,我們通常以
圖的結點數|V| 和 邊的條數|E|作爲輸入規模
僅在漸進記號中可用符號V、E代替
此外,僞碼中用G.V表示圖G的結點集,G.E表示圖G的邊集

兩種表示方法

鄰接鏈表和鄰接矩陣,都可用於表示有向圖和無向圖。

鄰接鏈表

通常使用鄰接鏈表表示稀疏圖,即 |E| << |V|^2 的圖。

無論表示有向圖還是無向圖,鄰接鏈表的存儲空間需求都是 Θ(V+E)

鄰接鏈表的一個缺陷是無法快速判斷一條 邊(u, v)是否存在於圖中
唯一的辦法是在鄰接鏈表Adj[u]中查找結點v

鄰接矩陣

通常使用鄰接矩陣表示稠密圖。

無論一個圖有多少條邊,鄰接矩陣的存儲空間需求都是 Θ(V^2)
在表示無向圖時可利用壓縮矩陣來存儲,其存儲空間需求減少爲鄰接矩陣需求的一半

鄰接矩陣克服了上述鄰接鏈表的缺陷,但付出的存儲代價增大。

在圖規模比較小時,一般用鄰接矩陣表示法更優
一是較簡單,二是每個記錄項僅需1位空間

無向圖

在這裏插入圖片描述

以2號結點爲例,在圖上與1 5 4 3 相連
則鄰接鏈表中,代表2號結點的鏈表由指向1 5 4 3結點的指針組成
鄰接矩陣爲對稱矩陣
其他類似

所有鄰接鏈表的長度和等於 2|E|,因爲每條邊都被計算了兩次

有向圖

在這裏插入圖片描述

以2號結點爲例
鄰接鏈表中,邊 (2, 5)的5結點出現在2的鏈表中,而邊(4, 2)的2結點會出現在4的鏈表中
鄰接矩陣中,座標爲(2, 5)即代表邊(2, 5)
其他類似

所有鄰接鏈表的長度等於|E|,每條邊在鄰接鏈表表示法中僅計算一次

權重圖

權重圖是圖中每條邊都帶有一個相關權重的圖

用鄰接鏈表表示時
邊(u, v)的權重可以放在u的鄰接鏈表裏

用鄰接矩陣表示時
邊(u, v)的權重可以放在座標爲(u, v)的位置,如不存在則放None或0或…

圖屬性的表示

算法表示:如u.d表示結點u的屬性d

程序實現:依賴因素較多。
如使用鄰接鏈表表示圖時,可以使用額外的數組表示結點屬性。
如增設一個與Adj數組對應的數組d[1...|V|],如果與u鄰接的結點都在Adj[u]中,則可將結點u的屬性存放到d[u]的位置

原裝Python實現

#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
@author: 無名Joker
@time:2020/02/29
Purpose:
有向圖和無向圖的Python實現,每種圖都使用鄰接鏈表和鄰接矩陣分別實現

Arguments:
Outputs:
本模塊對外提供:
基於鄰接鏈表的有向圖類:DirectedGraphAL
基於鄰接矩陣的有向圖類:DirectedGraphAM
基於鄰接鏈表的無向圖類:UndirectedGraphAL
基於鄰接矩陣的無向圖類:UndirectedGraphAM

"""


# 基於鄰接鏈表(AdjacencyList)的有向圖(DirectedGraph)實現
class DirectedGraphAL:
    def __init__(self):
        # 使用字典內嵌集合的數據結構存放所有的鄰接鏈表
        # 每個鄰接鏈表以 結點名: {其他結點名, 其他結點名} 的形式存放
        self.adj = dict()

    # 返回本圖中所有鄰接鏈表的key,即結點名
    def get_vertices(self):
        all_vertex_key = list()
        for key in self.adj:
            all_vertex_key.append(key)
        return all_vertex_key

    # 向圖中添加新結點,u爲結點名
    def add_vertex(self, u):
        if u in self.adj:
            print("%r is already in the graph" % u)
        else:
            # 用set存放所有有向邊(u, v)的v的結點名
            self.adj[u] = set()

    # 傳入結點名,移除某結點及相關所有邊
    def remove_vertex(self, u):
        if u not in self.adj:
            print("%r is not in the graph" % u)
        else:
            # 移除本結點
            del self.adj[u]
            # 移除其他結點和本結點的邊
            for vtx in self.adj:
                if u in self.adj[vtx]:
                    self.adj[vtx].remove(u)

    # 增加一條有向邊 (u, v)
    def add_edge(self, u, v):
        if u not in self.adj or v not in self.adj:
            print("at least one vertex is not in the graph, failed to add this edge")
        else:
            self.adj[u].add(v)

    # 移除有向邊
    def remove_edge(self, u, v):
        if u not in self.adj or v not in self.adj:
            print("this edge doesn't exist, failed to remove it")
        else:
            self.adj[u].remove(v)

    # 返回某結點的所有鄰接結點名
    def get_neighbors(self, u):
        if u not in self.adj:
            print("%r is not in the graph" % u)
        else:
            return self.adj[u]


# 基於鄰接矩陣的有向圖類
class DirectedGraphAM:
    def __init__(self):
        # 用二重字典做鄰接矩陣的存儲
        """格式如
        {
        '1': {'1': 0, '2': 1},
        '2': {'1': 1, '2': 0}
        }
        """
        self.matrix = dict()

    # 返回所有結點名稱
    def get_vertices(self):
        all_vertex_key = list()
        for k in iter(self.matrix):
            all_vertex_key.append(k)
        return all_vertex_key

    # 添加結點
    def add_vertex(self, u):
        if u not in self.matrix:
            self.matrix[u] = dict()
        else:
            print("%r is already in the graph" % u)

    # 刪除結點
    def remove_vertex(self, u):
        if u not in self.matrix:
            print("%r is not in the graph" % u)
        else:
            # 刪除行
            del self.matrix[u]
            for v in self.matrix:
                # 刪除列
                del self.matrix[v][u]

    # 初始化鄰接矩陣
    def init_matrix(self):
        for u in self.matrix:
            for v in self.matrix:
                # 每個結點與其他結點的距離初始化爲無窮
                self.matrix[u][v] = float("inf")

    # 添加有向邊(u, v)
    def add_edge(self, u, v):
        if u not in self.matrix or v not in self.matrix:
            print("at least one vertex is not in the graph, failed to add this edge")
        else:
            self.matrix[u][v] = True

    # 刪除有向邊
    def remove_edge(self, u, v):
        if u not in self.matrix or v not in self.matrix:
            print("this edge doesn't exist, failed to remove it")
        else:
            self.matrix[u][v] = float("inf")

    # 返回某結點的所有鄰接結點名,有向
    def get_neighbors(self, u):
        if u not in self.matrix:
            print("%r is not in the graph" % u)
        else:
            u_neighbors = list()
            for nb in self.matrix[u]:
                if self.matrix[u][nb] is True:
                    u_neighbors.append(nb)
            return u_neighbors


# 基於鄰接鏈表的無向圖
class UndirectedGraphAL(DirectedGraphAL):
    # 重寫添加邊的方法
    def add_edge(self, u, v):
        if u not in self.adj or v not in self.adj:
            print("at least one vertex is not in the graph, failed to add this edge")
        else:
            # 雙向添加
            self.adj[u].add(v)
            self.adj[v].add(u)

    # 重寫移除邊的方法
    def remove_edge(self, u, v):
        if u not in self.adj or v not in self.adj:
            print("this edge doesn't exist, failed to remove it")
        else:
            self.adj[u].remove(v)
            self.adj[v].remove(u)


# 基於鄰接矩陣的無向圖
class UndirectedGraphAM(DirectedGraphAM):
    # 重寫 添加無向邊(u, v)
    def add_edge(self, u, v):
        if u not in self.matrix or v not in self.matrix:
            print("at least one vertex is not in the graph, failed to add this edge")
        else:
            self.matrix[u][v] = True
            self.matrix[v][u] = True

    # 重寫 刪除無向邊
    def remove_edge(self, u, v):
        if u not in self.matrix or v not in self.matrix:
            print("this edge doesn't exist, failed to remove it")
        else:
            self.matrix[u][v] = float("inf")
            self.matrix[v][u] = float("inf")


if __name__ == '__main__':
    """
    # 1.基於鄰接鏈表的有向圖

    dgal = DirectedGraphAL()

    # 添加六個結點
    dgal.add_vertex("1")
    dgal.add_vertex("2")
    dgal.add_vertex("3")
    dgal.add_vertex("4")
    dgal.add_vertex("5")
    dgal.add_vertex("6")
    print(dgal.get_vertices())

    # 刪除結點時一併刪除相關所有邊
    dgal.add_vertex("7")
    dgal.add_edge("6", "7")
    for ver in range(1, 7):
        print(str(ver), dgal.get_neighbors(str(ver)))
    print()
    dgal.remove_vertex("7")
    for ver in range(1, 7):
        print(str(ver), dgal.get_neighbors(str(ver)))
    print()

    # 添加有向邊
    dgal.add_edge("1", "2")
    dgal.add_edge("1", "4")
    dgal.add_edge("2", "5")
    dgal.add_edge("3", "5")
    dgal.add_edge("3", "6")
    dgal.add_edge("4", "2")
    dgal.add_edge("5", "4")
    dgal.add_edge("6", "6")
    for ver in range(1, 7):
        print(str(ver), dgal.get_neighbors(str(ver)))
    print()
    
    # 刪除有向邊
    dgal.remove_edge("6", "6")
    for ver in range(1, 7):
        print(str(ver), dgal.get_neighbors(str(ver)))
    """

    """
    # 2.基於鄰接矩陣的有向圖
    dgam = DirectedGraphAM()
    dgam.add_vertex("1")
    dgam.add_vertex("2")
    dgam.add_vertex("3")
    dgam.add_vertex("4")
    dgam.add_vertex("5")
    dgam.add_vertex("6")

    print(dgam.get_vertices())

    print(dgam.matrix)
    dgam.init_matrix()
    for k in dgam.matrix:
        print(k, dgam.matrix[k])

    dgam.remove_vertex("1")
    for k in dgam.matrix:
        print(k, dgam.matrix[k])

    print(dgam.get_neighbors('4'))

    dgam.add_edge('4', '5')
    print(dgam.get_neighbors('4'))

    dgam.remove_edge('4', '5')
    print(dgam.get_neighbors('4'))
    """

    """
    # 3.基於鄰接鏈表的無向圖
    ugal = UndirectedGraphAL()
    ugal.add_vertex("1")
    ugal.add_vertex("2")
    ugal.add_vertex("3")

    # 添加無向邊
    ugal.add_edge("1", "2")
    ugal.add_edge("1", "3")
    ugal.add_edge("2", "3")
    for ver in range(1, 4):
        print(str(ver), ugal.get_neighbors(str(ver)))
    print()

    # 刪除無向邊
    ugal.remove_edge("2", "3")
    for ver in range(1, 4):
        print(str(ver), ugal.get_neighbors(str(ver)))
    print()
    """

    """
    # 4.基於鄰接矩陣的無向圖
    ugam = UndirectedGraphAM()
    ugam.add_vertex("1")
    ugam.add_vertex("2")
    ugam.add_vertex("3")
    ugam.init_matrix()

    ugam.add_edge("1", "2")
    ugam.add_edge("1", "3")
    ugam.add_edge("2", "3")
    for k in ugam.matrix:
        print(k, ugam.matrix[k])
    print()

    ugam.remove_edge("2", "3")
    for k in ugam.matrix:
        print(k, ugam.matrix[k])
    print()
    """

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