算法图解第六章笔记与习题(广度优先搜索)

算法图解第六章笔记与习题(广度优先搜索)

文章目录

  • 算法图解第六章笔记与习题(广度优先搜索)
    • 6.1 图(graph)
    • 6.2 广度优先搜索
      • 6.3.1 查找最短路径
      • 6.3.2 队列
    • 6.4 实现图
    • 6.5 实现算法
    • 6.6 小结
    • 练习
        • 习题6.1-6.2:
        • 习题6.3
        • 习题6.4
        • 习题6.5

算法图解pdf百度云链接,提取码:jttg

6.1 图(graph)

**图(graph)模拟一组连接。图由节点(node)边(edge)**组成。其中边还包含有向边和无向边。

算法图解第六章笔记与习题(广度优先搜索)_第1张图片

一个节点可能与众多的节点直接相连,直接相连的节点被称为邻居。在一个图中,可以有许多节点和边。


6.2 广度优先搜索

**广度优先搜索(breadth-first-search,BFS)**是一种用于图的查找算法。可帮助回答两类问题。

  • 第一类问题:从节点A出发,有前往节点B的路径吗?
  • 第二类问题:从节点A出发,前往节点B的哪条路径最短?

对于第一类问题,路径是否存在,广度优先搜索的方式是:

  • 创建包含节点A邻居节点的列表。
  • 检查这些邻居节点是否为节点B。若为节点B,则搜索完成。
  • 若不为节点B,则将该节点其余邻居节点加入到列表中,继续搜索其余节点,直到找到为止。

通过上述的方式,就能遍历整个图中的全部节点,直到搜索到目标节点。这就是广度优先搜索算法。

  • 模拟映射关系;
  • 防止重复;
  • 缓存/记住数据,以免服务器再通过处理来生成它们。

6.3.1 查找最短路径

如果能够按照顺序依次查找被添加到列表中的节点,那么最先找到目标节点的通过的节点就将是最短路径。

为了能将节点按顺序添加,需要使用数据结构队列(queue)



6.3.2 队列

队列的工作原理与现实生活中的队列完全相同。先排队的人将得到优先的处理。通过这种性质,我们就能将节点按顺序添加到队列中,实现广度优先搜索的查找最短路径的功能。

队列类似于栈,不能随机地访问队列中的元素。

队列只支持两种操作:入队出队

队列是一种先进先出(First In First Out,FIFO)的数据结构,而栈是一种后进先出(Last In
**First Out,LIFO)**的数据结构。


6.4 实现图

节点与相邻节点的有向连接用散列表来表示。

算法图解第六章笔记与习题(广度优先搜索)_第2张图片

表示上图这种这种映射关系的python代码如下:

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"] = []

you这个节点被映射到了一个数组,因此graph["you"]是一个数组,包含了you的全部相邻节点。

同时,在定义图的时候,定义的先后顺序不重要。

上图中的图为有向图(directed graph),其中的关系是单向的。如:BOB节点是ANUJ节点的邻居,反之则不是。**无向图(undirected graph)**没有方向,所有直接相邻的节点都互为邻居。


6.5 实现算法

广度优先搜索在图中查找最短路径的原理如下:

  1. 创建一个队列,用于存储要检查的节点。
  2. 从队列中弹出一个节点。
  3. 检查这个节点是否为目标节点。
  4. 如果是,则找到目标节点。如果不是,则将这个节点的所有邻居节点加入队列中。
  5. 若此时队列为空,则目标节点不存在。
  6. 重复2-5。
from collections import deque    # 在python中,可以用deque创建一个双端队列

def is_target_node(name):    # 用于检测目标节点thom的简易方式:判断节点最后一个字符是否为m
      return name[-1] == 'm' 

def search(name):
    search_queue = deque()    # 创建一个队列
    search_queue += graph[name]    # 将当前节点的邻居都加入到这个搜索队列中
    searched = []    # 该数组用于记录检查过的节点,以避免出现死循环
    while search_queue:    # 若队列不为空
        person = search_queue.popleft()    # 弹出队列中第一个节点
        if not node in searched:    # 判断该节点是否被检查过了,若检查过则跳过。
            if is_target_node(node):    # 检查该节点是否为目标节点。
                print(node + " is target node!")    # 若是,则输出节点信息并返回。
                return True
            else:
                search_queue += graph[person]  # 若不是目标节点。将该节点的邻居都压入搜索队列
                searched.append(person)    # 将该节点加入已搜索的数组中
    return False    # 搜索列表为空,循环结束,则没有目标节点,返回失败。

search("you")

程序结果为返回“thom is target node”

广度优先搜索的运行时间为 ( 边 数 顶 点 数 ) O ( 边 数 + 顶 点 数 ) (边数顶点数)O(边数+顶点数) O+,通常写作 ( ) O ( V + E ) ()O(V+E) OV+E。其中v为顶点(vertice),e为边(edge)。


6.6 小结

  • 广度优先搜索指出是否有从A到B的路径。
  • 如果有,广度优先搜索将找出最短路径。
  • 面临类似于寻找最短路径的问题时,可尝试使用图来建立模型,再使用广度优先搜索来解决问题。
  • 有向图中的边为箭头,箭头的方向指定了关系的方向,例如,rama→adit表示rama欠adit钱。
  • 无向图中的边不带箭头,其中的关系是双向的,例如,ross - rachel表示“ross与rachel约会,而rachel也与ross约会”。
  • 队列是先进先出(FIFO)的。
  • 栈是后进先出(LIFO)的。
  • 你需要按加入顺序检查搜索列表中的人,否则找到的就不是最短路径,因此搜索列表必须是队列。
  • 对于检查过的人,务必不要再去检查,否则可能导致无限循环。

练习

习题6.1-6.2:

最短路径的长度均为2,图略。


习题6.3

  • 题干略。

B可行,AC均不可行。


习题6.4

  • 根据下图创建一个可行的列表。

算法图解第六章笔记与习题(广度优先搜索)_第3张图片

一个可行的列表为:起床。刷牙。吃早餐。打包午餐。锻炼。洗澡。穿衣服。


习题6.5

  • 树是一种特殊的图,它没有往后指的边。下面哪个图也是树?

算法图解第六章笔记与习题(广度优先搜索)_第4张图片

AC是树。


你可能感兴趣的:(算法图解)