狄克斯特拉算法

来源:互联网 发布:win7公用网络无法点击 编辑:程序博客网 时间:2024/06/18 04:42

狄克斯特拉的作用

在一张图中,每一节点到另外一个节点都有一个权重(权重的意思就像从A节点到B节点需要花费一定的时间,而权重就是这个时间)这是一种用来求两点之间最小总权重路径的算法(当然也可以求最大)。

特殊情况

当某点到另外一点的权重为负时,狄克斯特拉算法无效。为什么会这样我会在文末给出答案。

算法过程描述

提示

开销:本文所提到的开销即为从起点到当前节点的总权重
开销的计算公式:开销 = 父节点的开销 + 权重

第一步

找出最小开销的节点(需要注意的是一开始并不是在图上的所有的点寻找,而是从起点《起点的开销为0》开始寻找,因为后面的子节点的开销你还不知道是多少,所以一开始我们还不知道开销是多少的子节点设为无穷大)

第二步

遍历最小开销节点所指向的所有子节点,并检测最小开销节点到该子节点上的开销是否小于原有的开销,如果小于就用当前开销替换原有开销,并且把该子节点的父节点指向该最小开销节点。(一开始只是最小开销节点指向子节点,子节点并没有指向最小开销节点,子节点指向最小开销节点是为了找到最小开销的同时也能知道他的路径)

第三步

把当前最小开销节点标记为已使用,再次回到第一步,直到所有的节点都被标记为已使用,那么那个时候终点的开销就是最小开销。

具体算法

这是python写的
算法所运算的图如下
这里写图片描述

#-所有子节点(注意变量名结尾带s)childs = {"a":{"b":6,"c":2},         "b":{"d":1},         "c":{"b":3,"d":5},         "d":{}         }#-存储使子节点变成最小开销节点的父节点prent = {}#-当前所有节点开销costs = {         "a":0,         "b":float("inf"), #还不知道到结尾的开销是多少,所以设为无穷大         "c":float("inf"), #还不知道到结尾的开销是多少,所以设为无穷大         "d":float("inf") #还不知道到结尾的开销是多少,所以设为无穷大        } #-存储已经处理过了的节点processed = []#-返回当前最小开销节点def getMinCostNode():    minCostNode = None    minCost = float("inf")    for node in costs: #从当前所有节点开销中寻找        if costs[node] < minCost and node not in processed:            minCost = costs[node]            minCostNode = node     return minCostNodewhile len(costs) != len(processed): #当所有节点都处理后运算就完成了    node = getMinCostNode()    node_childs = childs[node] #获取该节点的所有子节点    node_cost = costs[node] #获取该节点的开销    for node_child in node_childs:#更新他的所有子节点        if node_cost + node_childs[node_child] < costs[node_child]:#如果当前开销小于原来的开销就更新节点            costs[node_child] = node_cost + node_childs[node_child] #更新开销            prent[node_child] = node #更新父节点    processed.append(node) #加入已处理列表print("a到d的最小开销:",costs["d"])print("最小开销路径:d ",end="")node = prent["d"]while node in prent:    print(" ->",node,end="")    node = prent[node]print(" -> a") #a是起点#print("prent:",prent)

运算结果

a到d的最小开销: 6最小开销路径:d  -> b -> c -> a

为什么会算法失效

首先我们先来看这样一张图
我们需要在这张图上利用狄克斯特拉算法计算起点到终点的最小权重。

这里写图片描述

      根据狄克斯特拉算法第一步我们先查找拥有最小开销的节点,由图可知A/B两个节点的开销都是2,均为最小开销节点,所以在这随便拿A作为最小开销节点开始我们的算法。
红色数字表明该权重已经使用
节点上面的数字是该节点当前的开销

这里写图片描述

由图可知A的子节点仅有终点,由此可得当前到终点的权重为5

这里写图片描述

      现在A节点的所有子节点都已经走完了,根据算法(实际上不是)那么就意味着当前A的开销就是A的最小开销,下次我们就不需要再来,我们再次寻找拥有最小开销的节点,那么这次的最小开销节点是B。

这里写图片描述

      当我们来到B节点,我们可以知道B有两个子节点,我们首先走指向A的直接点,那么从起点到B的开销是2,从B到A的开销是-1,所以从起点到A的开销就是1了啊。那么问题来啦,按照狄克斯特拉算法,在刚才我们就已经确定了A的最小开销是2,现在怎么还能找出比2更小的开销1呢,还能有这种操作?
      哈哈哈哈,原因就是从B到A的权重是负值,而我们的狄克斯特拉算法面对这种问题就GG(不行)了。
      那如果B到A的权重是正值呢?你会发现无论是多么大还是多么小的正值,都不能使从B到A的开销小于A的最小开销2。这是因为A节点是在B节点之前取得的最小开销,那么就意味着到A的开销一定是小于等于B的,无论B加多少都不可能小于A。
      还有一些没能醒过来的孩子们会问,那如果一开始我是从B节点在前A节点在后呢。我的回答是:“如果是这样的确能用狄克斯特拉算法找出更加小的最小开销,而我在这只是演示了狄克斯特拉算法中权重为什么不能为负值的原因。“