《算法图解》学习笔记习题和代码(第六章 广度优先搜索)Python3

目录

6.1 图简介 

6.2 图是什么 

6.3 广度优先搜索 

6.3.1 查找最短路径 

6.3.2 队列 

 练习1

6.4 实现图 

 6.5 实现算法 

实现图和广度优先搜索代码

广度优先搜索的运行时间

练习

6.6 小结 


第六章  广度优先搜索

用新的数据结构图来建立网络模型。

学习广度优先搜索。(第一种图算法——广度优先搜索(breadth-first search,BFS)。 )

学习有向图和无向图。

学习拓扑排序,这种排序算法指出了节点之间的依赖关系。

 

广度优先搜索让你能够找出两样东西之间的最短距离,不仅仅是狭义上的距离,含义很多:

6.1 图简介 

假设你要从双子峰去金门大桥,为找出换乘最少的乘车路线,你将使用什么样的算法? 

 接下来按照下面这个思路分析:

一步能到达金门大桥吗?不能。那标出一步能到达的地方。

 两步能到达金门大桥吗?标出两步能到达的地方。

 三步能到达金门大桥吗?可以。那么到达金门大桥换乘最少的路线已经找出来,需要三步。其他能到达金门大桥的路线会更远,需要四步。

这种问题被称为最短路径问题(shorterst-path problem)。解决最短路径问题的算法被称为广度优先搜索。 

确定如何从双子峰前往金门大桥,需要两个步骤。 
(1) 使用图来建立问题模型。 
(2) 使用广度优先搜索解决问题。

6.2 图是什么 

图用于模拟不同的东西是如何相连的。

绘制一个欠钱图。比如下图表示Alex欠Rama钱。 

完整的欠钱图可能类似于下面这样:

 图由节点(node)和(edge)组成。 一个节点可能与众多节点直接相连,这些节点被称为邻居。图用于模拟不同的东西是如何相连的。

6.3 广度优先搜索 

广度优先搜索是一种用于图的查找算法,可帮助回答两类问题。 

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

解决第一类问题,有没有路径,例子:

我想看我的朋友有没有人是芒果经销商,我就列一个朋友清单,一个一个找。

如果没有朋友是芒果经销商,那就在朋友的朋友中找。在检查每个朋友时,如果他不是,就把他的朋友也加入到查找清单。

不仅在朋友中查找,还在朋友的朋友中查找,使用这种算法将搜遍你的整个人际关系网,直到找到芒果销售商。这就是广度优先搜索算法。  

6.3.1 查找最短路径 

解决第二类问题:最短路径,例子:

谁是关系最近的芒果售商。例如,朋友是一度关系,朋友的朋友是二度关系。

一度关系胜过二度关系,二度关系胜过三度关系,以此类推。因此,你应先在一度关系中搜索,确定其中没有芒果销售商后,才在二度关系中搜索。广度优先搜索就是这样做的。

广度优先搜索不仅查找从A到B的路径,而且找到的是最短的路径。 

只有按添加顺序查找时,才能实现这样的目的。有一个可实现这种目的的数据结构,那就是队列(queue)。 

6.3.2 队列 

队列类似于栈,你不能随机地访问队列中的元素。队列只支持两种操作:入队和出队。 

先加入的元素将在后加入的元素之前出队。(先加入的元素先出队)

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

 练习1

对于下面的每个图,使用广度优先搜索算法来找出答案。 
6.1 找出从起点到终点的最短路径的长度。 

答:最短路径长度为2。

6.2 找出从cab到bat的最短路径的长度。  

答:最短路径长度为2。 

6.4 实现图 

用代码来实现图。

每个节点都与邻近节点相连,如果表示类似于“你→Bob”这样的关系呢?要将节点映射到其所有邻居。,如下图:

表示这种映射关系的Python代码如下:

graph = {}
graph['you'] = ["alice", "bob", "claire"]

graph["you"]是一个数组,其中包含了“你”的所有邻居。  

那更大的图怎么表示,用代码表示下面这张图就是:

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

有向图和无向图

有向图单箭头,关系是单向的。无向图没有箭头,直接相连的节点互为邻居。例如,下面两个图是等价的。 

 6.5 实现算法 

这种算法的工作原理。:

 这就是入队出队,也叫作压入弹出。

首先,创建一个队列。在Python中,可使用函数deque来创建一个双端队列。 

我们来编写查找芒果商的代码

实现图和广度优先搜索代码

#python 内置函数创建队列
#在Python中,可使用函数deque来创建一个双端队列。 
#创建关系图
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

def search(name):
    search_que = deque()
    search_que += graph[name]
    searched = [] #用于记录检查过的人
    while search_que:           #只要search_que不为空
            person = search_que.popleft()    #取出队列第一个人
            if not person in searched:     
                if person_is_seller(person):      #判断是不是芒果商的函数
                    print(person+" is a seller")      #是芒果商
                    return True
                    
                else:
                    search_que += graph[person]   #不是芒果商就把他的所有朋友加入搜索队列
                    searched.append(person)
    return False                         #如果运行到这里,就说明队列中没有芒果商

def person_is_seller(name):
    return name[-1] == 'm'          #我们这里假设以m结尾的姓名,是芒果商 
search('you')

OUT: 

thom is a seller

True

广度优先搜索的运行时间

要在人际关系网中沿每条边搜索芒果商,运行时间至少为O(边数)。

当遇到不是芒果商的人,还要把他认识的人再加入到搜索队列中。将一个人添加到队列需要的时间是固定的,即为O(1),因此对每个人都这样做需要的总时间为O(人数)。

所以,广度优先搜索的运行时间为O(人数 + 边数),这通常写作O(V + E),其中V为顶点(vertice)数,E为边数。 

练习

下面的小图说明了我早晨起床后要做的事情。 

 该图指出,我不能没刷牙就吃早餐,因此“吃早餐”依赖于“刷牙”。 
另一方面,洗澡不依赖于刷牙,因为我可以先洗澡再刷牙。根据这个图,可创建一个列表,指出我需要按什么顺序完成早晨起床后要做的事情: (1) 起床 (2) 洗澡 (3) 刷牙 (4) 吃早餐 

请注意,“洗澡”可随便移动,因此下面的列表也可行: 
(1) 起床 (2) 刷牙 (3) 洗澡 (4) 吃早餐 

6.3 请问下面的三个列表哪些可行、哪些不可行? 

答:A不可行,吃早餐依赖于刷牙。C不可行,洗澡依赖于先起床。

6.4 下面是一个更大的图,请根据它创建一个可行的列表。 

答:1.起床 2.刷牙 3.锻炼 4.洗澡5. 穿衣服 6. 吃早餐 7.打包午餐

课后答案: 1——起床,2——锻炼,3——洗澡,4——刷牙,5——穿衣服,6——打包午餐,7——吃早餐。

 

从某种程度上说,这种列表是有序的。如果任务A依赖于任务B,在列表中任务A就必须在任务B后面。这被称为拓扑排序,使用它可根据图创建一个有序列表。假设你正在规划一场婚礼,并有一个很大的图,其中充斥着需要做的事情,但却不知道要从哪里开始。这时就可使用拓扑排序来创建一个有序的任务列表。  

假设你有一个家谱。 

这是一个图,因为它由节点(人)和边组成。其中的边从一个节点指向其父母,但所有的边都往下指。在家谱中,往上指的边不合情理!(如下图)因为你父亲不可能是你祖父的父亲! 

这种图被称为树。树是一种特殊的图,其中没有往后指的边。 

6.5 请问下面哪个图也是树? 

 答:A,C是树,B有往上指的。C是一棵横着的树。树是图的子集,因此树都是图,但图可能是树,也可能不是。 

6.6 小结 

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章