数据结构 算法 tarjan算法 图的桥 割点

算法以及相关数据结构介绍

时间戳:dfn[u]表示u节点深度优先遍历的序号
追溯点:low[u]表示u节点或者u的子孙能通过非父子边(深度优先搜索u节点的父亲节点v组成的边uv称为u的父子边)追溯到dfn最小节点的序列号,即节点不走回头路
能够追溯的最早过去

1、无向图的桥
所有点首次访问时间戳和追溯点相等,都是深度优先访问的次序
深度优先搜索满足结束条件时使用访问过的非父节点dfn值更新当前节点的low值,两者取小赋值给当前low
递归调用返回时判断自己节点和父节点low值的小值来更新父节点的low值.注意:此处是深度优先搜索顺序自己的Low值更新父亲Low值;非父子边是不能够用父亲的dfn更新自己的low值,即不能走回头路
递归返回时判断深度优先搜索顺序时孩子的low值大于父亲dfn,则此条边时桥。 即孩子除了父子边外回不到比父亲更早的过去

2、无向图的割点
若节点v不是根,当且仅当搜索树上v存在一个子节点v1满足low[v1]>=dfn[v]
若节点v是根,当且仅当搜索树上有两个子节点满足low[v1]>=dfn[v]

 

下面代码就无向图的桥如何求解进行实现,其中图的存储采用邻接矩阵;无向图割点的求解可以参考桥的实现只是判断条件稍有改动

#!/usr/bin/env python
# -*- coding:utf-8 -*-

graphNodeList = [] #图存储节点信息
graphAdjMatrix = [] #图邻接矩阵
searchNodeInfo = [] #深度优先搜索已经查找过的节点索引
searchResult = [] #深度优先搜索的结果
searchPath = [] #深度优先搜索返回前走过的节点信息
nodeInfoLowDfn = {} #节点值 low值 dfn值的字典,key为nodevalue value为元组,第一个值为dfn第二个为low值


#str切分后给列表赋值格式
def acquireNode():
    node_list = input("请输入途中所有的点,以空格分隔:")
    for nodeInfo in node_list.strip().split(" "):
        graphNodeList.append(nodeInfo)

#根据输入信息填写邻接矩阵
def acquireSideUndig():
    print("请输入图的所有边信息,格式为边的两个顶点,使用空格分隔。 eg:a b,如果全部信息输入完毕请输入end")
    while True:
        tempSide = input(">:")
        if tempSide.strip().lower() == "end":
            print("输入结束")
            break
        tempNodeList = tempSide.strip().split(" ")
        if len(tempNodeList) == 2:
            if tempNodeList[0] in graphNodeList and tempNodeList[1] in graphNodeList:
                createUndigraphAdjMatrix(tempNodeList[0], tempNodeList[1])
            else:
                print("边信息输入有误,请重新输入")
            continue
        else:
            print("输入有误请重新输入")
            continue

def acquireSideDig():
    print("请输入图的所有边信息,格式为边的两个顶点,使用空格分隔。 eg:a b,如果全部信息输入完毕请输入end")
    while True:
        tempSide = input(">:")
        if tempSide.strip().lower() == "end":
            print("输入结束")
            break
        tempNodeList = tempSide.strip().split(" ")
        if len(tempNodeList) == 2:
            if tempNodeList[0] in graphNodeList and tempNodeList[1] in graphNodeList:
                createDigraphAdjMatrix(tempNodeList[0], tempNodeList[1])
            else:
                print("边信息输入有误,请重新输入")
            continue
        else:
            print("输入有误请重新输入")
            continue

#初始化邻接矩阵;注意多维数组初始化格式以及数据的浅拷贝坑
def initGraphAdjMatrix(nodeNum):
    for row in range(nodeNum):
        tempList = []
        for column in range(nodeNum):
            tempList.append(0)
        graphAdjMatrix.append(tempList)

#根据输入顶点信息完成邻接表
def createUndigraphAdjMatrix(node0, node1):
    tempIndex1 = graphNodeList.index(node0)
    tempIndex2 = graphNodeList.index(node1)
    graphAdjMatrix[tempIndex1][tempIndex2] = 1
    graphAdjMatrix[tempIndex2][tempIndex1] = 1

def createDigraphAdjMatrix(node0, node1):
    tempIndex1 = graphNodeList.index(node0)
    tempIndex2 = graphNodeList.index(node1)
    graphAdjMatrix[tempIndex1][tempIndex2] = 1

#pRINT输出空格和换行的写法
def printAdjMatrix(nodeNum):
    for row in range(nodeNum):
        for column in range(nodeNum):
            print(graphAdjMatrix[row][column], end=" ")
        print("")

def mainUndigAdjMat():
    acquireNode()
    initGraphAdjMatrix(len(graphNodeList))
    acquireSideUndig()
    #printAdjMatrix(len(graphNodeList))

def unDigrBridge(nodeu, nodev): #nodeu时nodev的父亲,首次调用nodeu==nodev
    searchNodeInfo.append(graphNodeList.index(nodev))
    #初始化dfn low信息
    nodeInfoLowDfn[nodev] = [len(nodeInfoLowDfn) + 1, len(nodeInfoLowDfn) + 1]
    #遍历孩子节点的所有邻接点,
    for tempnextindex, tempnextvalue  in enumerate(graphAdjMatrix[graphNodeList.index(nodev)]):
        #当邻接矩阵为0表示没有边,直接下一个节点
        if tempnextvalue == 0:
            continue
        #当访问到父亲什么都不做,注意此时的父节点是针对nodev的,而tempnextvalue是nodev的所有邻接点此时判断条件为tempnextvalue=nodeu
        if tempnextindex == graphNodeList.index(nodeu) and tempnextvalue == 1:
            continue
        #符合继续查找桥条件时递归调用,条件是邻接矩阵值为1且此邻接点没有被访问过
        elif tempnextvalue == 1 and tempnextindex not in searchNodeInfo:
            unDigrBridge(nodev, graphNodeList[tempnextindex])
            #下面语句在递归调用返回时执行。1、通过此时通过tempnextindex节点的low值更新nodev节点low值,因为祠堂调用nodev是tempnextindex的父节点
            # 2、检查当前两个边是不是桥
            nodeInfoLowDfn[nodev][1] = min(nodeInfoLowDfn[nodev][1], nodeInfoLowDfn[graphNodeList[tempnextindex]][1])
            if nodeInfoLowDfn[nodev][1] < nodeInfoLowDfn[graphNodeList[tempnextindex]][1]:
                print("%s%s边是桥" % (nodev, graphNodeList[tempnextindex]))
        #此条件是深度优先搜索满足递归结束条件,如果nodev的邻接点已经被访问过则用其dfn值更新nodev的low值
        elif tempnextvalue == 1 and tempnextindex in searchNodeInfo:
            nodeInfoLowDfn[nodev][1] = min(nodeInfoLowDfn[graphNodeList[tempnextindex]][0], nodeInfoLowDfn[nodev][1])



if __name__ == "__main__":
    mainUndigAdjMat()
    #从第一个输入的节点作为根开始搜索,此根节点选择不会影响最终结果
    unDigrBridge(graphNodeList[0], graphNodeList[0])

 

posted @ 2021-01-23 22:53  flag_HW  阅读(210)  评论(1)    收藏  举报