《算法导论3rd第二十二章》基础的图算法

前言

图算法对于计算机学科至关重要。成百上千的计算问题最后都可以归约为图论问题。本章主要介绍图的表示和图的搜索。

图的表示

对于图G=(V,E)可以用两种标准表示方法表示:

  1. 邻接链表,因为在表示稀疏图(边的条数|E|远远小于 ∣ V ∣ 2 |V|^2 V2的图)时非常紧凑而成为通常的选择。
  2. 邻接矩阵,在稠密图(|E|接近 ∣ V ∣ 2 |V|^2 V2的图)的情况下,倾向于该表示法。

邻接链表表示由一个包含|V|条链表的数组Adj所构成,每个结点有一条链表。对于每个结点,邻接链表Adj[u]包含所有与结点u之间有边相连的结点v,即Adj[u]包含图G中所有与u邻接的结点。
在这里插入图片描述

  • 邻接链表的表示法也有着潜在的不足之处是无法快速判断一条边(u, v)是否是图中的一条边。唯一的办法是在Adj[u]里面搜索v结点。邻接矩阵则客服的了这个缺陷,但是付出的代价是更大的存储空间。
  • 对于无向图来说,邻接矩陈还有一个优势:第个记录项只需要1位的空间

广度优先搜索

对于图G,广度优先搜索算法需要在发现所有距离源结点s为k的所有结点之后,才会发现距离源结点s为k+1的其他结点。
在这里插入图片描述

# u.d记录的是广度优先搜索算法所计算出的从源点s到结点u之间的距离。
# u.π记录u的前驱结点。
BFS(G, s)
    for each vertex u∈G.V - {s}
        u.color = White
        u.d = ∞
        d.π = NIL
    s.color = Gray
    u.d = 0
    d.π = NIL
    Q = ∅
    EnQueue(Q, s)
    while Q != ∅
        u = DeQueue(Q)
        for each v∈G.Adj[u]
            if v.color == White
                v.color = Gray
                v.d = u.d + 1
                v.π = u
                EnQueue(Q, v)
        u.color = Black

广度优先搜索算法可以得到从源顶点s到所有可达顶点的最短距离v.d。同时,该算法能够构造出广度优先树。下面的代码为打印一颗广度优先树中,源节点s到任意结点v的路径:

PRINT-PATH(G, s, v)
   if v == s
      print s
   else  if v.π == NIL
      print “nopath from” s “to” v “exists”
   else PRINT-PATH(G, s, v.π)
      print v

深度优先搜索

对于图G,深度优先搜索总是对最近才发现的结点v的出发边进行探索,直到该结点的所有出发边都被发现为止。一旦结点v的所有出发边都被发现,搜索则“回溯”到v的前驱结点,来搜索前驱结点的出发边。
在这里插入图片描述

# u.d表示u结点开始搜索的时间 
# u.f表示u结点结束搜索的时间 
DFS(G)
    for each vertex u∈G.V
        u.color = White
        u.π = NIL
    time = 0
    for each vertex u∈G.V
        if u.color == White
            DFS-Visit(G, u)

DFS-Visit(G, u)
    time = time + 1         // white vertex u has just been discovered
    u.d = time
    u.color = Gray
    for each v∈G.Adj[u]     // explore edge(u, v)
        if v.color == White
            v.π = u
            DFS-Visit(G, v)
    u.color = Black         // black u, it is finished
    time = time + 1
    u.f = time

深度优先搜索算法的运行时间为θ(V+E)。

深度优先搜索的性质

深度优先搜索生成的前驱子图可能由多棵树组成,因为搜索可能从多个源结点重复进行。即深度优先森林
深度优先搜索的一个重要特性是发现和完成时间具有括号结构
在这里插入图片描述

图b是图a根据发现时间和结束时间的时序

对于图G,根据发现结速时间,总结三大性质:

  • 性质1:在对有向图或无向图G = (V, E)进行的任意DFS过程中,对于任意两个节点u和v来说,下面三种情况只有一种成立:
    • a:区间[u.d, u.f]和区间[v.d, v.f]完全分离,在深度优先森林中,节点u不是节点v的后代,节点v也不是节点u的后代。
    • b:区间[u.d, u.f]包含在区间[v.d, v.f]中,在深度优先树中,节点u是节点v的后代。
    • c:区间[v.d, v.f]包含在区间[u.d, u.f]中,在深度优先树中,节点v是节点u的后代。
  • 性质二:在有向或无向图G的深度优先森林中,节点v是节点u的真后代当且仅当u.d < v.d <v.f < u.f成立。
  • 性质三:在有向或无向图G = (V, E)的深度优先森林中,节点v是节点u的后代,当且仅当在时间u.d,存在一条从节点u到节点v的全部由白色节点所构成的路径。
边的分类

深度优先搜索的另一个有趣的性质是,可以通过搜索来对输入图G=(V, E)的边进行分类:

在这里插入图片描述

  1. 树边:为深度优先森林Gπ中的边。如果结点v是因算法对边(u, v)的探索而首先被发现,则(u, v)是一条树边。
  2. 前向边(上图灰色虚线):从某个点到它的某个子孙节点的边。这种边相当于提供某种“捷径”,在这个问题里不太重要,即使把它们全部删去,对于连通性也没什么影响。
  3. 后向边(上图绿色虚线):从某个点到它的某个祖先节点的边。这种边就是产生环的原因,如果删去所有反向边,那么原图会成为有向无环图。
  4. 横叉边(上图蓝色虚线):从某个点到一个既非它子孙节点、也非它祖先节点的边。这种边本身不产生环,但是它可能把两个强连通子图“连接”起来,形成一个更大的强连通子图。

反向边和横叉边都有一个特点:起点的dfs序必然大于终点的dfs序。

性质四:在对无向图G进行DFS时,每条边要么是树边,要么是后向边

拓扑排序

对于一个有向无环图G = (V,E)来说,其拓扑排序是G中所有结点的一种线性次序,该次序满足如下条件:如果图G包含边(u, v),则结点u在拓扑排序中处于结点v的前面。
在这里插入图片描述
拓扑排序就是将节点按完成时间进行降序排序

Topological-Sort(G)
    call DFS(G) to compute finishing times v.f for each vertex v as each vertex is finished, insert it onto the front of a linked list
    return  the linked list of vertices

强连通分量

如果一个有向图中任意两个顶点互相可达,则称该有向图是强连通的。
有向图G=(V, E)的强连通分量是一个最大节点集合 C ∈ V C\in V CV,对于该集合中的任意一对节点u和v来说,路径u->v和路径v->u同时存在,即节点u和v可以相互抵达.
在这里插入图片描述
Kosaraju算法其实比较容易理解,它使用了两次DFS,算法的主要过程也就是这两次DFS:

  1. 第一次DFS计算完成时间的反序(实际上就是拓扑排序)。
  2. 第二次在图G转置图Gt上,按照第一次DFS计算出的顺序进行DFS。

在这里插入图片描述
主要利用“强连通分量”在转置图中,依旧是“强连通分量”。利用DFS分别计算图G与图Gt的连通性,然后取交集。

主要过程:《Kosaraju强连通子图算法

主要参考

Elementary Graph Algorithms
算法学习笔记(69): 强连通分量
Kosaraju强连通子图算法
强连通分量

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值