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

生成隨機(jī)圖,繼承上面的Graph類:
from Graph import Graph,Vertex,Edge
from random import randint
class RandomGraph(Graph):
""" 隨即圖 """
def add_random_edges(self,p):
""" 從一個(gè)·無(wú)邊圖開(kāi)始隨機(jī)生成邊
使得任意兩個(gè)節(jié)點(diǎn)間存在邊的概率為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))
測(cè)試一下:
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)無(wú)限序列
print zip('abc',AllTrue())
# 通過(guò)編寫(xiě)生成器函數(shù)創(chuàng)建一個(gè)迭代器
def generate_letters():
for letter in 'abc':
yield letter
iter = generate_letters()
import string
# 帶有無(wú)限循環(huán)的生成器會(huì)返回一個(gè)不會(huì)終止的迭代器
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入門(mén)與進(jìn)階經(jīng)典教程》
希望本文所述對(duì)大家Python程序設(shè)計(jì)有所幫助。
- Python數(shù)據(jù)結(jié)構(gòu)與算法之圖的廣度優(yōu)先與深度優(yōu)先搜索算法示例
- python數(shù)據(jù)結(jié)構(gòu)之圖深度優(yōu)先和廣度優(yōu)先實(shí)例詳解
- python深度優(yōu)先搜索和廣度優(yōu)先搜索
- python 遞歸深度優(yōu)先搜索與廣度優(yōu)先搜索算法模擬實(shí)現(xiàn)
- Python數(shù)據(jù)結(jié)構(gòu)與算法之圖結(jié)構(gòu)(Graph)實(shí)例分析
- Python數(shù)據(jù)結(jié)構(gòu)與算法之圖的最短路徑(Dijkstra算法)完整實(shí)例
- Python算法之圖的遍歷
- python圖的深度優(yōu)先和廣度優(yōu)先算法實(shí)例分析
相關(guān)文章
Python Django給admin添加Action的方法實(shí)例詳解
這篇文章主要介紹了Django給admin添加Action的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-04-04
python django下載大的csv文件實(shí)現(xiàn)方法分析
這篇文章主要介紹了python django下載大的csv文件實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了Django框架下載csv大文件的相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下2019-07-07
Python+ChatGPT制作一個(gè)AI實(shí)用百寶箱
ChatGPT最近在互聯(lián)網(wǎng)掀起了一陣熱潮,其高度智能化的功能能夠給我們現(xiàn)實(shí)生活帶來(lái)諸多的便利。本文就來(lái)用Python和ChatGPT制作一個(gè)AI實(shí)用百寶箱吧2023-02-02
Python開(kāi)發(fā)文字版密室逃脫游戲的實(shí)例(含代碼)
密室逃脫游戲是一種頗受歡迎的解謎類游戲,玩家通常需要通過(guò)觀察、推理、合作等方式解決一系列難題,以逃脫困境,在這篇博文中,我們將使用Python開(kāi)發(fā)一個(gè)文字版密室逃脫游戲,旨在通過(guò)簡(jiǎn)單的文本交互來(lái)呈現(xiàn)游戲的趣味性與挑戰(zhàn)性2025-04-04
Python使用openpyxl實(shí)現(xiàn)Excel超鏈接批量化設(shè)置
在Excel中,超鏈接是一種非常有用的功能,本文我們將介紹如何使用Python來(lái)處理Excel中的超鏈接,以及如何將超鏈接與對(duì)應(yīng)的工作表鏈接起來(lái),需要的可以參考一下2023-07-07
pandas實(shí)現(xiàn)數(shù)據(jù)可視化的示例代碼
本文主要介紹了pandas實(shí)現(xiàn)數(shù)據(jù)可視化的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
Python網(wǎng)絡(luò)編程之ZeroMQ知識(shí)總結(jié)
這篇文章主要介紹了Python網(wǎng)絡(luò)編程之ZeroMQ知識(shí)總結(jié),文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)python的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04

