判断子图算法
def issubgraph(V,E,Vs,Es):
tv=(Vs <= V) and (Es <= E) # 点集是子集,边集是子集
return tv
判断真子图算法
def ispropersubgraph(V,E,Vs,Es):
# 首先要是一个子图,然后点集或者边集是真子集
tv=((Vs <= V) and (Es <= E)) and ((Vs < V) or (Es < E))
return tv
判断生成子图算法
def isspanningsubgraph(V,E,Vs,Es):
# 边集是子集,点集相同
tv=((Vs == V) and (Es <= E))
return tv
判断导出子图算法
def isinducedsubgraph(V,E,Vs,Es):
tv=((Vs <= V) and (Es <= E)) # 首先判断是否是一个子图
# 对于E中的边所关联的两个点,要么不是都在Vs中,如果都在Vs里,
# 这条边就要在Es中出现
for (u,v) in E:
tv=tv and ((not((u in Vs) and (v in Vs))) or ((u,v) in Es))
return tv
完全图构建算法
def completeset(n):
V=set({})
E=set({})
# 两两顶点之间创建边
for i in range(n):
V=V | {i}
for j in range(n):
if(i < j):
E=E | {(i,j)}
return (V,E)
圈图构建算法
def cyclicset(n):
V={0} # 取出一个顶点作为起始顶点
E=set({})
for k in range(1,n):
V=V | {k}
E=E | {(k-1,k)} # 每个顶点和下一个顶点连边
E=E | {(n-1,0)} # 补上圈的最后一条边
return (V,E)
轮图构建算法
def wheelgset(n):
V={0,1} # 0作为内部的轮心
E=set({})
# 整体思路和圈图的是一样的
# 只不过对于外面的圈上的每个顶点都增加一个和内部轮心
# 相连的边
for k in range(1,n):
V=V | {k}
E=E | {(0,k)}
E=E | {(k-1,k)}
E=E | {(n-1,1)}
return (V,E)
二部图构建算法
def completebipartitegraph(n,m):
V0=set({})
V1=set({})
# 添加V0中的n个节点
for k in range(n):
V0=V0 | {k}
E=set({})
# 添加V1中的m个节点
for k in range(n,n+m):
V1=V1 | {k}
# 对于V0和V1中的每两个节点之间创建边
for u in V0:
for v in V1:
E=E | {(u,v)}
return (V0,V1,E)
n立方体图构建算法
def ncubeset(m): # 在这里m是二进制数的位数,如m = 3
V=set({})
E=set({})
n=2**m # 总共的顶点数,n = 8
for i in range(n):
V=V | {i}
u=1 # u作为一个指标,n = 001, 010, 100
k=0 # k作为一个遍历,用来使n里面的1二进制位遍历m个位置
while(k < m):
# 如果i里面u中1二进制位所对应的位是0,则将该位变成1
# 如果i里面u中1二进制位所对应的位是1,则将该位变成0
# 通过遍历,就可以使得每个顶点相邻的m个顶点与其都
# 只有一个二进制位不同
if(i & u == 0):
j=i | u
else:
j=i - u
E=E | {(i,j)}
u=u*2
k=k+1
return (V,E)
0-1序列图构建算法
def sequence01set(m):
V=set({})
E=set({})
n=2**m
# 思路就是对于每个顶点,将其左移一位,然后在最低位填0或1
# 这样就构造了两条边,而且其所关联的两个顶点满足条件
for i in range(n):
V=V | {i}
j=(2*i)%n
E=E | {(i,j)}
j=(2*i + 1)%n
E=E | {(i,j)}
return (V,E)
通路算法
import datetime
def pathset0(V,E,u0):
# path1最后保存的是所有以u0为起始节点的边
path1=set({})
for (u,v) in E:
if(u == u0):
path1=path1 |{(u,v)}
return path1
def pathset(path0,E):
path=set({})
for p0 in path0:
y=p0[-1] # 取通路的最后一个顶点
for (u,v) in E:
if u==v: # 如果是自环,忽略
continue
p=p0
if (u==y): # 如果发现能将该通路扩充
p=p+tuple([v]) # 将新的节点添加到当前路径中
path=path |{p}
return path
def main(V,E,k):
global path
t0=datetime.datetime.now( )
path=pathset0(V,E,1)
for k in range(k): # k这个参数我认为是寻找长度为k的通路
path=pathset(path,E)
t1=datetime.datetime.now( )
t=t1-t0
return [t,len(path)]
基本通路算法
def basicpathset(path0,E):
path=set({})
# 总体的思路是和pathset()一样的
# 但是由于是基本通路,要保证每个顶点出现不超过1次
for pk in path0:
x=pk[-1]
for (u,v) in E:
p=pk
if (u==x and v not in pk):
p=p+tuple([v])
path=path |{p}
if(v==x and u not in pk): # 这里好像又考虑无向图了...
# 知道思路就好,如果自己写也不会写成这样
p=p+tuple([u])
path=path |{p}
return path
def basicpathnset(V,E,v0):
pathk=pathv0(V,E,v0) # 这里的pathv0,我真不知道是什么....
# 我在这里当作pathset0这个函数
m=len(V)-2
for k in range(m):
n=len(pathk) # 我不知道这个n他要干啥
pathk=basicpathset(pathk,E)
return pathk
连通图的构建
def connectedgraph(V,E):
for (u,v) in E: # 神奇操作
break
Vc=set({u,v})
Ec=set({(u,v)})
while E !=set({}):
n=len(Ec) # 和后面的len(Ec) == n相对应,用来判断是都本次循环
# 对Ec进行了修改,如果没有就说明已经构造完了,就break
for (u,v) in E:
# 这里就是不断地加点
if (u,v) not in Ec and (u in Vc or v in Vc):
Vc=Vc | {u,v}
Ec=Ec | {(u,v)}
if len(Ec) == n:
break
return [Vc,Ec]
连通图判断
def isconnectedgraph(V,E):
# 就是判断这个图是不是只有一个连通分支
# 所以先构造一个联通子图,然后看边集和E是不是相等
[Vc,Ec]=connectedgraph(V,E)
if set(Ec)==set(E):
tv=True
else:
tv=False
return tv
树的构建
def graph2tree(V,E):
for (u,v) in E: # 神奇操作
break
Vt={u,v}
Et={(u,v)}
n=len(V)
while(len(Et) < (n-1)): # m = n - 1
m=len(Vt) # 用来判断本次循环是否对Vt更改,如果没有,就直接返回
# 避圈加边
for (u,v) in E:
if((u in Vt and v not in Vt)
or (u not in Vt and v in Vt)):
Vt=Vt | {u} | {v}
Et=Et | {(u,v)}
E=E-Et # 将已经加过的边从原来E中去掉
if(m == len(Vt)):
break
return [Vt,Et]
树的判断
def istree(V,E):
for (u,v) in E:
Vt={u,v}
E=E-{(u,v)}
tv=True
while(E != set({})):
for (u,v) in E:
# 如果E中的边所关联的两个点在Vt中已经出现
# 则说明出现了圈,不为树
if u in Vt and v in Vt:
E=E-{(u,v)}
tv=False
break
# 从E中去掉边,将点添加到Vt中
if u in Vt and v not in Vt:
Vt=Vt | {u} | {v}
E=E-{(u,v)}
if u not in Vt and v in Vt:
Vt=Vt | {u} | {v}
E=E-{(u,v)}
if(tv == False):
break
# 判断是否连通
if(len(Vt) != len(V)):
tv=False
return tv
二叉树构建算法
def createbitree(nodes):
# []是树
if(len(nodes) == 0):
return [ ]
# [a]是树
if(len(nodes) == 1):
return [nodes[0]]
# 从当前结点集中任意挑出一个点作为父结点
k=random.randint(0,len(nodes)-1)
# 左儿子
Lnodes=nodes[0:k]
# 右儿子
Rnodes=nodes[k+1:]
a=nodes.pop(k)
# 递归构造
L=createbitree(Lnodes)
R=createbitree(Rnodes)
return [a,L,R]
前序遍历
# 树的形式[root, left, right]
# 和数据结构书上的差不多
# 中序和后序同理,在这里就不写了
def preordertraversal(tree):
if(len(tree)==0):
return []
if(len(tree)==1):
return [tree[0]]
else:
return [tree[0]]+preordertraversal(tree[1])+preordertraversal(tree[2])
霍夫曼编码
def main( ):
global W,tree,gtree
# W中的每个子列表第一项是权值,第二项是名字
W=[[0.08,'a'],[0.10,'b'],[0.12,'c'],[0.15,'d'],[0.20,'e'],[0.35,'f']]
# tree的形式是[[1, [...], [...]]],省略号表示形式是递归的
tree=Huffmantree(W)
# 将tree的形式变成[1, [...], [...]]
tree=tree[0]
gtree=Huffmantree2graph(tree)
drawgraph(gtree)
return gtree
def Huffmantree(W):
tree=sorted(W) # 首先对权值进行排序
while len(tree) != 1:
# 将权值最小的两个子树结合起来
# 权值相加,权值小的作为左子树,权值大的作为右子树
tree=[[tree[0][0]+tree[1][0],tree[0],tree[1]]]+tree[2:]
# 重新进行排序
tree=sorted(tree)
return tree
def Huffmantree2graph(tree):
gtree=set({})
if len(tree) == 2: # 如果是底层,例如(1.0, 0.38)这种形式,则直接返回
return gtree
L=Huffmantree2graph(tree[1]) # 递归左子树
R=Huffmantree2graph(tree[2]) # 递归右子树
a=math.floor(tree[0]*10000)/10000
l=math.floor(tree[1][0]*10000)/10000
r=math.floor(tree[2][0]*10000)/10000
# 将当前结点的左右子树和左右子树的递归给添加到gtree
gtree= {(a,l)} | {(a,r)}|L|R
return gtree
def huffmancoding(subtree,code):
# 和上一个函数思路是差不多的
if len(subtree) == 2:
return [[subtree[1],code]]
else:
node0=subtree[1]
node1=subtree[2]
# 如果是左子树,就加0,如果是右子树,就加1
huffmancode=huffmancoding(node0,code+'0')+huffmancoding(node1,code+'1')
return huffmancode
Prim和Kruskal以及Dijiskra和Floyd略
欧拉图构建方法
# 其实总体的思路就是树上欧拉图构建的证明
# 如果书上理解了,这里就会方便很多
def subEulerCircuit(v0,E):
# 这个函数起始就是从v0开始找圈
circuit=tuple([v0])
S=set({})
while (circuit[0]!=circuit[-1] or len(circuit) ==1):
y=circuit[-1]
for (u,v) in E:
if(u==y or v==y):
if (u==y):
circuit=circuit+tuple([v])
else:
circuit=circuit+tuple([u])
E=E-{(u,v)}
S=S|{(u,v)}
break
return [circuit,S]
def EulerCircuit(v0,E):
# 首先找到一个圈
[circuit,S]=subEulerCircuit(v0,E)
# 将E中在这个圈里的边去掉
E=E-S
while (E != set({})):
V1=set2V(E)
V2=set2V(S)
V1V2=V1&V2 # 找到E和S的公共点
for v0 in V1V2:
# 以公共点找圈
[subcircuit,S]=subEulerCircuit(v0,E)
k=circuit.index(v0)
# 将两个圈合成一个
circuit=circuit[0:k]+subcircuit+circuit[k+1:-1]+tuple([circuit[-1]])
E=E-S
break
return circuit
def set2V(E): # 将边集转换成顶点集
V=set({})
for (u,v) in E:
V=V|{u,v}
return V
哈密尔顿周游算法
# 整体算法ppt上写的很详细
# 在这里只是将算法拆分到具体的代码上
def tourpath0(V,E,path,m):
while(len(path) < m):
w=path[-1] # 取出路径最后一个点
i=V.index(w)
E1=E[i]
E2=E1[1] # 找到这个点的邻接点集
for u in E2: # 遍历邻接点集
if(u not in path): # 如果不在当前路径,就添加
path.append(u)
break
if(path[-1] == w): # 如果本次循环没有添加任何结点,就退出
break
return path
def tourpath1(V,E,path,m):
v=path.pop( ) # 取路径path的末尾顶点v=path.pop( )为v
while(len(path) != 0): # 若 path非空
u=path[-1] # 取路径path的末尾顶点path[-1]为u
i=V.index(u)
E1=E[i]
E2=E1[1]
k=E2.index(v) # 从顶点u的邻接表从顶点v以后依次检查k=E2.index(v)
while(k < (len(E2)-1)):
k=k+1
v=E2[k]
if(v not in path): # 若v=E2[k]不在path,则v为path的新末尾顶点,直至邻接表结束
path.append(v)
break
if(u != path[-1]): #若path有新顶点,则结束,否则,path弹出末尾顶点。
break
v=path.pop( )
return path
def tourpath(V,E,path,m):
if(len(path) == m): # 若len(path) == m,则求新的周游通路,即tourpath1(V,E,path,m)
path=tourpath1(V,E,path,m)
while(len(path) != 0): # 若len(path) != 0,依次迭代执行tourpath0与tourpath1
path=tourpath0(V,E,path,m)
if(len(path) == m):
break
path=tourpath1(V,E,path,m)
return path
# 这个是构建邻接表
def adjacentlist(V,E):
Ea=[]
for w in V:
e0=[w]
e1=[]
for (u,v) in E:
if u == w and (v not in e1):
e1=e1+[v]
if v==w and (u not in e1):
e1=e1+[u]
e0=e0+[sorted(e1)]
Ea=Ea+[e0]
return sorted(Ea)
关键路径算法
# di0中存的是入度为0的结点集
def stepu0v(di0,E):
S=set({})
for u0 in di0: # 对于每个入度为0的结点
# S添加E中所有以u0为起始结点的边集
for (w,u,v) in E:
if u == u0:
S=S|{(w,u,v)}
# 最后S中包含的是所有以di0中结点为起始结点的边集
return S
# do0中存的是出度为0的结点集
def stepuv0(do0,E):
S=set({})
for v0 in do0:
for (w,u,v) in E:
if v == v0:
S=S|{(w,u,v)}
# 最后S中包含的是所有以di0中结点为终止结点的边集
return S
def craticalTE(V,E,di,v0,vn):
Hx= [0]*len(V) # 初始化所有的最早完成时间都是0
di0={v0} # 以v0作为初始入度为0的结点
while vn not in di0:
S=stepu0v(di0,E) # 得到以di0中结点为起始结点的边集
[Hx,di,di0]=TEpath(S,Hx,di)
E=E-S
return Hx
# 与TE同理
def craticalTL(V,E,do,Hn,v0,vn):
Hy= [1000]*len(V)
Hy[len(V)-1]=Hn
do0={vn}
while v0 not in do0:
S=stepuv0(do0,E)
[Hy,do,do0]=TLpath(S,Hy,do)
E=E-S
return Hy
def TEpath(S,Hx,di):
# 对S中的每一条边,更新最早完成时间的值
di0=set({})
for (w,u,v) in S:
if Hx[v] < Hx[u]+w: # 更新
Hx[v]=Hx[u]+w
if di[v]>0: # 如果v的入度大于0,则减1
di[v]=di[v]-1
if di[v] == 0: # 将入度为0的点添加到di0中
di0=di0 | {v}
return [Hx,di,di0]
def TLpath(S,Hy,do):
do0=set({})
for (w,u,v) in S:
if Hy[u] > Hy[v]-w:
Hy[u]=Hy[v]-w
if do[u]>0:
do[u]=do[u]-1
if do[u] == 0:
do0=do0 | {u}
return [Hy,do,do0]
def craticalpath(V,E,di,do,v0,vn):
Hx=craticalTE(V,E,di,v0,vn)
Hn=Hx[len(V)-1]
Hy=craticalTL(V,E,do,Hn,v0,vn)
V=list(V)
N=len(V)
C=set({})
for k in range(N):
if Hx[k] == Hy[k]:
C=C|{V[k]}
W=set({})
for (w,u,v) in E:
if u in C and v in C:
W=W|{(u,v,w)}
return W
def main( ):
global V0,E0,V,E,path
V0={0,1,2,3,4,5} # 顶点集
E0={(0,1),(0,2),(0,3),(1,2),(1,4),(2,3),(2,5),(3,4),(3,5),(4,5)} # 边集
E={(1,0,1),(3,0,2),(5,0,3),(1,1,2),(2,1,4),(1,2,3),(2,2,5),(1,3,4),(3,3,5),(2,4,5)} # 带权的边集
drawdigraph(E0)
[d,di,do]=degreeset(V0,E) # 我理解这个函数的意思是,对每一个顶点算出度、入度、出度
# 保存在三个容器中
v0=0 # 起始结点
vn=5 # 终止结点
W=craticalpath(V0,E,di,do,v0,vn)
S=graphw2graph(W)
drawdigraph(S)
return W
二分图判断
def isbigraph(V,E,V1,V2):
tv=(V==V1|V2) # V是V1和V2的并
tv=tv&(V1&V2==set({})) # V1交V2是空集
for (u,v) in E: # E中的每条边都是一个点在V1一个点在V2
tv=tv&(u in V1)&(v in V2)
return tv