Python數(shù)據(jù)結(jié)構(gòu)與算法之圖的基本實現(xiàn)及迭代器實例詳解
本文實例講述了Python數(shù)據(jù)結(jié)構(gòu)與算法之圖的基本實現(xiàn)及迭代器。分享給大家供大家參考,具體如下:
這篇文章參考自《復(fù)雜性思考》一書的第二章,并給出這一章節(jié)里我的習題解答。
(這書不到120頁紙,要賣50塊?。?,一開始以為很厚的樣子,拿回來一看,尼瑪。。。。。代碼很少,給點提示,然后讓讀者自己思考怎么實現(xiàn))
先定義頂點和邊
class Vertex(object): def __init__(self, label=''): self.label = label def __repr__(self): return 'Vertex(%s)' % repr(self.label) # __repr__返回表達式, __str__返回可閱讀信息 __str__=__repr__ # 使其指向同一個函數(shù) class Edge(tuple): # 繼承自建tuple類型并重寫new方法 def __new__(cls, e1, e2): return tuple.__new__(cls, (e1,e2)) def __repr__(self): return "Edge(%s, %s)" % (repr(self[0]), repr(self[1])) __str__ = __repr__
創(chuàng)建頂點和邊的方法如下
if __name__=="__main__":
# 創(chuàng)建兩個頂點一條邊
v = Vertex('v')
w = Vertex('w')
e = Edge(v,w)
# print e
# 將頂點和邊放入圖中
g = Graph([v,w],[e])
# print g
創(chuàng)建一個基本的圖類:
# 通過字典的字典實現(xiàn)圖的結(jié)構(gòu)
class Graph(dict):
def __init__(self, vs=[], es=[]):
""" 建立一個新的圖,(vs)為頂點vertices列表,(es)為邊緣edges列表 """
for v in vs:
self.add_vertex(v)
for e in es:
self.add_edge(e)
def add_vertex(self,v):
""" 添加頂點 v: 使用字典結(jié)構(gòu)"""
self[v] = {}
def add_edge(self, e):
""" 添加邊緣 e: e 為一個元組(v,w)
在兩個頂點 w 和 v 之間添加成員e ,如果兩個頂點之間已有邊緣,則替換之 """
v, w = e
# 由于一條邊會產(chǎn)生兩個項目,因此該實現(xiàn)代表了一個無向圖
self[v][w] = e
self[w][v] = e
練習2-2解答:圖的一些基本操作
def get_edge(self,v1, v2):
""" 接收兩個頂點,若這兩個頂點之間右邊則返回這條邊,否則返回None """
try:
return self[v1][v2]
except:
return None
def remove_edge(self,e):
""" 接受一條邊,并且刪除圖中該邊的所有引用 """
v, w = e
self[v].pop(w)
self[w].pop(v)
def vertices(self):
""" 返回圖中所有頂點的列表 """
return self.keys()
def edges(self):
""" 返回圖中邊的列表 """
es = set() # 為了避免返回重復(fù)的邊,設(shè)為集合
for v1 in self.vertices():
for v2 in self.vertices():
es.add(self.get_edge(v2, v1))
es.discard(None) # 若集合中存在None元素,則刪除
return list(es)
""" 利用圖的字典結(jié)構(gòu)獲得所有邊
es = []
for v in self.vertices():
es.extend(self[v].values())
es = list(set(es))
return es
"""
def out_vertices(self,v):
""" 接受一個Vertex并返回鄰近頂點(通過一條邊連接到給定節(jié)點的節(jié)點)的列表 """
return self[v].keys()
def out_edges(self,v):
""" 接受一個Vertex并返回連接到給定節(jié)點的邊的列表 """
return self[v].values()
def add_all_edges(self,vs=None):
""" 從一個無邊的圖開始,通過在各個頂點間添加邊來生成一個完全圖
輸入為目標頂點的列表,如果為None,則對所有的點進行全聯(lián)結(jié) """
if vs == None:
vs = self.vertices()
for v1 in vs:
for v2 in vs:
if v1 is v2 : continue # 假設(shè)不存在單頂點連通
self.add_edge(Edge(v1,v2))
習題2-3 生成正則圖
正則圖是指圖中每個頂點的度相同,生成正則圖需要頂點數(shù)和度數(shù)滿足一定條件,具體算法見注釋:
def add_regular_edges(self,k):
""" 從一個無邊的圖開始不斷添加邊,使得每個頂點都有相同的度k
一個節(jié)點的度指的是連接到它的邊的數(shù)量 """
n = len(self.vertices())
assert n > 1
if k==1:
vs = self.vertices()
for i in range(n-1):
self.add_edge(Edge(vs[i],vs[i+1]))
return True
if n < k+1:
print "Cannot create regular graph"
return False
if n == k+1:
self.add_all_edges()
return True
"""
設(shè)度數(shù)為k,圖的階數(shù)(頂點個數(shù))為n
利用歸納方法生成邊的個數(shù)
偶數(shù)度 當k=2m,m>=1時
遞歸過程:
0. 假設(shè)n>k+1,因為當n=k+1時,只要生成全連接即可,當n<k+1,則不能生成正則圖
1. 當n>k+1時:先從原圖中前k+1個頂點(v1,v2,...,v2m-1,v2m, v2m+1)生成完全圖
此時,該k+1個頂點的度數(shù)均為k
2. 現(xiàn)添加一個頂點vx,x=2m+2該頂點的度為0
3. 刪除m條不相連的邊,如(v1,v2),(v3,v4),(v5,v6),...,(v2m-1,v2m),這時頂點v1,v2,...v2m的度為k-1
記錄下這m條邊的頂點
4. 聯(lián)結(jié) (v1,vx),(v2,vx),...,(v2m-1,vx),(v2m,vx),使得v1,v2,...,v2m,v2m+2的度=k
5. 對新加入的點,重復(fù)3,4
奇數(shù)度 當k=2m+1,m>=1時
遞歸過程:
設(shè)圖G是有n個頂點的k正則圖,且k=2m+1,m>=1,按照下面法則生成新圖G1
0. 假設(shè)n>k+1,因為當n=k+1時,只要生成全連接即可,當n<k+1,則不能生成正則圖
1. 在圖G中任取m條頂點不同的邊(x1,x2),(x3,x4),(x5,x6),...,(x2m-1,x2m) 記為組es1
再另取m條頂點不同的邊 (y1,y2),(y3,y4),(y5,y6),...,(y2m-1,y2m) 記為組es2
其中xi和yj可以存在相同,但是兩組中的所有邊都不相同
此時,該k+1個頂點的度數(shù)均為k
2. 在圖G中去掉m條邊(x1,x2),(x3,x4),(x5,x6),...,(x2m-1,x2m),增加新的頂點v1,并增加2m條新邊
(v1,x1),(v1,x2),...,(v1,x2m-1),(v1,x2m)
3. 在圖G中去掉m條邊(y1,y2),(y3,y4),(y5,y6),...,(y2m-1,y2m),增加新的頂點v2,并增加2m條新邊
(v2,y1),(v2,y2),...,(v2,y2m-1),(v2,y2m)
4. 增加新邊 (v1,v2)
5. 對新的點v3,v4,重復(fù)1,2,3,4
增加的頂點和邊保證了v1,v2和x1,x2,...,x2m,y1,y2,...,y2m的度數(shù)為2m+1其余頂點度數(shù)不變
"""
if k%2==0:
# 選取前k+1個點,先構(gòu)造完全圖
vs = self.vertices()
self.add_all_edges(vs[:k+1])
for i in range(k+1,n): # 對之后的點進行遍歷
vsdel = [] # 記錄刪除過邊的頂點
for e in self.edges():
# 獲得邊的兩個頂點
v1,v2 = e[0],e[1]
if v1 not in vsdel and v2 not in vsdel:
vsdel.append(v1)
vsdel.append(v2)
# 刪除不相連的邊
self.remove_edge(e)
# 當已刪除的邊數(shù)為k/2,即共k個非鄰近點時,退出循環(huán)
if len(vsdel)==k:
break
# 將新的點與記錄的點進行連接
for v in vsdel:
self.add_edge(Edge(v,vs[i]))
else:
if n%2==0 and n>k+1: # 由上述法則可知,n必須為偶數(shù)
# 選取前k+1個偶數(shù)點,先構(gòu)造完全圖
vs = self.vertices()
self.add_all_edges(vs[:k+1])
for i in range(k+1,n,2): # 之后的點進行兩兩遍歷
vsdel1 = [] # 記錄第1組刪除的點
edel1 = [] # 記錄第1組刪除的邊
for e in self.edges():
# 獲得邊的兩個頂點
v1,v2 = e[0],e[1]
if v1 not in vsdel1 and v2 not in vsdel1:
vsdel1.append(v1)
vsdel1.append(v2)
# 刪除不相連的邊
edel1.append(e)
self.remove_edge(e)
# 當已刪除的邊數(shù)為m,即共k-1個非鄰近點時,退出循環(huán)
if len(vsdel1)==k-1:
break
vsdel2 = [] # 記錄第2組刪除的點
edel2 = [] # 記錄第2組刪除的邊
for e in self.edges():
# 獲得邊的兩個頂點
v1,v2 = e[0],e[1]
# 點可以和第一組相同,但邊不可以
if v1 not in vsdel2 and v2 not in vsdel2 and e not in edel1:
vsdel2.append(v1)
vsdel2.append(v2)
# 刪除不相連的邊
edel2.append(e)
self.remove_edge(e)
# 當已刪除的邊數(shù)為m,即共k-1個非鄰近點時,退出循環(huán)
if len(vsdel2)==k-1:
break
# 分別連接兩組邊
for v in vsdel1:
self.add_edge(Edge(v,vs[i]))
for v in vsdel2:
self.add_edge(Edge(v,vs[i+1]))
self.add_edge(Edge(vs[i],vs[i+1]))
else:
print "Cannot create regular graph"
return False
return True
習題2-4:判斷一個圖是否連通,可以用BFS實現(xiàn):
def is_connect(self):
""" 判斷一個圖是否連通的
從任意頂點開始進行一次BFS,將所有到達的節(jié)點都標記上,然后檢查是否所有的節(jié)點都被標記上 """
pass
vs = self.vertices() # 獲得所有頂點
q, s = [], set() # 搜索隊列,標記集合
q.append(vs[0]) # 從第1個頂點開始搜索
while q: # 當隊列非空
v = q.pop(0) # 從隊列中刪除移一個頂點
s.add(v) # 并標記當前頂點
# 搜索當前頂點的連接點,如果這些連接點沒有被標記
# 則將其添加到隊列中
for w in self.out_vertices(v):
if w not in s:
q.append(w)
# 當隊列為空時完成搜索,檢查標記過的頂點是否等于圖的頂點數(shù)
if len(s)==len(vs):
return True
else:
return False
測試代碼:需要用到作者書中網(wǎng)頁提供的GraphWorld.py實現(xiàn)可視化功能
from GraphWorld import CircleLayout,GraphWorld from Graph import Graph,Vertex,Edge import string def test(n,k): # create n Vertices labels = string.ascii_lowercase + string.ascii_uppercase vs = [Vertex(c) for c in labels[:n]] # create a graph and a layout g = Graph(vs) g.add_regular_edges(k) layout = CircleLayout(g) # draw the graph gw = GraphWorld() gw.show_graph(g, layout) gw.mainloop() if __name__ == '__main__': test(n=10,k=3)
以下為生成10個結(jié)點,度為3的正則圖:

生成隨機圖,繼承上面的Graph類:
from Graph import Graph,Vertex,Edge
from random import randint
class RandomGraph(Graph):
""" 隨即圖 """
def add_random_edges(self,p):
""" 從一個·無邊圖開始隨機生成邊
使得任意兩個節(jié)點間存在邊的概率為p (0<=p<=1) """
for v1 in self.vertices():
for v2 in self.vertices():
if v1 is v2: continue
if randint(0,100) < p*100 :
self.add_edge(Edge(v1,v2))
測試一下:
from GraphWorld import CircleLayout,GraphWorld import string def test(n,p): # create n Vertices labels = string.ascii_lowercase + string.ascii_uppercase vs = [Vertex(c) for c in labels[:n]] # create a graph and a layout g = RandomGraph(vs) g.add_random_edges(p) print "connect?:",g.is_connect() layout = CircleLayout(g) # draw the graph gw = GraphWorld() gw.show_graph(g, layout) gw.mainloop() if __name__ == '__main__': test(p=0.2,n=5)

迭代器部分代碼:
# 迭代器
class AllTrue(object):
def next(self):
return True
def __iter__(self):
return self
# 使用AllTrue之類的迭代器可以表現(xiàn)無限序列
print zip('abc',AllTrue())
# 通過編寫生成器函數(shù)創(chuàng)建一個迭代器
def generate_letters():
for letter in 'abc':
yield letter
iter = generate_letters()
import string
# 帶有無限循環(huán)的生成器會返回一個不會終止的迭代器
def alphabet_cycle():
while True:
for i in range(1,10):
for c in string.lowercase:
yield c+str(i)
iter_ac = alphabet_cycle()
print iter_ac.next()
更多關(guān)于Python相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python加密解密算法與技巧總結(jié)》、《Python編碼操作技巧總結(jié)》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》及《Python入門與進階經(jīng)典教程》
希望本文所述對大家Python程序設(shè)計有所幫助。
- Python數(shù)據(jù)結(jié)構(gòu)與算法之圖的廣度優(yōu)先與深度優(yōu)先搜索算法示例
- python數(shù)據(jù)結(jié)構(gòu)之圖深度優(yōu)先和廣度優(yōu)先實例詳解
- python深度優(yōu)先搜索和廣度優(yōu)先搜索
- python 遞歸深度優(yōu)先搜索與廣度優(yōu)先搜索算法模擬實現(xiàn)
- Python數(shù)據(jù)結(jié)構(gòu)與算法之圖結(jié)構(gòu)(Graph)實例分析
- Python數(shù)據(jù)結(jié)構(gòu)與算法之圖的最短路徑(Dijkstra算法)完整實例
- Python算法之圖的遍歷
- python圖的深度優(yōu)先和廣度優(yōu)先算法實例分析
相關(guān)文章
Python Django給admin添加Action的方法實例詳解
這篇文章主要介紹了Django給admin添加Action的方法,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-04-04
python django下載大的csv文件實現(xiàn)方法分析
這篇文章主要介紹了python django下載大的csv文件實現(xiàn)方法,結(jié)合實例形式分析了Django框架下載csv大文件的相關(guān)操作技巧與注意事項,需要的朋友可以參考下2019-07-07
Python使用openpyxl實現(xiàn)Excel超鏈接批量化設(shè)置
在Excel中,超鏈接是一種非常有用的功能,本文我們將介紹如何使用Python來處理Excel中的超鏈接,以及如何將超鏈接與對應(yīng)的工作表鏈接起來,需要的可以參考一下2023-07-07
pandas實現(xiàn)數(shù)據(jù)可視化的示例代碼
本文主要介紹了pandas實現(xiàn)數(shù)據(jù)可視化的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-05-05
Python網(wǎng)絡(luò)編程之ZeroMQ知識總結(jié)
這篇文章主要介紹了Python網(wǎng)絡(luò)編程之ZeroMQ知識總結(jié),文中有非常詳細的代碼示例,對正在學習python的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04

