Python数据结构与算法之图的基本实现及迭代器实例详解


Posted in Python onDecember 12, 2017

本文实例讲述了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的正则图:

Python数据结构与算法之图的基本实现及迭代器实例详解

生成随机图,继承上面的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)

Python数据结构与算法之图的基本实现及迭代器实例详解

迭代器部分代码:

# 迭代器
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 相关文章推荐
python中使用urllib2获取http请求状态码的代码例子
Jul 07 Python
Python3 适合初学者学习的银行账户登录系统实例
Aug 08 Python
python实现图片处理和特征提取详解
Nov 13 Python
Python爬虫之网页图片抓取的方法
Jul 16 Python
OpenCV 模板匹配
Jul 10 Python
解决Pytorch 加载训练好的模型 遇到的error问题
Jan 10 Python
Python文件操作基础流程解析
Mar 19 Python
opencv 图像腐蚀和图像膨胀的实现
Jul 07 Python
在django中查询获取数据,get, filter,all(),values()操作
Aug 09 Python
Python: glob匹配文件的操作
Dec 11 Python
Python datetime模块的使用示例
Feb 02 Python
基于Python实现流星雨效果的绘制
Mar 18 Python
Python数据结构与算法之图的最短路径(Dijkstra算法)完整实例
Dec 12 #Python
你真的了解Python的random模块吗?
Dec 12 #Python
Python判断两个对象相等的原理
Dec 12 #Python
浅谈Django REST Framework限速
Dec 12 #Python
Django admin美化插件suit使用示例
Dec 12 #Python
Django admin实现图书管理系统菜鸟级教程完整实例
Dec 12 #Python
基于Django filter中用contains和icontains的区别(详解)
Dec 12 #Python
You might like
php 用sock技术发送邮件的函数
2007/07/21 PHP
基于php上传图片重命名的6种解决方法的详细介绍
2013/04/28 PHP
PHP中大括号'{}'用法实例总结
2017/02/08 PHP
PHP实现的常规正则验证helper公共类完整实例
2017/04/27 PHP
简单实现php上传文件功能
2017/09/21 PHP
JQuery上传插件Uploadify使用详解及错误处理
2010/04/27 Javascript
SWFObject 2.1以上版本语法介绍
2010/07/10 Javascript
jquery png 透明解决方案(推荐)
2010/08/21 Javascript
jquery事件机制扩展插件 jquery鼠标右键事件。
2011/12/26 Javascript
基于javascript滚动图片具体实现
2013/11/18 Javascript
jquery选择器之基本过滤选择器详解
2014/01/27 Javascript
JavaScript获取Url里的参数
2014/12/18 Javascript
jQuery使用CSS()方法给指定元素同时设置多个样式
2015/03/26 Javascript
jQuery禁用快捷键例如禁用F5刷新 禁用右键菜单等的简单实现
2016/08/31 Javascript
浅谈jquery拼接字符串效率比较高的方法
2017/02/22 Javascript
详解vue项目构建与实战
2017/06/27 Javascript
ES6学习教程之对象字面量详解
2017/10/09 Javascript
详解JS浏览器事件循环机制
2019/03/27 Javascript
python封装对象实现时间效果
2020/04/23 Python
Python实现对百度云的文件上传(实例讲解)
2017/10/21 Python
Python数据分析库pandas基本操作方法
2018/04/08 Python
基于Python实现ComicReaper漫画自动爬取脚本过程解析
2019/11/11 Python
Pytest参数化parametrize使用代码实例
2020/02/22 Python
在pytorch中实现只让指定变量向后传播梯度
2020/02/29 Python
Python flask框架实现浏览器点击自定义跳转页面
2020/06/04 Python
python 实现性别识别
2020/11/21 Python
全球最大的生存食品、水和装备专用在线市场:BePrepared.com
2020/01/02 全球购物
企业党员个人自我评价
2014/09/20 职场文书
教师国庆节演讲稿范文2014
2014/09/21 职场文书
县政府班子个人对照检查材料
2014/10/05 职场文书
教师个人师德总结
2015/02/06 职场文书
温馨祝福晨语:美丽的一天从我的问候开始
2019/11/28 职场文书
python制作图形界面的2048游戏, 基于tkinter
2021/04/06 Python
golang协程池模拟实现群发邮件功能
2021/05/02 Golang
Go标准容器之Ring的使用说明
2021/05/05 Golang
Python实战之疫苗研发情况可视化
2021/05/18 Python