图:图的遍历

图的遍历


和树的遍历一样,我们希望从图中某一顶点出发遍历图中其余顶点,且使的每一个顶点仅被访问一次。这一过程称之为图的遍历
图的遍历算法是求解图的连通性、拓扑排序和求解关键路径等算法的基础。
图的遍历通常有两种方法:广度优先搜索(BFS)深度优先搜素(DFS);他们对 无向图有向图 都适用。


广度优先搜索(BFS)


在这里插入图片描述

广度优先搜索(BFS): 类似于树的按层次遍历过程。其算法过程如下:

  • 假设从图中某个 V 1 V_1 V1 出发,在访问了 V 1 V_1 V1 之后,依次访问 V 1 V_1 V1 的各个未曾访问的相连点 { $V_2、V_6、V_9、V_{12} $ };
  • 然后分别从这些相连点出发依次访问它们的连接点,直至所有的连接点被访问到;访问顺序如下:
    • { V 3 , V 4 , V 5 } \{ V_3, V_4, V_5 \} {V3,V4,V5}
    • { V 7 , V 8 } \{ V_7, V_8 \} {V7,V8}
    • { V 1 0 , V 1 1 } \{ V_10, V_11 \} {V10,V11}
    • { V 1 3 , V 1 4 } \{ V_13, V_14 \} {V13,V14}

假设有如下的一个有向图,我们需要检索从"you" 能否找到 "mongo":

在这里插入图片描述

我们用字典来表示如上有向图:

graph = {}
graph["you"] = ["alice", "bob", "claire"]
graph["bob"] = ["anuj", "peggy"]
graph["alice"] = ["peggy"]
graph["claire"] = ["thom", "jonny"]
graph["anuj"] = []
graph["peggy"] = []
graph["thom"] = []
graph["jonny"] = []

算法的实现原理:

在这里插入图片描述

代码实现如下:

from collections import deque
search_queue = deque() // 创建一个双端队列
search_queue += graph["you"] // 将顶点 "you" 的邻居加入到这个搜索队列

while search_queue: // 只有队列不为空
    person = search_queue.popleft() // 就取出第一个人
    if person == "mango": # 检查这个节点是不是 "mongo"
        print(person + " is a mango seller!") # 是 "mongo"
        return True
    else:
        search_queue += graph[person] // 将这个人的邻居加入搜索队列
return False # 如果到达这里,就说明队列中没有 "mongo"

上述代码存在一个问题,即有些节点被多次添加队列中,例如 “PEGGY”,即会存在多次访问一个节点的情况;如果出现,会造成无限循环访问部分节点的情况,如下所示:

在这里插入图片描述

检查一个人之前,要确认之前没检查过他,这很重要。为此,你可使用一个列表来记录检查过的人。
上述代码修改为如下:

from collections import deque
def BFSearch(name): // 从节点 name 处开始访问
    search_queue = queue()
    search_queue += graph[name] 
    searched = [] // 这个数组用于记录检查过的人
    while search_queue:
        person = search_queue.popleft() # 取出队列中第一个节点
        if person not in searched: # 仅当这个人没检查过时才检查
            print(person + " is a mango seller!")
            return True
        else:
            search_queue += graph[person] // 将这个人的邻居加入搜索队列
            searched.apped[person] # 将这个人标记为检查过
    return False

BFSearch("you") # 从节点 "you" 开始搜索

上述代码并不适合含有多个子图的图结构。

运行时间

如果你在你的整个人际关系网中搜索 “mongo”,就意味着你将沿每条边前行(记住,边是从一个节点到另一个节点的箭头或连接),因此运行时间至少为 O ( 边 数 ) O(边数) O()
你还使用了一个队列,其中包含要检查的每个人。将一个人添加到队列需要的时间是固定的,即为 O ( 1 ) O(1) O(1),因此对每个人都这样做需要的总时间为 O ( 人 数 ) O(人数) O()。所以,广度优先搜索的运行时间为 O ( 人 数 + 边 数 ) O(人数 + 边数) O(+),这通常写作 O ( V + E ) O(V + E) O(V+E),其中 V V V 为顶点(vertice)数, E E E 为边数。


深度优先搜索


在这里插入图片描述

深度优先搜索是优先从一个节点的深度进行搜索,这个过程需要用到栈;
算法的实现过程如下:

序号操作
1创建一个栈,用来存储节点“you”
2从栈中取出一个节点,例如:“you”
3判断节点是否符合要求,如果符号要求,则退出;如果不符合要求,进入 5 5 5
4不符合要求,把此节点的邻居加入栈“alice”
“bob”
“claire”
5回到第二步“alice”
“bob”
“claire”
6如果栈空,说明并没有想要的节点None

代码实现如下:

def DFSearch(name): // 从节点 name 处开始访问
    search_stack = [] # 用列表实现栈
    search_stack = graph[name] + search_stack # 给栈添加元素
    searched = [] // 这个数组用于记录检查过的人
    while search_stack:
        person = search_stack.pop(0) # 取栈顶节点
        if person not in searched: # 仅当这个人没检查过时才检查
            print(person + " is a mango seller!")
            return True
        else:
            search_stack = graph[person] + search_stack // 将这个人的邻居加入栈中
            searched.apped[person] # 将这个人标记为检查过
    return False

DFSearch("you") # 从节点 "you" 开始搜索

上述代码并不适合含有多个子图的图结构。

时间复杂度 O ( V + E ) O(V+E) O(V+E)


小结


  1. 图的搜索指出是否有从节点 A A A 到节点 B B B 的存在路径;
  2. BFS 用一个队列来维护待检查节点;DFS 用一个栈来维护待检查节点;
  3. 对于检查过的人,务必不要再检查,否则可能导致无限循环;

参考资料


  1. 《算法新解》
  2. 《数据结构(C语言版)》
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值