广度优先搜索(breadth-first search,BFS)

广度优先搜索让你能够找出两样东西之间的最短距离。

在广度优先搜索的执行过程中,搜索范围从起点开始逐渐向外延伸,即先检查一度关系,再检查二度关系。一度关系在二度关系之前加入查找名单。广度优先搜索不仅查找从A到B的路径,而只有按添加顺序查找时找到的是最短的路径。

用队列(queue)这种数据结构来实现。类似于栈,不能随机地访问队列中的元素。队列只支持两种操作:入队和出队

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

广度优先搜索(breadth-first search,BFS)_第1张图片

图由多个节点组成。每个节点都与邻近节点相连.

散列表能够表示节点间的这种关系,将节点映射到其所有邻居。散列表是无序的,因此添加键—值对的顺序无关紧要。压入大致相当于入队,而弹出大致相当于出队。

如果你在你的整个人际关系网中搜索芒果销售商,就意味着你将沿每条边前行(记住,边是从一个人到另一个人的箭头或连接),因此运行时间至少为O(边数)。

  • 首先,创建一个队列。在Python中,可使用函数deque来创建一个双端队列
'''从你的关系网中查找一个芒果销售商  '''
from collections import deque
search_queue = deque()  ←------------创建一个队列
search_queue += graph["you"]  ←------将你的邻居都加入到这个搜索队列中 ,graph["you"]是一个包含你的所有邻居的数组,
while search_queue:  ←------只要队列不为空    
    person = search_queue.popleft()  ←------就取出其中的第一个人    
    if person_is_seller(person):  ←------检查这个人是否是芒果销售商        
        print(person + " is a mango seller!")  ←------是芒果销售商        
    return True    
    else:        
        search_queue += graph[person]  ←------不是芒果销售商。将这个人的朋友都加入搜索队列
return False  ←------如果到达了这里,就说明队列中没人是芒果销售商

算法将不断执行,直到满足以下条件之一:

  • 找到一位芒果销售商;
  • 队列变成空的,这意味着你的人际关系网中没有芒果销售商。

检查完一个人后,应将其标记为已检查,且不再检查他。如果不这样做,就可能会导致无限循环。可使用一个列表来记录检查过的人。将一个人添加到队列需要的时间是固定的,即为O(1),因此对每个人都这样做需要的总时间为O(人数)。广度优先搜索的运行时间为O(人数 + 边数),这通常写作O(V + E),其中V为顶点(vertice)数,E为边数。

完整代码如下:

def search(name):    
    search_queue = deque()    
    search_queue += graph[name]
    searched = []  ←------------------------------这个数组用于记录检查过的人    
    while search_queue:        
        person = search_queue.popleft()        
        if person not in searched:     ←----------仅当这个人没检查过时才检查                
            if person_is_seller(person):                
                print(person + " is a mango seller!")              
                return True            
            else:                
                search_queue += graph[person]                
                searched.append(person)    ←------将这个人标记为检查过    
    return False
search("you")

如果任务A依赖于任务B,在列表中任务A就必须在任务B后面。这被称为拓扑排序,使用它可根据图创建一个有序列表。

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

 

  • 广度优先搜索指出是否有从A到B的路径,如果有,广度优先搜索将找出最短路径。广度优先搜索用于在非加权图中查找最短路径。
  • 面临类似于寻找最短路径的问题时,可尝试使用图来建立模型,再使用广度优先搜索来解决问题。
  • 有向图中的边为箭头,箭头的方向指定了关系方向;无向图中的边不带箭头,其中的关系是双向的。
  • 图中有负权边时,不能使用迪克斯特拉算法,该算法假设:对于处理过的节点,没有前往该节点的更短的路径。
  • 仅当权重为正时狄克斯特拉算法才管用。
  • 对于含有负权边的图,可用贝尔曼-福德算法求解。

广度优先搜索(breadth-first search,BFS)_第2张图片

 

 

''' 实现狄克斯特拉算法的Python代码 ;每个节点都有开销。开销指的是从起点前往该节点需要多长时间。
在这里的计算从起点出发,经节点B前往节点A(而不是直接前往节点A)需要多长时间。
'''
node = find_lowest_cost_node(costs)  ←------在未处理的节点中找出开销最小的节点
while node is not None:  ←------这个while循环在所有节点都被处理过后结束    
    cost = costs[node]    
    neighbors = graph[node]    
    for n in neighbors.keys():  ←------遍历当前节点的所有邻居        
        new_cost = cost + neighbors[n]        
        if costs[n] > new_cost:  ←------如果经当前节点前往该邻居更近,            
            costs[n] = new_cost  ←------就更新该邻居的开销            
            parents[n] = node  ←------同时将该邻居的父节点设置为当前节点 
    processed.append(node)  ←------将当前节点标记为处理过    
    node = find_lowest_cost_node(costs)  ←------找出接下来要处理的节点,并循环
'''   完整代码'''
def find_lowest_cost_node(costs):    
    lowest_cost = float("inf")    
    lowest_cost_node = None    
    for node in costs:  ←------遍历所有的节点        
        cost = costs[node]        
        if cost < lowest_cost and node not in processed:  ←------如果当前节点的开销更低且未处理过,            
        lowest_cost = cost  ←------就将其视为开销最低的节点            
        lowest_cost_node = node    
    return lowest_cost_node

 

参考:《算法图解》   

 

 

你可能感兴趣的:(广度优先搜索(breadth-first search,BFS))