【数据结构与算法系列】高级篇:赏析《图结构及其算法》(Python版)

《图结构及其算法》

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())
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值