代码来自《数据结构与算法Python语言实现》裘宗燕著
# 图的生成树
#----------图的邻接矩阵实现--------------------
class Graph:
def __init__(self,mat,unconn = 0):
# 顶点个数
vnum = len(mat)
# 检查是否为方阵
for x in mat:
if len(x) != vnum:
raise ValueError("Argument for 'Graph'.")
# 拷贝
self._mat = [mat[i][:] for i in range(vnum)]
self._unconn = unconn
self._vnum = vnum
# 返回顶点个数
def vertex_num(self):
return self._vnum
# 验证序号v是否有效
def _invalid(self,v):
return 0>v or v>=self._vnum
# 添加顶点
def add_vertex(self):
raise ValueError("Adj-Matrix does not support 'add_vertex'.")
# 添加边,即添加两个点之间的关联线段
def add_edge(self,vi,vj,val=1):
# 先检查vi和vj是否有效
if self._invalid(vi) or self._invalid(vj):
raise ValueError(str(vi) + 'or' + str(vj) + 'is not a valid vertex.')
# 为线段附加权重
self._mat[vi][vj] = val
# 获取边的权重
def get_edge(self,vi,vj):
if self._invalid(vi) or self._invalid(vj):
raise ValueError(str(vi) + 'or' + str(vj) + 'is not valid vertex.')
return self._mat[vi][vj]
# 获取指定结点的所有相连边
def out_edges(self,vi):
if self._invalid(vi):
raise ValueError(str(vi) + 'is not a valid vertex.')
return self._out_edges(self._mat[vi],self._unconn)
@staticmethod # 静态方法,无需实例化即可调用
def _out_edges(row,unconn):
edges = []
for i in range(len(row)):
if row[i] != unconn:
edges.append((i,row[i]))
return edges
# 将矩阵转化为字符串模式
def __str__(self):
return "[\n" + ".\n".join(map(str,self._mat)) + "\n]" + "\nUnconnected:" + str(self._unconn)
#----------图的邻接矩阵实现--------------------
#----------图的压缩邻接矩阵实现-----------------
class GraphAL(Graph):
# GraphAL继承自Graph
def __init__(self,mat = [],unconn = 0):
vnum = len(mat)
# 检查是否为方阵
for x in mat:
if len(x) != vnum:
raise ValueError("Argument for 'Graph'.")
# 只复制带有权重的边
self._mat = [Graph._out_edges(mat[i],unconn) for i in range(vnum)]
self._vnum = vnum
self._unconn = unconn
# 添加一个顶点
def add_vertex(self):
self._mat.append([])
self._vnum += 1
return self._vnum - 1
# 添加一个边
def add_edge(self,vi,vj,val=1):
if self._vnum == 0:
raise ValueError("Cannot add edge to an empty Graph!")
if self._invalid(vi) or self._invalid(vj):
raise ValueError(str(vi) + 'or' + str(vj) + 'is not valid vertex!')
row = self._mat[vi]
i = 0
while i < len(row):
# 寻找vj对应的权重
if row[i][0] == vj:
self._mat[vi][i] = [vj,val]
return None
# 如果没有找到vj对应的值,说明vi和vj不相连
# 则退出循环,添加vi与vj的关联边
if row[i][0] > vj:
break
i += 1
# 添加vi与vj的关联边
self._mat[vi].insert(i,(vj,val))
def get_edge(self,vi,vj):
if self._invalid(vi) or self._invalid(vj):
raise ValueError(str(vi) + 'or' + str(vj) + 'is not valid vertex!')
# 寻找对应vi和vj的边的权重
if i,val in self._mat[vi]:
if i == vj:
return val
# 如果没有匹配到,则返回0
return self._unconn
#----------图的压缩邻接矩阵实现-----------------
#----------栈的实现---------------------------
class Node:
def __init__(self):
self.val = val
self.next = None
class SStack:
def __init__(self):
self.top = None
# 获取栈顶的元素
def peek(self):
if self.top != None:
return self.top.val
else:
return None
# 将新元素压入到栈中
def push(self,n):
n = Node(n)
n.next = self.top
self.top = n
return n.val
# 退出栈
def pop(self):
if self.top == None:
return None
else:
tmp = self.top.val
self.top = self.top.next
return tmp
#----------栈的实现---------------------------
#----------1. 非递归DFS遍历--------------------
def DFS_graph(graph,v0):
# 顶点个数
vnum = graph.vertex_num()
# 为每个顶点打上未访问的标记
visited = [0] * vnum
# 遍历序列
DFS_seq = [v0]
# 栈、用于存储每个顶点的边信息
st = SStack()
st.push((0,graph.out_edges(v0))) # 压入与起始结点相连的边中的第一条
while not st.is_empty():
# 取出顶点序号i与边列表
i,edges = st.pop()
if i < len(edges):
# v是顶点,e是边
v,e = edges[i]
# 压入与该顶点相连的下一个边
# 待前一个边下面的所有结点都访问完了之后再访问
st.push((i+1,edges))
# 如果没有访问过这个顶点
if not visited[v]:
# 将顶点加入列表中
DFS_seq.append(v)
# 访问过之后将标记置为1
visited[v] = 1
# 将与搜索到的顶点相连的顶点压入栈中,以备下次访问,这个比163行的更先访问到
st.push((0,graph.out_edges(v)))
return DFS_seq
#----------1. 非递归DFS遍历--------------------
#---------2. DFS生成树-------------------------
def DFS_span_forest(graph):
# 顶点个数
vnum = graph.vertex_num()
# 记录路径信息
span_forest = [None] * vnum
def dfs(graph,v):
# 需要修改非局部变量,所以将之声明为nonlocal
nonlocal span_forest
# 遍历顶点v连接的所有路径,u是顶点,w是权重
for u,w in graph.out_edges(v):
# 如果这个顶点尚未加入span_forest
if span_forest[u] is None:
# 将结点u的上一结点和连接权重记录下来
span_forest[u] = (v,w)
dfs(graph,u)
# 避免还有不连通的点,所以要遍历到每个顶点
for v in range(vnum):
if span_forest[v] is None:
span_forest[v] = [v,0]
dfs(graph,v)
return span_forest
#---------2. DFS生成树-------------------------
#---------3. Kruskal最小生成树-----------------
def Kruskal(graph):
# 基本思路:设G = (V,E) 是一个网络,其中|V|(顶点数量)为n,则Kruskal的基本思路为:
# (1)取包含G的所有n个顶点但是没有任何边的孤立点子图T = (V,{})
# T中的每个顶点自称一个连通分量,下面将通过不断扩充T的方式构造G的最小生成树;
# (2)将边集E按照权值递增的顺序排序,在构造中的每一步按顺序地检查这个边序列
# 找到最短的且两端位于T的两个不同的连通分量的边e,将e加入T,这样两个连通分量
# 就在e的作用下连接成了一个连通分量;
# (3)每次操作使T减少一个连通分量。不断重复这个动作,往T中加入新边,直到T中所有顶点
# 都包含在一个连通分量为止,这个连通分量就是G的一棵最小生成树。
# 获取顶点个数
vnum = graph.vertex_num()
# 将各顶点的代表元初始化,代表元代表了顶点所属的连通分量
reps = [i for i in range(vnum)]
# mst记录最小生成树的边,edges记录graph所有的边
mst,edges = [],[]
# 将所有的边加入edges
for vi in range(vnum):
for v,w in graph.out_edges(vi):
edges.append((w,vi,v))
# 按权重排列
edges.sort()
for w,vi,vj in edges:
# 如果vi和vj属于两个不同的连通分量
if reps[vi] != reps[vj]:
mst.append(((vi,vj),w))
# 如果有n-1条边代表已经构造完成
if len(mst) == vnum - 1:
break
# 修改代表元,将所有与vj在同一连通分量的顶点归入vi所在的连通分量中
rep,orep = reps[vi],reps[vj]
for i in range(vnum):
if reps[i] == orep:
reps[i] = rep
return mst
#---------3. Kruskal最小生成树-----------------