图基本算法介绍:广度优先搜索、深度优先搜索、拓扑排序、强连通分支(算法篇)

时间:2021-01-28 11:44:16

一、广度优先搜索

     什么是广度优先搜索?   在给定图G=(V,E)后和一个特定的源顶点s的情况下,广度优先搜索,系统的探索G中的边,以期发现从s可以到达的所有顶点,并计算s到所有这些可达顶点之间的距离(即最少的边数)    广度优先搜索的作用?个人从定义理解就是,计算出s可以到达的所有顶点,并且计算出到这些顶点的距离(最短路径上的边数,如果边没有权重,这个结果将更有意义)。另一方面,通过广度优先搜索,我们可以判断一个无向图是否是连通图。   广度优先搜索算法:   1、算法导论书上实现BFS算法,本人用python实现。 代码介绍:类G是图的邻接表表示方式,这里是一个有向图,有10个顶点,G的vector数组存储每个顶点可出发的边。BFS函数:color数组存储每个顶点的颜色,parent数组存储每个顶点的父母,distance存储s到该顶点的距离,不可达填None 代码改进:这里可以BFS算法走完后,我们可以通过parent数组打印出,顶点到s的最短路径(可能有多个,在这里我们只能打印出一条,这是一个局限)。         算法分析: 运行时间O(V+E)V是顶点数,E是边数                         额外存储 3*v
class G:
number = 10
vector=[]
    time = 0    def __init__(self):        for n in range( 0,self.number):            print n            self.vector.append( [n*2%10,0])        returndef BFS(G1,s):    color = []    parent = []    distance = []    for n in range(0,G1.number):            color.append("white")            parent.append(None)            distance.append(None)    color[s] = "gray"    parent[s] = None    distance[s] = 0
    que = [](注意这边使用数组做的一个队列:先进先出)    que.append(s)    while len(que) != 0:        u = que[0]        que = que[1:]        for item in G1.vector[u]:            if color[item] == "white":                color[item] = "gray"                distance[item]= distance[u] + 1                parent[item] = u                que.append(item)        color[u] = "black"    print distance    returna1 = G()BFS(a1,1)

2、算法导论习题:如果把迷宫看成一个连通的无向图,设计一条路径,走出迷宫?(各位大虾指教)

     

二、深度优先搜索

        什么是深度优先搜索?简述:对每个可能的分支路径深入到不能再深入为止。                                             算法导论上定义:在深度优先搜索中,对于新发现的顶点,如果他还有以此为起点的而未探测到的边,就沿此边继续搜索下去。当顶点v的所有边都被探索过后,搜索将回朔到发现丁v有起始点的边。这一过程一直进行下去,知道发现从源顶点可达的所有顶点为止。         深度优先搜索的作用?1、深度优先搜索可以进行拓扑排序。  (拓扑排序对有向图意义比较大)2、可以判断一个有向无回图是否有回路 3、对边分类成 树边、反向边、正向边、交叉边(如果一个有向图有反向边,则该图有回路)  1、算法导论的 深度优先搜索算法:
      代码介绍:类G通过邻接表方式标示图(详见上面定义),数组color存储节点颜色,表示节点状态;数组parent存储节点的父母;数组d存储节点的发现时间(发现节点为白色);数组f存储节点搜索结束时间(节点颜色变为黑色)。
       代码改进:通过d、v两个数组,我们能够对G进行拓扑排序
       算法分析:
                           O(v+E)
                           额外存储:4*V
def DFS(G1):
color = []
parent = []
d = []
f = []
for i in range(0,G1.number):
color.append("white")
parent.append(None)
d.append(-1)
f.append(-1)
for n in range (0,G1.number):
if color[n] == "white":
DFS_vist(G1,n,d,f,color,parent)
print d
print f
return
def DFS_vist(G1,n,d,f,color,parent):
color[n] = "gray"
G1.time = G1.time + 1
d[n] = G1.time
for v in G1.vector[n]:
if color[v] == "white":
parent[v] = n
DFS_vist(G1,v,d,f,color,parent)
color[n] = "black"
G1.time = G1.time + 1
f[n] = G1.time
return
a2 = G()
DFS(a2)


2、习题:不采用递归实现DFS算法(可参考广度优先搜索)
  本人实现方法:
def DFS_2(G1):
    G1.time = 0
    color = []
    parent = []
    d = []
    stack = []
    f = []
    for i in range(0,G1.number):
        color.append("white")
        parent.append(None)
        d.append(-1)
        f.append(-1)
        stack.append(i)
    while len(stack) > 0:
        u = stack.pop()
        vNext = None
        if color[u] != "black":
            for v in G1.vector[u]:
                if color[v] == "white":
                   vNext = v
                   break;
        else:
            continue
        if color[u] == "white":
            G1.time = G1.time + 1
            d[u] = G1.time
            color[u] = "gray"
        elif color[u] == "gray" and vNext == None:
            G1.time = G1.time + 1
            f[u] = G1.time
            color[u] = "black"
        if vNext != None:
            color[vNext] = "gray"
            G1.time = G1.time + 1
            d[vNext] =  G1.time
            stack.append(u)
            stack.append(vNext)
    print d
    print f
    return

3、习题:如何证明一个有向图是单连通,即存在一条唯一的简单路径(路径上的顶点不重复),该路径经过所有顶点 。     一种假设解决方法:求出图存在的所有拓扑排序,找出相邻点之间有边(方向前指后)的拓扑排序,如果只有一条这种拓扑排序,则证明是单连通,否则不是。

三、拓扑排序

    什么是拓扑排序?对有向无回图进行拓扑排序后,如果图G包含边(u,v),那么拓扑排序中u在v的前面     拓扑排序的作用?1、可以表达顶点之间的顺序,如果映射到实际情况中,能反应事物之前的先后顺寻(比如,先穿内衣再穿外套) 1、算法导论给出的拓扑排序算法:        对图进行深度优先搜索         在搜索过程中,如果一个顶点搜索结束(颜色变为black),则将顶点插入结果序列的最前端        最后这个结果序列就是图的拓扑排序 2、习题:另一种拓扑排序算法,重复寻找一个入度为0的顶点,将该顶点、以及所有的出边删除。解释如何来实现这一想法,才能使得它的运行时间为O(v+e)?如果G中包含回路,这个算法运行时会发生什么。         算法分析: 计算顶点入度,计算次数 E(边数)         寻找入读为0,比较次数V(顶点数) while循环内部for循环执行次数:E                                         for循环其他部分:V 所以可以得出时间为O(E+v)         额外存储:3*V
def top_sort(G1):
    #初始化入度表为0    inDegree = [0 for i in range(0,G1.number)]
    #计算顶点入度    for n in range(0,G1.number):        for v in G1.vector[n]:            inDegree[v] = inDegree[v] + 1    print inDegree    stack = []
    #将入度为0的压入stack    for n in range(0,G1.number):        if inDegree[n] == 0:            stack.append(n)    print stack    ret = []    while len(stack) != 0:        u = stack.pop()        ret.append(u)        for v in G1.vector[u]:            inDegree[v] = inDegree[v] - 1            if inDegree[v] == 0:                stack.append(v)    print ret

四、强连通分支(概念请参考上一篇)

    1、算法导论给出的求解强连通分支算法:               scc算法 一通过DFS对图G进行拓扑排序 求G的转置图G^t 对转置图按照 第一步计算得出的拓扑顺序进行DFS搜索,每一次搜索发现的顶点组成的就是图G的最强连通分支 算法分析:两次DFS,详见 深度优先搜索介绍      2、在图中加入一条新边后,强连通分支数目如何变化?             如果新边在一个强连通分支内部:无变法     如果新边连接了两个强两通分支:则可能-1,或者不变化      3、证明一个有向图是半连通的,如果对于图G = (V,E),如果对于所有顶点u、v属于V,u可到达v或者v可到达u,则称为半连通。            先求解图的所有强连通分支,如果各个分支之间有桥梁边连接,则,该图为半连通