《图结构及其算法》
1.图的分类:
1.1 Graph
#######定义类接口
from abc import abstractmethod,ABCMeta
class Graph(metaclass=ABCMeta):
#边数
@abstractmethod
def getE(self):
pass
#顶点数
@abstractmethod
def getV(self):
pass
#判断边
@abstractmethod
def adj(self,v:int):
pass
#度数:相邻边
@abstractmethod
def degree(self,v:int):
pass
#
@abstractmethod
def toString(self):
pass
1.2 领接矩阵(AdjMatrix)
from python_code.graph.Graph import Graph
#空:O(V^2)
class AdjMatrix(Graph):
def __init__(self,fileName):
with open(fileName,'r') as reader:
lines=reader.readlines()
arr=lines[0].split(" ")
#总定点数,总边数
self.V,self.E=int(arr[0]),int(arr[1])
#二维邻接矩阵
#初始化
self.__adj=[[0] * self.V for _ in range(self.V)]
#无向无权图
for i in range(1,len(lines)):
arr=lines[i].split(" ")
a,b=int(arr[0]),int(arr[1])
self.validateVertex(a)
self.validateVertex(b)
#检测自环边
if a==b:
raise Exception("出现了自环边,错误")
#检测平行边:已经被定义
if self.__adj[a][b]==1:
raise Exception("出现了平行边,错误")
#构建双向关系
self.__adj[a][b]=1
self.__adj[b][a]=1
def validateVertex(self,V):
if V<0 or V>=self.V:
raise Exception("顶点 {} 不合格".format(v))
#边数
def getE(self):
return self.E
#顶点数
def getV(self):
return self.V
#判边
#时:o(1)
def hasEdge(self, v: int, w: int):
self.validateVertex(v)
self.validateVertex(w)
return self.__adj[v][w] == 1
#获邻顶点:同行有“1”
def adj(self,V:int):
self.validateVertex(V)
res=[]
for i in range(self.V):
if self.__adj[V][i]==1:
res.append(i)
return res
#获指定顶点度数
def degree(self, v: int):
return len(self.adj(v))
def toString(self):
res='顶点数={},边数={}\n'.format(self.V,self.E)
for i in range(self.V):
for j in range(self.V):
res+= str(self.__adj[i][j]) + ' '
res+='\n'
return res
# 测试
adjMatrix = AdjMatrix("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph.txt")
print(adjMatrix.toString())
print(adjMatrix.degree(1))
1.2 邻接Set
from python_code.graph.Graph import Graph
class AdjSet(Graph):
# 时:O(E)
def __init__(self, fileName):
with open(fileName, 'r') as reader:
lines = reader.readlines()
arr = lines[0].split(" ")
#总定点数,总边数
self.V, self.E = int(arr[0]), int(arr[1])
#key(初始化数组表):使用set存储邻接表
self.adjSets=[0]*self.V
for i in range(self.V):
self.adjSets[i]=set()
#无向无权图
for i in range(1, len(lines)):
arr = lines[i].split(" ")
a, b = int(arr[0]), int(arr[1])
self.validateVertex(a)
self.validateVertex(b)
# 检测自环边
if a == b:
raise Exception("出现了自环边,错误")
# 检测平行边:已经过定义
# O(1)
if b in self.adjSets[a]:
raise Exception("出现了平行边,错误")
#构建双向关系
self.adjSets[a].add(b)#a:含b
self.adjSets[b].add(a)#b:含a
def validateVertex(self,v):
if v < 0 or v >= self.V:
raise Exception("顶点 {} 不合格".format(v))
# 边数
def getE(self):
return self.E
# 顶点数
def getV(self):
return self.V
# 判边
# 时:o(1)
def hasEdge(self, v: int, w: int):
self.validateVertex(v)
self.validateVertex(w)
#key
return w in self.adjSets[v]
# 获邻顶点:同行有“1”
def adj(self, V: int):
self.validateVertex(V)
return self.adjSets[V]
# 获指定顶点度数
def degree(self, v: int):
return len(self.adjSets[v])
def toString(self):
res = '顶点数={},边数={}\n'.format(self.V, self.E)
for V in range(self.V):
res+=str(V)+':'
for w in self.adjSets[V]:
res += str(w) + ' '
res += '\n'
return res
# 测试
adjMatrix = AdjSet(
"D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph.txt")
print(adjMatrix.toString())
print(adjMatrix.degree(1))
2.图的遍历
2.1深度优先遍历 (DFS)
2.1.1基本遍历
#方案一:迭代法(stack)
from python_code.graph.AdjSet import AdjSet
class GraphDFS:
def __init__(self,g:AdjSet):
#
if g is None:
return
#
self.g=g
self.res,self.visited=[],[False]*self.g.getV()
#遍历,递归
for v in range(self.g.getV()):
if not self.visited[v]:
self.dfs(v)
#o(v),o(v)
def dfs(self,v):
#
stack,self.visited[v]=[v],True
#
while len(stack):
#
curr=stack.pop()
self.res.append(curr)
#
for w in self.g.adj(curr):
if not self.visited[w]:
stack.append(w)#注:默认升序入,降序出
self.visited[w]=True
def getRes(self):
return self.res
if __name__ == '__main__':
adjSet = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-dfs.txt")
graphDFS = GraphDFS(adjSet)
print(graphDFS.getRes())
#方案二:递归法(DFS)
from python_code.graph.AdjSet import AdjSet
#也适用于有向有权图
#from python_code.graph.direct.GraphImpl import GraphImpl
class GraphDFSR:
def __init__(self,g:AdjSet):
#
if g is None:
return
#
self.g=g
self.res,self.visited=[],[False]*self.g.getV()
#
for v in range(self.g.getV()):
if not self.visited[v]:
self.dfs(v)
#o(v),o(v)
def dfs(self,v):
#
self.visited[v]=True
self.res.append(v)
#
for w in self.g.adj(v):
if not self.visited[w]:
self.dfs(w)
def getRes(self):
return self.res
if __name__ == '__main__':
adjSet = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-dfs.txt")
graphDFS = GraphDFSR(adjSet)
print(graphDFS.getRes())
2.1.2联通分量
#功能:分量计数
from python_code.graph.AdjSet import AdjSet
class CC:
def __init__(self,g:AdjSet):
if g is None:
return
self.g=g
#ccCount:统计联通分量
self.ccCount,self.visited=0,[False]*self.g.getV()
#遍历:未防止未联通顶点
for v in range(self.g.getV()):
if not self.visited[v]:
self.ccCount+=1
self.dfs(v)
#o(v),o(v)
def dfs(self,v):
self.visited[v]=True
for w in self.g.adj(v):
if not self.visited[w]:
self.dfs(w)
def getccCount(self):
return self.ccCount
if __name__ == '__main__':
adjSet = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-dfs.txt")
cc = CC(adjSet)
print(cc.getccCount())
#功能:分量存储
from python_code.graph.Graph import Graph
from python_code.graph.AdjSet import AdjSet
class CC1:
def __init__(self, g: Graph):
if g is None:
return
self.g = g
#ccCount:统计联通分量
self.ccCount, self.visited = 0, [-1] * self.g.getV()
for v in range(self.g.getV()):
if self.visited[v] == -1:
self.ccCount += 1
#key
self.dfs(v, self.ccCount)
def dfs(self,v,ccId):
self.visited[v]=ccId-1
for w in self.g.adj(v):
if self.visited[w]==-1:
self.dfs(w,ccId)
def getccCount(self):
return self.ccCount
def components(self):
#用于存储分支分量
res=[0]*self.ccCount
for i in range(self.ccCount):
res[i]=[]
#在各支路分别赋值
for v in range(self.g.getV()):
res[self.visited[v]].append(v)
return res
def isConnected(self, v, w):
self.validateVertex(v)
self.validateVertex(w)
return self.visited[v] == self.visited[w]
def validateVertex(self, v):
if v < 0 or v >= self.g.getV():
raise Exception("顶点 {} 不合格".format(v))
if __name__ == '__main__':
adjSet = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-dfs.txt")
cc = CC1(adjSet)
print(cc.getccCount())
print(cc.components())
print(cc.isConnected(0, 6))
2.1.3单源路径
#方案一:先存储所有prev->按指定target返回路径
from python_code.graph.AdjSet import AdjSet
class SingleSourcePath:
def __init__(self, g: AdjSet, source: int):
if g is None:
return
self.g = g
self.source = source
self.visited = [False] * self.g.getV()
# key:self.prevs 存储每个顶点的前一个顶点,初始化为 -1
self.prevs = [-1] * self.g.getV()
# 遍历初始化:源顶点的前一个顶点设置为源顶点本身
self.dfs(self.source, self.source)
def dfs(self, v, prev):
self.visited[v] = True
# key:维护顶点 v 的前一个顶点
self.prevs[v] = prev
for w in self.g.adj(v):
if not self.visited[w]:
# v 是 w 的前一个顶点
self.dfs(w, v)
def isConnected(self, target):
self.validateVertex(target)
return self.visited[target]
def validateVertex(self, v):
if v < 0 or v >= self.g.getV():
raise Exception("顶点 {} 不合格".format(v))
#根据prev[v],存储路径
def path(self,target):
#
if not self.isConnected(target):
return
#
res=[]
while target !=self.source:
res.append(target)
target=self.prevs[target]
res.append(self.source)
#
res.reverse()
return res
if __name__ == '__main__':
adjSet = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-bfs.txt")
graphDFS = SingleSourcePath(adjSet, 0)
print(graphDFS.path(6))
#方案二:只遍历至指定target,并保存prev->直接返回path()
from python_code.graph.AdjSet import AdjSet
class TwoVertexPath:
def __init__(self, g: AdjSet, source: int, target: int):
if g is None:
return
self.g = g
self.source = source
# key
self.target = target
self.res = []
self.visited = [False] * self.g.getV()
# self.prevs 存储每个顶点的前一个顶点,初始化为 -1
self.prevs = [-1] * self.g.getV()
#源顶点的前一个顶点设置为源顶点本身
self.dfs(self.source, self.source)
#key
def dfs(self, v, prev) -> bool:
self.visited[v] = True
# 维护顶点 v 的前一个顶点
self.prevs[v] = prev
#key key key
if v == self.target:
return True
#
for w in self.g.adj(v):
if not self.visited[w]:
# v 是 w 的前一个顶点
# 如果已经找到了,不用再去遍历 v 的下一个相邻节点了
if self.dfs(w, v):
return True
return False
def path(self):
#
if not self.isConnected():
return
#
res = []
#key
tmp = self.target
while tmp != self.source:
res.append(tmp)
tmp = self.prevs[tmp]
res.append(self.source)
#
res.reverse()
return res
def isConnected(self):
self.validateVertex(self.target)
return self.visited[self.target]
def validateVertex(self, v):
if v < 0 or v >= self.g.getV():
raise Exception("顶点 {} 不合格".format(v))
if __name__ == '__main__':
adjSet = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-bfs.txt")
graphDFS = TwoVertexPath(adjSet, 0, 6)
print(graphDFS.path())
2.1.4环检测
#方案一
from python_code.graph.AdjSet import AdjSet
class CycleDetection:
def __init__(self,g:AdjSet):
#
if g is None:
return
self.g=g
#
self.hasCycle,self.visited=False,[False]*self.g.getV()
#防止存在非联通顶点
for v in range(self.g.getV()):
if not self.visited[v]:
self.dfs(v,v)
#o(V),o(V)
def dfs(self,v,prev):
self.visited[v]=True
for w in self.g.adj(v):
if not self.visited[w]:
self.dfs(w,v)
else:
#存环:w非v的前一个顶点
if w!=prev:
self.hasCycle= True
def hasCycleInGraph(self):
return self.hasCycle
if __name__ == '__main__':
adjSet = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-dfs.txt")
graphDFS = CycleDetection(adjSet)
print(graphDFS.hasCycleInGraph())
#方案二:一旦递归检测到有一个环,直接返回,不在进行递归
from python_code.graph.AdjSet import AdjSet
class CycleDetection1:
def __init__(self,g:AdjSet):
#
if g is None:
return
#
self.g=g
self.hasCycle,self.visited=False,[False]*self.g.getV()
#遍历递归
for v in range(self.g.getV()):
if not self.visited[v]:
#key:bool类型
if self.dfs(v,v):
self.hasCycle=True
break
# o(V),o(V)
def dfs(self,v,prev)->bool:
self.visited[v]=True
for w in self.g.adj(v):
if not self.visited[w]:
#key:一旦递归一环,直接返回
if self.dfs(w,v):
return True
else:
#存环:w非v的前一个顶点
if w!=prev:
return True
return False
def hasCycleInGraph(self):
return self.hasCycle
if __name__ == '__main__':
adjSet = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-dfs.txt")
graphDFS = CycleDetection1(adjSet)
print(graphDFS.hasCycleInGraph())
2.1.5二分图检测
from python_code.graph.AdjSet import AdjSet
class BitpartitionDetection:
def __init__(self,g:AdjSet):
#
if g is None:
return
#0:红 1:蓝
self.g=g
self.isBipartition,self.visited=True,[False]*self.g.getV()
self.colors=[-1]*self.g.getV()
#
for v in range(self.g.getV()):
if not self.visited[v]:
#转成bool类型,减少遍历
if not self.dfs(v,0):
self.isBipartition=False
break
#o(v),o(v)
def dfs(self,v,color)->bool:
#设置属性
self.visited[v]=True
self.colors[v]=color
#
for w in self.g.adj(v):
if not self.visited[w]:
#v与w的颜色相异
if not self.dfs(w,1-color):
return False
#二分图:相邻颜色相异
elif self.colors[w]==self.colors[v]:
return False
return True
def isBipartitionGraph(self):
return self.isBipartition
if __name__ == '__main__':
adjSet = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-dfs.txt")
graphDFS = BitpartitionDetection(adjSet)
print(graphDFS.isBipartitionGraph())
2.2 广度优先遍历(BFS)
2.2.1基本遍历
#迭代法(queue)
from collections import deque
from python_code.graph.AdjSet import AdjSet
#也适用有向有权图
#from python_code.graph.direct.GraphImpl import GraphImpl
class GraphBFS:
def __init__(self, g:AdjSet):
#
if g is None:
return
#
self.g=g
self.res=[]
self.visited=[False]*self.g.getV()
#
for v in range(self.g.getV()):
if not self.visited[v]:
self.bfs(v)
def bfs(self,v):
#
self.visited[v] = True
queue=deque()
queue.append(v)
#
while len(queue):
#
curr=queue.popleft()
self.res.append(curr)
#
for w in self.g.adj(curr):
if not self.visited[w]:
queue.append(w)
self.visited[w]=True
def getRes(self):
return self.res
if __name__ == '__main__':
g = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-bfs.txt")
graphBFS = GraphBFS(g)
print(graphBFS.getRes())
2.2.2联通分量
#统计分量个数
from collections import deque
from python_code.graph.AdjSet import AdjSet
class CC:
def __init__(self,g:AdjSet):
#
if g is None:
return
#
self.g=g
self.ccCount=0
self.visited=[-1]*self.g.getV()
#
for v in range(self.g.getV()):
if self.visited[v]==-1:
self.ccCount+=1
self.bfs(v,self.ccCount)
def bfs(self,v,ccId):
#
self.visited[v]=ccId-1
queue=deque()
queue.append(v)
#
while len(queue):
#
curr=queue.popleft()
#
for w in self.g.adj(curr):
if self.visited[w]==-1:
queue.append(w)
#key
self.visited[w]=ccId
def getccCount(self):
return self.ccCount
def components(self):
#
res=[0]*self.ccCount
for i in range(self.ccCount):
res[i]=[]
#
for v in range(self.g.getV()):
res[self.visited[v]].append(v)
return res
def isConnected(self, v, w):
self.validateVertex(v)
self.validateVertex(w)
return self.visited[v] == self.visited[w]
def validateVertex(self, v):
if v < 0 or v >= self.g.getV():
raise Exception("顶点 {} 不合格".format(v))
if __name__ == '__main__':
g = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-bfs.txt")
cc = CC(g)
print(cc.getccCount())
2.2.3单源路径
#方案一:先存储所有prev->按指定target返回路径
from collections import deque
from python_code.graph.AdjSet import AdjSet
class SingleSourcePath:
def __init__(self,g:AdjSet,source:int):
#
if g is None:
return
#
self.g=g
self.source=source
self.prevs=[-1]*self.g.getV()
self.visited=[False]*self.g.getV()
#
self.bfs(source)
def bfs(self,v):
#key
self.prevs[v]=v
self.visited[v] = True
queue=deque()
queue.append(v)
#
while len(queue):
#
curr=queue.popleft()
#
for w in self.g.adj(curr):
if not self.visited[w]:
queue.append(w)
self.visited[w]=True
#key
self.prevs[w]=curr
def isConnected(self, target):
self.validateVertex(target)
return self.visited[target]
def validateVertex(self, v):
if v < 0 or v >= self.g.getV():
raise Exception("顶点 {} 不合格".format(v))
def path(self,target):
#
if not self.isConnected(target):
return
#
res=[]
while target != self.source:
res.append(target)
target=self.prevs[target]
res.append(self.source)
#
res.reverse()
return res
if __name__ == '__main__':
g = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-bfs.txt")
graphBFS = SingleSourcePath(g, 0)
print(graphBFS.path(6))
#方案二:只遍历至指定target,并保存prev->直接返回path()
from collections import deque
from python_code.graph.AdjSet import AdjSet
class SingleSourcePath1:
def __init__(self, g: AdjSet, source: int, target: int):
if g is None:
return
#
self.g = g
self.source = source
self.target = target
self.prevs = [-1] * self.g.getV()
self.visited = [False] * self.g.getV()
#
self.bfs(source)
def bfs(self, v):
#key
self.prevs[v] = v
self.visited[v] = True
queue = deque()
queue.append(v)
#
while len(queue):
#
curr = queue.popleft()
#key
if curr == self.target:
return
#
for w in self.g.adj(curr):
if not self.visited[w]:
queue.append(w)
self.visited[w] = True
#key
self.prevs[w] = curr
def isConnected(self):
self.validateVertex(self.target)
return self.visited[self.target]
def validateVertex(self, v):
if v < 0 or v >= self.g.getV():
raise Exception("顶点 {} 不合格".format(v))
def path(self):
#
if not self.isConnected():
return
#
res = []
tmp=self.target
while tmp != self.source:
res.append(tmp)
tmp = self.prevs[tmp]
res.append(self.source)
#
res.reverse()
return res
if __name__ == '__main__':
g = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-bfs.txt")
graphBFS = SingleSourcePath1(g, 0, 6)
print(graphBFS.path())
#方案三:最短路径
from collections import deque
from python_code.graph.AdjSet import AdjSet
class SingleSourcePathShortestPath:
def __init__(self, g: AdjSet, source: int):
if g is None:
return
#
self.g = g
self.source = source
self.prevs = [-1] * self.g.getV()
self.visited = [False] * self.g.getV()
self.distance = [-1] * self.g.getV()#key
#
self.bfs(source)
def bfs(self,v):
#
self.distance[v]=0#key
self.prevs[v]=v
self.visited[v]=True
queue=deque()
queue.append(v)
#
while len(queue):
#
curr=queue.popleft()
#
for w in self.g.adj(curr):
if not self.visited[w]:
queue.append(w)
self.visited[w]=True
#key
self.prevs[w]=curr
self.distance[w]=self.distance[curr]+1
def isConnected(self, target):
self.validateVertex(target)
return self.visited[target]
def validateVertex(self, v):
if v < 0 or v >= self.g.getV():
raise Exception("顶点 {} 不合格".format(v))
def path(self,target):
#
if not self.isConnected(target):
return
#
res = []
while target != self.source:
res.append(target)
target = self.prevs[target]
res.append(self.source)
#
res.reverse()
return res
#source->target间的距离
#O(1)
def dis(self, target):
self.validateVertex(target)
return self.distance[target]
if __name__ == '__main__':
g = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-bfs.txt")
graphBFS = SingleSourcePathShortestPath(g, 0)
print(graphBFS.path(6))
print(graphBFS.dis(6))
2.2.4环检测
from collections import deque
from python_code.graph.AdjSet import AdjSet
class CycleDetection:
def __init__(self, g: AdjSet):
if g is None:
return
#
self.g = g
self.visited = [False] * self.g.getV()
self.prevs = [-1] * self.g.getV()
#
self.hasCycle = False
for v in range(self.g.getV()):
if not self.visited[v]:
if self.bfs(v):
self.hasCycle = True
break
def bfs(self,v)->bool:
#
self.prevs[v] = v
self.visited[v] = True
queue = deque()
queue.append(v)
#
while len(queue):
curr = queue.popleft()
for w in self.g.adj(curr):
if not self.visited[w]:
queue.append(w)
self.visited[w] = True
#key
self.prevs[w] = curr
else:
if w!=self.prevs[curr]:
return True
return False
def hasCycleInGraph(self):
return self.hasCycle
if __name__ == '__main__':
g = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-bfs.txt")
graphBFS = CycleDetection(g)
print(graphBFS.hasCycleInGraph())
2.2.5二分图检测
from collections import deque
from python_code.graph.AdjSet import AdjSet
class BipartitionDetection:
def __init__(self, g: AdjSet):
if g is None:
return
#属性设置
self.g = g
self.visited = [False] * self.g.getV()
self.colors = [-1] * self.g.getV()#0:红 1:蓝色
#
self.isBipartition = True
for v in range(self.g.getV()):
if not self.visited[v]:
if not self.bfs(v):
self.isBipartition=False
break
def bfs(self, v):
#
self.colors[v] = 0
self.visited[v] = True
queue = deque()
queue.append(v)
#
while len(queue):
#
curr = queue.popleft()
#
for w in self.g.adj(curr):
if not self.visited[w]:
queue.append(w)
self.visited[w] = True
#key
self.colors[w] = 1 - self.colors[curr]
#二分图:相邻颜色相异
elif self.colors[w] == self.colors[curr]:
return False
return True
def getIsBipartition(self):
return self.isBipartition
if __name__ == '__main__':
g = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-bfs.txt")
graphBFS = BipartitionDetection(g)
print(graphBFS.getIsBipartition())
3.floodfill 算法
3.1数组联通
from typing import List
#二维转一维
def twoDimConvertOneDim(arr)->List[int]:
#
rows,cols=len(arr),len(arr[0])
res=[0]*(rows*cols)
#
for i in range(rows):
for j in range(cols):
#key
index=i*cols+j
res[index]=arr[i][j]
return res
#一维转二维
def oneDimConvertTwoDim(arr,rows,cols)->List[List[int]]:
res=[[0]*cols for _ in range(rows)]
for index in range(len(arr)):
#key
i=index//cols
j=index%rows
res[i][j]=arr[index]
return res
#一个元素的四周、八向上的元素
def printAdj(arr,i,j)->None:
rows,cols=len(arr),len(arr[0])
#key
directions=[
[-1,0],[1,0],[0,-1],[0,1],
[-1,-1],[-1,1],[1,-1],[1,1]
]
for dir in directions:
row=i+dir[0]
col=j+dir[1]
if 0<=row<rows and 0<=col<cols:
print(arr[row][col])
if __name__ == '__main__':
data = [
[4, 2, 5, 11],
[3, 7, 1, 9],
[32, 22, 13, 8]
]
print(twoDimConvertOneDim(data))
arr = [4, 2, 5, 11, 3, 7, 1, 9, 32, 22, 13, 8]
print(oneDimConvertTwoDim(arr, 3, 4))
printAdj(data, 0, 0)
3.2最大联通分量顶点个数
from python_code.graph.AdjSet import AdjSet
class ccMaxCount:
def __init__(self,g:AdjSet):
if g is None:
return
#
self.g=g
self.maxVertexNum,self.visited=0,[False]*self.g.getV()
#
for v in range(self.g.getV()):
if not self.visited[v]:
#key
self.maxVertexNum=max(self.dfs(v),self.maxVertexNum)
#o(v),o(v)
def dfs(self,v)->int:
#
num = 1
self.visited[v]=True
#
for w in self.g.adj(v):
if not self.visited[w]:
#key
num+=self.dfs(w)
return num
def getMaxVertexNum(self):
return self.maxVertexNum
if __name__ == '__main__':
adjSet = AdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\graph-dfs.txt")
graphDFS = ccMaxCount(adjSet)
print(graphDFS.getMaxVertexNum())
3.3岛屿最大面积
#方案一:先建图(==1),再遍历
class MaxAreaOfIsland:
def __init__(self):
#
self.directions=[[-1,0],[1,0],[0,-1],[0,1]]
self.rows=self.cols=0
#
self.grid=[]
self.graph=[]
def maxAreaOfIsland(self,grid)->int:
#
if grid is None:
return 0
#
self.rows=len(grid)
if self.rows==0:
return 0
self.cols=len(grid[0])
#
self.grid = grid
self.graph=self.constructGraph()
self.visited = [False] * len(self.graph)
#
size=0
for index in range(len(self.graph)):
i=index//self.cols
j=index%self.cols
if not self.visited[index] and self.grid[i][j]==1:
#key
size=max(self.dfs(index),size)
return size
def dfs(self,v)->int:
#
self.visited[v]=True
#
num = 1
for w in self.graph[v]:
if not self.visited[w]:
num+=self.dfs(w)
return num
def constructGraph(self):
#
g=[0]*(self.rows*self.cols)
for v in range(len(g)):
g[v]=set()
#key:添加相邻元素至graph
for v in range(len(g)):
i,j=v//self.cols,v%self.cols
if self.grid[i][j]==1:
for dir in self.directions:
nexti=i+dir[0]
nextj=j+dir[1]
if self.inArea(nexti,nextj) and self.grid[nexti][nextj]==1:
w=nexti*self.cols+nextj
g[v].add(w)
g[w].add(v)
return g
def inArea(self,row,col):
return 0<=row<self.rows and 0<=col<=self.cols
if __name__ == '__main__':
grid = [
[0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0],
[0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
]
maxAreaOfIsland = MaxAreaOfIsland()
print(maxAreaOfIsland.maxAreaOfIsland(grid))
#方案二:直接在grid上,利用dfs遍历
class MaxAreaOfIsland:
def __init__(self):
#
self.directions=[[-1,0],[1,0],[0,-1],[0,1]]
self.rows=self.cols=0
#
self.grid=[]
#self.graph=[]
def maxAreaOfIsland(self,grid)->int:
#
if grid is None:
return 0
#
self.rows=len(grid)
if self.rows==0:
return 0
self.cols=len(grid[0])
#
self.grid = grid
#self.graph=self.constructGraph()
#self.visited = [False] * len(self.graph)
#
size=0
for i in range(self.rows):
for j in range(self.cols):
if self.grid[i][j]==1:
size = max(self.dfs(i,j), size)
return size
def dfs(self,row,col)->int:
#key:设置为0与设置已访问,处理方式一样
self.grid[row][col]=0
#
num = 1
for dir in self.directions:
nexti = row + dir[0]
nextj = col + dir[1]
if self.inArea(nexti, nextj) and self.grid[nexti][nextj] == 1:
num += self.dfs(nexti,nextj)
return num
def inArea(self,row,col):
return 0<=row<self.rows and 0<=col<=self.cols
if __name__ == '__main__':
grid = [
[0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0],
[0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
]
maxAreaOfIsland = MaxAreaOfIsland()
print(maxAreaOfIsland.maxAreaOfIsland(grid))
#方案三:直接在grid上,利用利用栈迭代(dfs)
class MaxAreaOfIsland:
def __init__(self):
#
self.directions=[[-1,0],[1,0],[0,-1],[0,1]]
self.rows=self.cols=0
#
#self.grid=[]
#self.graph=[]
def maxAreaOfIsland(self,grid)->int:
#
if grid is None:
return 0
#
self.rows=len(grid)
if self.rows==0:
return 0
self.cols=len(grid[0])
#
#self.grid = grid
#self.graph=self.constructGraph()
#self.visited = [False] * len(self.graph)
#
size=0
for row in range(self.rows):
for col in range(self.cols):
if grid[row][col]==1:
#初始化栈[[0,0]]
stack=[[row,col]]
# key:
currsize = 0
grid[row][col]=0#表明已访问
while len(stack):
#源头
curr=stack.pop()
curri,currj=curr[0],curr[1]
currsize+=1
#附近
for dir in self.directions:
nexti=curri+dir[0]
nextj=currj+dir[1]
if self.inArea(nexti, nextj) and grid[nexti][nextj] == 1:
stack.append([nexti, nextj])
grid[nexti][nextj] = 0
size = max(currsize, size)
return size
def inArea(self,row,col):
return 0<=row<self.rows and 0<=col<=self.cols
if __name__ == '__main__':
grid = [
[0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0],
[0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
]
maxAreaOfIsland = MaxAreaOfIsland()
print(maxAreaOfIsland.maxAreaOfIsland(grid))
#方案四:直接在grid上,利用利用队列迭代(bfs)
from collections import deque
class MaxAreaOfIsland:
def __init__(self):
#
self.directions=[[-1,0],[1,0],[0,-1],[0,1]]
self.rows=self.cols=0
#
#self.grid=[]
#self.graph=[]
def maxAreaOfIsland(self,grid)->int:
#
if grid is None:
return 0
#
self.rows=len(grid)
if self.rows==0:
return 0
self.cols=len(grid[0])
#
#self.grid = grid
#self.graph=self.constructGraph()
#self.visited = [False] * len(self.graph)
#
size = 0
for row in range(self.rows):
for col in range(self.cols):
if grid[row][col] == 1:
# 初始化栈[[0,0]]
queue = deque()
queue.append([row, col])
# key:
currsize = 0
grid[row][col] = 0
while len(queue):
#源头
curr = queue.popleft()
curri, currj = curr[0], curr[1]
currsize += 1
#附近
for dir in self.directions:
nexti = curri + dir[0]
nextj = currj + dir[1]
if self.inArea(nexti, nextj) and grid[nexti][nextj] == 1:
queue.append([nexti, nextj])
#key
grid[nexti][nextj] = 0
size = max(size, currsize)
return size
def inArea(self,row,col):
return 0<=row<self.rows and 0<=col<=self.cols
if __name__ == '__main__':
grid = [
[0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0],
[0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0],
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
]
maxAreaOfIsland = MaxAreaOfIsland()
print(maxAreaOfIsland.maxAreaOfIsland(grid))
4.有权图
4.1无向有权图
from python_code.graph.Graph import Graph
#空:O(V+E)
class WeightAdjSet(Graph):
def __init__(self,fileName):
with open(fileName,'r') as reader:
lines=reader.readlines()
arr=lines[0].split(' ')
# 总定点数,总边数
self.V, self.E = int(arr[0]), int(arr[1])
# 初始化(邻接表):用dic()来存储
self.adjSets = [0] * self.V
for i in range(self.V):
self.adjSets[i]=dict()
#无向有权图:时间o(E)
for i in range(1, len(lines)):
arr = lines[i].split(" ")
a, b = int(arr[0]), int(arr[1])
self.validateVertex(a)
self.validateVertex(b)
# 检测自环边
if a == b:
raise Exception("出现了自环边,错误")
# 检测平行边:已经被定义
# o(1)
if b in self.adjSets[a].keys():
raise Exception("出现了平行边,错误")
# 构建双向关系
weight=int(arr[2])
self.adjSets[a][b] = weight
self.adjSets[b][a] = weight
def validateVertex(self, V):
if V < 0 or V >= self.V:
raise Exception("顶点 {} 不合格".format(V))
# 边数
def getE(self):
return self.E
# 顶点数
def getV(self):
return self.V
# 判边
# 时:o(1)
def hasEdge(self, v: int, w: int):
self.validateVertex(v)
self.validateVertex(w)
return w in self.adjSets[v].keys()
#获边权重值
def getWeight(self, v, w):
if self.hasEdge(v, w):
return self.adjSets[v][w]
return -1
# 获邻顶点
def adj(self, V: int):
self.validateVertex(V)
return self.adjSets[V].keys()
# 获指定顶点度数
def degree(self, v: int):
return len(self.adjSets(v))
def toString(self):
res = '顶点数={},边数={}\n'.format(self.V, self.E)
for v in range(self.V):
res += str(v) + ':'
for w in self.adjSets[v].keys():
res +='('+str(w)+','+str(self.adjSets[v][w])+')'
res += '\n'
return res
if __name__ == '__main__':
# 测试
adjSet = WeightAdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\weighted-graph.txt")
print(adjSet.toString())
4.2最小生成树
#方案一:
from python_code.graph.weighted.WeightAdjSet import WeightAdjSet
from python_code.graph.dfs.CC1 import CC1
class Prim:
def __init__(self,g:WeightAdjSet):
self.res=[]
MAX_INT=2**31-1
#条件:g为连通图
cc=CC1(g)
if cc.getCcCount()>1:
return
#时:o(V*V*E)
visited=[False]*g.getV()
#切分起点
visited[0]=True
#v-1次切分
for i in range(g.getV()-1):
minEdge=(-1,-1,MAX_INT)
#key:minEdge
for v in range(g.getV()):
if visited[v]:
for w in g.adj(v):
if not visited[w] and g.getWeight(v,w)<minEdge[2]:
minEdge=(v,w,g.getWeight(v,w))
self.res.append(minEdge)
#key:扩展切分(将横切边都设为已访问)
v,w=minEdge[0],minEdge[1]
newV=w if visited[v] else v
visited[newV]=True
def getRes(self):
return self.res
if __name__ == '__main__':
adjSet = WeightAdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\prim.txt")
prim = Prim(adjSet)
print(prim.getRes())
#方案二:用优先队列优化
import heapq
from python_code.graph.weighted.WeightAdjSet import WeightAdjSet
from python_code.graph.dfs.CC1 import CC1
class Prim:
def __init__(self,g:WeightAdjSet):
self.res=[]
MAX_INT=2**31-1
#条件:g为连通图
cc=CC1(g)
if cc.getCcCount()>1:
return
#时:o(E*logE)
visited=[False]*g.getV()
#切分起点
visited[0]=True
pq=[]
for w in g.adj(0):
heapq.heappush(pq,(g.getWeight(0,w),0,w))#将(weight,v,w)push进小顶堆
while len(pq):
# key:minEdge
# 利用小顶堆的优先队列,实现weight比较
minEdge = heapq.heappop(pq)
#非横切边判断
if visited[minEdge[1]] and visited[minEdge[2]]:
continue
#
self.res.append(minEdge)
#key:扩展切分(将横切边都设为已访问),并放入优先队列
v,w=minEdge[1],minEdge[2]
newV=w if visited[v] else v
visited[newV]=True
for w in g.adj(newV):
heapq.heappush(pq,(g.getWeight(newV,w),newV,w))
def getRes(self):
return self.res
if __name__ == '__main__':
adjSet = WeightAdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\prim.txt")
prim = Prim(adjSet)
print(prim.getRes())
4.3最短路径
#方案1.1
#https://www.bilibili.com/video/BV1zz4y1m7Nq?spm_id_from=333.337.search-card.all.click
from python_code.graph.weighted.WeightAdjSet import WeightAdjSet
#时:O(V * (V + E))
class Dijkstra:
def __init__(self,g:WeightAdjSet,source:int):
self.g=g
#设置属性
self.distance=[2**31-1]*g.getV()
self.distance[source]=0
visited=[False]*g.getV()
#找:当前未访问的最短路径节点
while True:
curr,currDis=-1,2**31-1
for v in range(g.getV()):
if not visited[v] and self.distance[v] < currDis:
currDis = self.distance[v]
curr = v
if curr==-1:
break
#定:该节点的最短路径
visited[curr]=True
#更:其他节点路径长度
for w in g.adj(curr):
if not visited[w]:
if self.distance[curr]+g.getWeight(curr,w)<self.distance[w]:
self.distance[w]=self.distance[curr]+g.getWeight(curr,w)
def minDistanceTo(self,w):
self.validateVertex(w)
return self.distance[w]
def validateVertex(self, v):
if v < 0 or v >= self.g.getV():
raise Exception("顶点 {} 不合格".format(v))
if __name__ == '__main__':
g = WeightAdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\Dijkstra.txt")
dijkstra = Dijkstra(g, 0)
print(dijkstra.minDistanceTo(1))
#方案1.2:利用queue,找当前未访问的最短路径节点
import heapq
from python_code.graph.weighted.WeightAdjSet import WeightAdjSet
#时:O(V * (logV + E))
class Dijkstra:
def __init__(self,g:WeightAdjSet,source:int):
self.g=g
#设置属性
self.distance=[2**31-1]*g.getV()
self.distance[source]=0
visited=[False]*g.getV()
#找:当前未访问的最短路径节点
pq=[]
heapq.heappush(pq,(0,source))
while len(pq):
curr=heapq.heappop(pq)[1]
if visited[curr]:
continue
#定:该节点的最短路径
visited[curr]=True
#更:其他节点路径长度
for w in g.adj(curr):
if not visited[w]:
if self.distance[curr]+g.getWeight(curr,w)<self.distance[w]:
self.distance[w]=self.distance[curr]+g.getWeight(curr,w)
#key:把更新后的其他节点路径长度,加载进queue->curr=w
heapq.heappush(pq, (self.distance[w], w))
def minDistanceTo(self,w):
self.validateVertex(w)
return self.distance[w]
def validateVertex(self, v):
if v < 0 or v >= self.g.getV():
raise Exception("顶点 {} 不合格".format(v))
if __name__ == '__main__':
g = WeightAdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\Dijkstra.txt")
dijkstra = Dijkstra(g, 0)
print(dijkstra.minDistanceTo(1))
#方案2:+可返回最短路径
import heapq
from python_code.graph.weighted.WeightAdjSet import WeightAdjSet
class Dijkstra:
def __init__(self,g:WeightAdjSet,source:int):
self.g=g
#设置属性
self.source=source
self.distance=[2**31-1]*g.getV()
self.distance[source]=0
self.visited=[False]*g.getV()
#key:记录路径1
self.prevs=[-1]*g.getV()
#找:当前未访问的最短路径节点
pq=[]
heapq.heappush(pq,(0,source))
while len(pq):
curr=heapq.heappop(pq)[1]
if self.visited[curr]:
continue
#定:该节点的最短路径
self.visited[curr]=True
#更:其他节点路径长度
for w in g.adj(curr):
if not self.visited[w]:
if self.distance[curr]+g.getWeight(curr,w)<self.distance[w]:
self.distance[w]=self.distance[curr]+g.getWeight(curr,w)
#key1:把更新后的其他节点路径长度,加载进queue->curr=w
heapq.heappush(pq, (self.distance[w], w))
#key2:记录路径2
self.prevs[w]=curr
def minDistanceTo(self,w):
self.validateVertex(w)
return self.distance[w]
def validateVertex(self, v):
if v < 0 or v >= self.g.getV():
raise Exception("顶点 {} 不合格".format(v))
def isConnected(self, target):
self.validateVertex(target)
return self.visited[target]
def path(self,target):
#
if not self.isConnected(target):
return
#
res = []
while target != self.source:
res.append(target)
target = self.prevs[target]
res.append(self.source)
#
res.reverse()
return res
if __name__ == '__main__':
g = WeightAdjSet("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\Dijkstra.txt")
dijkstra = Dijkstra(g, 0)
print(dijkstra.minDistanceTo(1))
print(dijkstra.path(1))
5.有向图
5.1有向有权图
from python_code.graph.Graph import Graph
#空:O(V+E)
class WeightedGraphImpl(Graph):
def __init__(self,fileName,isDirected):
#
self.isDirected=isDirected
with open(fileName,'r') as reader:
lines=reader.readlines()
arr=lines[0].split(' ')
# 总定点数,总边数
self.V, self.E = int(arr[0]), int(arr[1])
# 初始化(邻接表):用dic()来存储
self.adjSets = [0] * self.V
for i in range(self.V):
self.adjSets[i]=dict()
#key
self.indegrees=[0]*self.V
self.outdegrees=[0]*self.V
#无向/有向有权图:时间o(E)
for i in range(1, len(lines)):
arr = lines[i].split(" ")
a, b = int(arr[0]), int(arr[1])
self.validateVertex(a)
self.validateVertex(b)
# 检测自环边
if a == b:
raise Exception("出现了自环边,错误")
# 检测平行边:已经被定义
# o(1)
if b in self.adjSets[a].keys():
raise Exception("出现了平行边,错误")
#有权图:取weight
weight=int(arr[2])
#key:构建关系
#有向图:(a->b)
if isDirected:
self.indegrees[b]+=1
self.outdegrees[a]+=1
self.adjSets[a][b] = weight
#无向图
if not isDirected:
self.adjSets[a][b] = weight
self.adjSets[b][a] = weight
def validateVertex(self, V):
if V < 0 or V >= self.V:
raise Exception("顶点 {} 不合格".format(V))
def isDirectedGraph(self):
return self.isDirected
# 边数
def getE(self):
return self.E
# 顶点数
def getV(self):
return self.V
# 判边
# 时:o(1)
def hasEdge(self, v: int, w: int):
self.validateVertex(v)
self.validateVertex(w)
return w in self.adjSets[v].keys()
#获边权重值
def getWeight(self, v, w):
if self.hasEdge(v, w):
return self.adjSets[v][w]
return -1
# 获邻顶点
def adj(self, V: int):
self.validateVertex(V)
return self.adjSets[V].keys()
#无向图:获指定顶点度数
def degree(self, v: int):
if self.isDirected:
raise Exception("只有无向图才可以计算顶点的度数")
return len(self.adjSets(v))
#有向图:入度与出度
def indegree(self, v):
if not self.isDirected:
raise Exception("只有有向图才可以计算顶点的入度")
self.validateVertex(v)
return self.indegrees[v]
def outdegree(self, v):
if not self.isDirected:
raise Exception("只有有向图才可以计算顶点的出度")
self.validateVertex(v)
return self.outdegrees[v]
def toString(self):
res = '顶点数={},边数={},isDirected = {}\n'.format(self.V, self.E,self.isDirected)
for v in range(self.V):
res += str(v) + ':'
for w in self.adjSets[v].keys():
res +='('+str(w)+','+str(self.adjSets[v][w])+')'
res += '\n'
return res
if __name__ == '__main__':
# 测试
adjSet = WeightedGraphImpl("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\weighted-graph.txt", True)
print(adjSet.toString())
5.2环检测
#有向有权图:一旦递归检测到有一个环,直接返回,不在进行递归
from python_code.graph.AdjSet import AdjSet
from python_code.graph.direct.GraphImpl import GraphImpl
class CycleDetection1:
def __init__(self,g:AdjSet):
#
if g is None:
return
#
self.g=g
self.isOnPath=[False]*self.g.getV()#key
self.hasCycle=False
self.visited=[False]*self.g.getV()
#遍历递归
for v in range(self.g.getV()):
if not self.visited[v]:
#key:bool类型
if self.dfs(v,v):
self.hasCycle=True
break
# o(V),o(V)
def dfs(self,v,prev)->bool:
#key
self.isOnPath[v]=True
self.visited[v]=True
for w in self.g.adj(v):
if not self.visited[w]:
#key:一旦递归一环,直接返回
if self.dfs(w,v):
return True
else:
#有向存环(区别于:无向有环w!=prev)
if self.isOnPath[w]:
return True
self.isOnPath[v]=False
return False
def hasCycleInGraph(self):
return self.hasCycle
if __name__ == '__main__':
adjSet = GraphImpl("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\directedgraph-dfs.txt",True)
graphDFS = CycleDetection1(adjSet)
print(graphDFS.hasCycleInGraph())
5.3拓补结构
#方案一:利用有向图的入度信息和BFS
from collections import deque
from python_code.graph.direct.GraphImpl import GraphImpl
class TopologySortBFS:
def __init__(self, g: GraphImpl):
#
if not g.isDirectedGraph():
raise Exception("只能对有向图进行拓扑排序")
#
self.hasCycle = False
self.res=[0]*g.getV()
#key:顶点入度存储
indegrees=[0]*g.getV()
for v in range(g.getV()):
indegrees[v]=g.indegree(v)
#queue:利用有向图的入度信息,进行拓扑‘有向性’存储
queue = deque()
for v in range(g.getV()):
if indegrees[v]==0:
queue.append(v)
#
index = 0
while len(queue):
#初始
curr = queue.popleft()
self.res[index]=curr
index+=1
#相应顶点入度调整
for w in g.adj(curr):
indegrees[w]-=1
if indegrees[w]==0:
queue.append(w)
#判环
if index!=g.getV():
self.hasCycle=True
def isHasCycle(self):
return self.hasCycle
def getRes(self):
return self.res
if __name__ == '__main__':
g = GraphImpl("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\directedgraph-dfs.txt", True)
bfs = TopologySortBFS(g)
print(bfs.isHasCycle())
print(bfs.getRes())
#方案二:DFS回溯的过程,记录的就是拓扑排序(有向性)的逆序
#基于了有向图环检测:一旦递归检测到有一个环,直接返回,不在进行递归
from python_code.graph.AdjSet import AdjSet
from python_code.graph.direct.GraphImpl import GraphImpl
class TopologySortDFS:
def __init__(self,g:AdjSet):
#
if g is None:
return
#
self.g=g
self.res=[0]*g.getV()#key
self.index=len(self.res)-1#key:dfs的回溯是逆序的
self.isOnPath=[False]*self.g.getV()
self.hasCycle=False
self.visited=[False]*self.g.getV()
#遍历递归
for v in range(self.g.getV()):
if not self.visited[v]:
#key:bool类型
if self.dfs(v,v):
self.hasCycle=True
break
# o(V),o(V)
def dfs(self,v,prev)->bool:
#key
self.isOnPath[v]=True
self.visited[v]=True
for w in self.g.adj(v):
if not self.visited[w]:
#key:一旦递归一环,直接返回
if self.dfs(w,v):
return True
else:
#存环:w非v的前一个顶点
if self.isOnPath[w]:#无向:w!=prev:
return True
self.isOnPath[v]=False
#回溯的过程,记录的就是拓扑排序(有向性)的逆序
self.res[self.index]=v
self.index-=1
def isHasCycle(self):
return self.hasCycle
def getRes(self):
return self.res
if __name__ == '__main__':
g = GraphImpl("D:\install by myself\BaiduNetdiskDownload\新建文件夹(桌面)\深度学习总结系列\douma-algo-master\data\directedgraph-dfs.txt", True)
dfs = TopologySortDFS(g)
print(dfs.isHasCycle())
print(dfs.getRes())