python数据结构与算法——图的基本实现及迭代器
本文参考自《复杂性思考》一书的第二章,并给出这一章节里我的习题解答。
(这书不到120页纸,要卖50块!!,一开始以为很厚的样子,拿回来一看,尼玛。。。。。代码很少,给点提示,然后让读者自己思考怎么实现)
先定义顶点和边
class Vertex(object):
def __init__(self, label=''):
self.label = label
def __repr__(self):
return 'Vertex(%s)' % repr(self.label)
# __repr__返回表达式, __str__返回可阅读信息
__str__=__repr__ # 使其指向同一个函数 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__
创建顶点和编的方法如下
if __name__=="__main__":
# 创建两个顶点一条边
v = Vertex('v')
w = Vertex('w')
e = Edge(v,w)
# print e
# 将顶点和边放入图中
g = Graph([v,w],[e])
# print g
创建一个基本的图类:
# 通过字典的字典实现图的结构
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: 使用字典结构"""
self[v] = {} def add_edge(self, e):
""" 添加边缘 e: e 为一个元组(v,w)
在两个顶点 w 和 v 之间添加成员e ,如果两个顶点之间已有边缘,则替换之 """
v, w = e
# 由于一条边会产生两个项目,因此该实现代表了一个无向图
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() # 为了避免返回重复的边,设为集合
for v1 in self.vertices():
for v2 in self.vertices():
es.add(self.get_edge(v2, v1))
es.discard(None) # 若集合中存在None元素,则删除
return list(es)
""" 利用图的字典结构获得所有边
es = []
for v in self.vertices():
es.extend(self[v].values())
es = list(set(es))
return es
""" def out_vertices(self,v):
""" 接受一个Vertex并返回邻近顶点(通过一条边连接到给定节点的节点)的列表 """
return self[v].keys() def out_edges(self,v):
""" 接受一个Vertex并返回连接到给定节点的边的列表 """
return self[v].values() def add_all_edges(self,vs=None):
""" 从一个无边的图开始,通过在各个顶点间添加边来生成一个完全图
输入为目标顶点的列表,如果为None,则对所有的点进行全联结 """
if vs == None:
vs = self.vertices()
for v1 in vs:
for v2 in vs:
if v1 is v2 : continue # 假设不存在单顶点连通
self.add_edge(Edge(v1,v2))
习题2-3 生成正则图
正则图是指图中每个顶点的度相同,生成正则图需要顶点数和度数满足一定条件,具体算法见注释:
def add_regular_edges(self,k):
""" 从一个无边的图开始不断添加边,使得每个顶点都有相同的度k
一个节点的度指的是连接到它的边的数量 """
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
"""
设度数为k,图的阶数(顶点个数)为n
利用归纳方法生成边的个数
偶数度 当k=2m,m>=1时
递归过程:
0. 假设n>k+1,因为当n=k+1时,只要生成全连接即可,当n<k+1,则不能生成正则图
1. 当n>k+1时:先从原图中前k+1个顶点(v1,v2,...,v2m-1,v2m, v2m+1)生成完全图
此时,该k+1个顶点的度数均为k
2. 现添加一个顶点vx,x=2m+2该顶点的度为0
3. 删除m条不相连的边,如(v1,v2),(v3,v4),(v5,v6),...,(v2m-1,v2m),这时顶点v1,v2,...v2m的度为k-1
记录下这m条边的顶点
4. 联结 (v1,vx),(v2,vx),...,(v2m-1,vx),(v2m,vx),使得v1,v2,...,v2m,v2m+2的度=k
5. 对新加入的点,重复3,4 奇数度 当k=2m+1,m>=1时
递归过程:
设图G是有n个顶点的k正则图,且k=2m+1,m>=1,按照下面法则生成新图G1
0. 假设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个顶点的度数均为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,重复1,2,3,4
增加的顶点和边保证了v1,v2和x1,x2,...,x2m,y1,y2,...,y2m的度数为2m+1其余顶点度数不变
"""
if k%2==0:
# 选取前k+1个点,先构造完全图
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)
# 当已删除的边数为k/2,即共k个非邻近点时,退出循环
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必须为偶数
# 选取前k+1个偶数点,先构造完全图
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)
# 当已删除的边数为m,即共k-1个非邻近点时,退出循环
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)
# 当已删除的边数为m,即共k-1个非邻近点时,退出循环
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实现:
def is_connect(self):
""" 判断一个图是否连通的
从任意顶点开始进行一次BFS,将所有到达的节点都标记上,然后检查是否所有的节点都被标记上 """
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)
# 当队列为空时完成搜索,检查标记过的顶点是否等于图的顶点数
if len(s)==len(vs):
return True
else:
return False
测试代码:需要用到作者书中网页提供的GraphWorld.py实现可视化功能
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个结点,度为3的正则图:

生成随机图,继承上面的Graph类:
from Graph import Graph,Vertex,Edge
from random import randint class RandomGraph(Graph):
""" 随即图 """
def add_random_edges(self,p):
""" 从一个·无边图开始随机生成边
使得任意两个节点间存在边的概率为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之类的迭代器可以表现无限序列
print zip('abc',AllTrue()) # 通过编写生成器函数创建一个迭代器
def generate_letters():
for letter in 'abc':
yield letter iter = generate_letters() import string
# 带有无限循环的生成器会返回一个不会终止的迭代器
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()
python数据结构与算法——图的基本实现及迭代器的更多相关文章
- python数据结构与算法——图的最短路径(Floyd-Warshall算法)
使用Floyd-Warshall算法 求图两点之间的最短路径 不允许有负权边,时间复杂度高,思路简单 # 城市地图(字典的字典) # 字典的第1个键为起点城市,第2个键为目标城市其键值为两个城市间的直 ...
- python数据结构与算法——图的最短路径(Dijkstra算法)
# Dijkstra算法——通过边实现松弛 # 指定一个点到其他各顶点的路径——单源最短路径 # 初始化图参数 G = {1:{1:0, 2:1, 3:12}, 2:{2:0, 3:9, 4:3}, ...
- python数据结构与算法——图的最短路径(Bellman-Ford算法)解决负权边
# Bellman-Ford核心算法 # 对于一个包含n个顶点,m条边的图, 计算源点到任意点的最短距离 # 循环n-1轮,每轮对m条边进行一次松弛操作 # 定理: # 在一个含有n个顶点的图中,任意 ...
- python数据结构与算法——图的广度优先和深度优先的算法
根据维基百科的伪代码实现: 广度优先BFS: 使用队列,集合 标记初始结点已被发现,放入队列 每次循环从队列弹出一个结点 将该节点的所有相连结点放入队列,并标记已被发现 通过队列,将迷宫路口所有的门打 ...
- python数据结构与算法
最近忙着准备各种笔试的东西,主要看什么数据结构啊,算法啦,balahbalah啊,以前一直就没看过这些,就挑了本简单的<啊哈算法>入门,不过里面的数据结构和算法都是用C语言写的,而自己对p ...
- Python数据结构与算法之图的最短路径(Dijkstra算法)完整实例
本文实例讲述了Python数据结构与算法之图的最短路径(Dijkstra算法).分享给大家供大家参考,具体如下: # coding:utf-8 # Dijkstra算法--通过边实现松弛 # 指定一个 ...
- Python数据结构与算法之图的广度优先与深度优先搜索算法示例
本文实例讲述了Python数据结构与算法之图的广度优先与深度优先搜索算法.分享给大家供大家参考,具体如下: 根据维基百科的伪代码实现: 广度优先BFS: 使用队列,集合 标记初始结点已被发现,放入队列 ...
- Python数据结构与算法--List和Dictionaries
Lists 当实现 list 的数据结构的时候Python 的设计者有很多的选择. 每一个选择都有可能影响着 list 操作执行的快慢. 当然他们也试图优化一些不常见的操作. 但是当权衡的时候,它们还 ...
- Python数据结构与算法--算法分析
在计算机科学中,算法分析(Analysis of algorithm)是分析执行一个给定算法需要消耗的计算资源数量(例如计算时间,存储器使用等)的过程.算法的效率或复杂度在理论上表示为一个函数.其定义 ...
随机推荐
- 配置rt-thread开发环境(配置系统,生成系统镜像)
配置rt-thread开发环境 ===========Python============= 1.Python的下载地址:http://www.python.org/ftp/python/ 链接中有各 ...
- Java 中的 static 使用之静态方法
与静态变量一样,我们也可以使用 static 修饰方法,称为静态方法或类方法.其实之前我们一直写的 main 方法就是静态方法.静态方法的使用如: 运行结果: 需要注意: 1. 静态方法中可以直接调用 ...
- C# 使用lock关键字lock不同的对象
c# lock关键字的本质 是调用Monitor.Enter(object obj)并且在finally的时候调用Monitor.Exit(obj) 在obj是不同数据类型的时候会出现不同的情况 1. ...
- java jar包解析:打包文件,引入文件
java jar包解析:打包文件,引入文件 cmd下: jar命令:package包打包 javac命令:普通类文件打包 Hello.java: package org.lxh.demo; publi ...
- 使用File类递归列出E盘下全部文件
import java.io.File;public class FileListTest { public void tree(File file){ if(file.listFiles()!=nu ...
- GitHub 上有哪些完整的 iOS-App 源码值得参考
作者:wjh2005链接:https://www.zhihu.com/question/28518265/answer/88750562来源:知乎著作权归作者所有,转载请联系作者获得授权. 1. Co ...
- 樱花漫地集于我心,蝶舞纷飞祈愿相随---总结 顕出:void-sampling 显示:void-sampling
知识点: 分支语句,四则运算,( ̄y▽ ̄)~* 实验过程中遇到的问题及解决方法: 忘记换行,忘记代码,输入法切换(´∀`*) 暂时还得照书写,*★,°*:.☆\( ̄▽ ̄)/$:*.°★* 实验心得体会 ...
- iOS开发拓展篇—音频处理(音乐播放器6)
iOS开发拓展篇—音频处理(音乐播放器6) 一.图片处理 说明: Aspect表示按照原来的宽高比进行缩放. Aspectfit表示按照原来的宽高比缩放,要求看到全部图片,后果是不能完全覆盖窗口,会留 ...
- Discuz升级提示static/image/postbg/3.jpg下载出现问题的解决办法
discuz2.5升级3.0的时候出现错误.提示static/image/postbg/3.jpg下载出现问题,其解决办法如下 找到 source/admincp/admincp_upgrade.ph ...
- WaitForSingleObject 和 WaitForMultipleObjects函数
1.WaitForSingleObject 等待函数可使线程自愿进入等待状态,直到一个特定的内核对象变为已通知状态为止.这些等待函数中最常用的是WaitForSingleObject: DWORD ...