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

一、广度优先搜索

      什么是广度优先搜索?   在给定图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])
        return
def 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
    return
a1 = 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,则称为半连通。
           先求解图的所有强连通分支,如果各个分支之间有桥梁边连接,则,该图为半连通
      
  



     

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