《算法图解》六、七

广度优先搜索

可用于求解从A到B有无路径及最短路径问题,从一个节点出发,寻找该节点的邻居,遍历结束后如果没有达到目的,继续查找邻居的邻居。这里用到一种数据结构–队列
情景:你是一个果农,需要在你的人际交往网找到一位水果的销售商。

from collections import deque

def search(name):
    search_queue = deque()
    search_deque += graph[name]        #graph是个散列表
    searched = []                      #用于记录已搜索的节点
    
    while search_deque:
        person = search_queue.popleft()
        if not person in searched:
            if person_is_seller(person):
                print(person+"is a seller.")
                return True
            else:
                search_queue+=graph[person]
                searched.append(person) 
    return False

狄克斯特拉算法

使用了广度优先搜索来查找两点之间的最短路径,那时“最短路径”的意思是 段数最少。在狄克斯特拉算法中,你给每段都分配了一个数字或权重,因此狄克斯特拉算法找出的是总权重最小的路径。即要计算非加权图中的最短路径,可使用广度优先搜索。要计算加权图中的最短路径,可使用狄克斯特拉算法。
注意:狄克斯特拉算法只适用于有向无环图,如果有负边,也不能使用狄克斯特拉算法。
狄克斯特拉算法包含4个步骤。
(1) 找出最便宜的节点,即可在最短时间内前往的节点。
(2) 对于该节点的邻居,检查是否有前往它们的更短路径,如果有,就更新其开销。
(3) 重复这个过程,直到对图中的每个节点都这样做了。
(4) 计算最终路径。
换钢琴问题
Rama就能拿乐谱换架钢琴。现在他需要确定的是,如何花最 少的钱实现这个目标。我们来绘制一个图,列出大家的交换意愿
在这里插入图片描述
创建一个表格,在其中列出每个节点的开销。这里的开销指的是达到节点需要额外支付多少钱。在执行狄克斯特拉算法的过程中,你将不断更新这个表。为计算最终路径,还需在这个表中 添加表示父节点的列
第一步: 找到最便宜的节点。
在这里插入图片描述
第二步:计算前往该节点的各个邻居的开销
在这里插入图片描述
再次执行第一步:下一个最便宜的节点是黑胶唱片——需要额外支付5美元。 再次执行第二步:更新黑胶唱片的各个邻居的开销。
在这里插入图片描述
下一个最便宜的是吉他,因此更新其邻居的开销
在这里插入图片描述
最后,对最后一个节 点——架子鼓,做同样的处理。
在这里插入图片描述
如果用架子鼓换钢琴,Rama需要额外支付的费用更少。因此,采用最便宜的交换路径时, Rama需要额外支付35美元。
负边时
是因为狄克斯特拉算法这样假设:对于处理过的节点,没有前往该节点的更短路径。 因此,不能将狄克斯特拉算法用于包含负权边的图。在包含 负权边的图中,要找出最短路径,可使用另一种算法——贝尔曼福德算法(Bellman-Ford algorithm)。

实现

以下免得图为例,找从七点到终点的最短路径
在这里插入图片描述
数据结构:存储这个图需要一个散列表;记录每个节点的花费需要一个散列表;记录节点的父节点需要一个散列表;记录一个访问过的点需要一个 数组。在这里插入图片描述
首先建立Graph,这里不但要存储每个节点的邻居节点,还要记录他们到邻居节点的花销,所以

graph["start"] = {} 
graph["start"]["a"] = 6
graph["start"]["b"] = 2 

graph["a"] = {} 
graph["a"]["fin"] = 1 

graph["b"] = {} 
graph["b"]["a"] = 3 
graph["b"]["fin"] = 5 

graph["fin"] = {} 

接下来,需要用一个散列表来存储每个节点的开销。在Python中表示无穷大:infinity = float(“inf”)
创建开销表的代码如下:

infinity = float("inf") 
costs = {} 
costs["a"] = 6 
costs["b"] = 2 
costs["fin"] = infinity 

创建这个散列表的代码如下:

parents = {} 
parents["a"] = "start" 
parents["b"] = "start" 
parents["fin"] = None 

最后,你需要一个数组,用于记录处理过的节点,因为对于同一个节点,你不用处理多次processed = []
算法流程图:
在这里插入图片描述
代码:

node = find_lowest_cost_node(costs)        # 寻找花销最少的节点
while node is not None:
     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 

小结

 广度优先搜索用于在非加权图中查找最短路径。
 狄克斯特拉算法用于在加权图中查找最短路径。
 仅当权重为正时狄克斯特拉算法才管用。
 如果图中包含负权边,请使用贝尔曼 福德算法

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值