雜
對圖算法進行討論需要引入一些約定。
給定 圖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()
"""