写给媳妇儿的算法(十二)——狄克斯特拉算法

在我们寻找身边的人际关系的时候,可以采用上一篇的广度优先搜索来进行查找。当我们走出门去,想要到某个地方的时候,这种场景我们安排路径就更加的适合使用 狄克斯特拉算法

狄克斯特拉算法是应用于权值为 加权有向无环图 寻找最短路径的一种算法。

算法过程

加权有向无环图

什么是加权有向无环图呢?形如:

Start到End的所有路径图.png

这个就是我们的主角,加权有向无环图。如果你的家住在 Start 处,你想出门去一趟 End,过程中的所有你能走的路径都以箭头的方式标注在图中,所有路径的距离都以数字的方式标注在路径的边上,那么你能找到一条最短的从Start到End的路径吗?

狄克斯特拉算法

我们跟着狄克斯特拉算法的过程,来看狄克斯特拉算法的解决问题的思路。

我们需要一些工具,来完成这个算法:

  • 一张记录到达中间地点距离的表格(距离表)
  • 一张记录到达该地点的之前的一个节点的表格(父地点表)
  • 一张记录所有能走的路径的表格(路径图)
  1. 站在 Start 处,我们此时只有两条路可以走:A、B,此时我们的这三个图表分别是:


    站在Start处.png
  2. 继续找,我们的宗旨是,每次都根据距离表,找最短的路径。比如现在,我们找完了Start,查看一下距离表。此时,距离表告诉我们,此时表中,我们“记录在册”的地点只有两个,A、B,而这两个地点最近的那个是B,距离只有3,所以,我们选择去B。B地点能到达的点只有两个:C、E,此时的三张图表分别为:


    站在B处.png

解释一下图中的意思,距离表中,除了我们第一次确定距离的A、B两个点之外,我们从路径图中又得到了可以经由B到达的B点的两个邻居:C、E两点。最终的结果就是,我们从Start(最开始)经由B点所到达的C、E两点的距离分别是:
C:Start -> B -> C(3 + 1 = 4)(到达B点的距离3加上B点到C点的距离1)
D:Start -> B -> E (3 + 6 = 9)(到达B点的距离3加上B点到E点的距离6)

我们将这个距离写入了距离表,就得到了现在的距离表。而C、E两点是从B点走过来,所以C、E两点的父地点表都写入对应的B地点。

此时,B点所能到达的所有点都记录在了距离表中,所以B点的使命完成,我们把B点标记了一个 X ,表示已经处理完毕。

3.此时我们站在了B点,并且将B点所能到达的地点都“记录在册”。此时我们继续寻找路径。我们再次查看距离表(2中的距离表),此时,距离最短的点是C(因为B点已经标记为已处理,所以C点的4的距离是最短的),所以,我们去到C点,C点能到达的点只有End,所以,站到C点之后,我们继续更新三个图表:


站在C处.png

跟上面一样,所有C点能到达的点,我们都记录在册了,所以,C的使命完成了,我们把C标记一个 X。

4.我们继续寻找。还是老规矩,我们依然查看距离表,此时,距离表上最短的距离是到达A点的距离5,所以,我们来到A点。A点所能到达的是C、D两点,我们分别登记入册,所以现在的三个图表分别是:


站在A处.png

由于之前我们已经处理过C点,所以,即使现在C点还可以到达,但是已经不是最近的可到达距离了(C点到达的最短距离是之前的经由B的距离4,现在经由A到达C的距离是5+2 = 7)。所以我们就不再去记录C了(不再记录已经处理过的点)。

5.我们继续找。还是查看距离表,此时,距离表中,能到达的最近的点是D点(注意:距离表中的距离指的都是从起点到达该点的距离,是7)。此时我们来到D点,D点能到达的点是:C、End,此时我们登记入册(C已经处理过,不入册)我们得到的三个图表分别是:


站在D处.png

C点已经处理过了,我我们就不再考虑经由D点去到C点了。我们来看经由D点到达End点,此时,距离表中,从Start可以到达End的距离已经有了一个9,我们此时需要比较一下,经由D到达End的距离是:7 + 4 = 11 < 9。距离表中的距离要小于现在这条经由D点到End的路径,所以,我们不用更新End点的路径和父地点表。但是D点所有能到到达的地方都已经处理完了,所以我们给D打一个X。

  1. 我们继续找。此时表中除了最终到达的点End之外,只有一个E点了。所以此时最近能到达的点就只有E了,我们来到E点。E点所能到达的点只有:End。我们此时来更新一下距离表(比较距离,看是否需要更新):


    站在E处.png

此时,我们可以看到,E点到达End点的距离是1,所以经由E点到达End点的距离是: 9(到达E点的距离)+ 1(E点到End点的距离) = 10 > 9(表中到达End点的距离),所以我们不用跟新这个距离跟父节点,但是E点所能到达的所有邻居节点已经处理完毕了,所以我们把E点标记为 X。

最终,我们已经处理完毕了图中所有从Start到达End点所经由的点。我们现在查看距离表,此时到达End的距离是9,所以最终所有路径中能到达End点的最小距离就是9。

还有一点,我们要找到这条路径。这点好办,我们查看最终的父地点表,End的父地点是C,我们此时记下: End -> C。我们在看C的父地点是B,此时我们完善记录:End -> C -> B。还没有到Start,我们继续找B的父地点,父地点是:Start!我们终于找到头了,我们记录:End -> C -> B -> Start。此时,我们翻转这条逆向回溯的路径:Start -> B -> C -> End,这条路径就是从Start到达End最短的路径!

注意: 迪克斯特拉算法只能用于权值为正的有向无环图。如果存在负的权值,那么就会造成狄克斯特拉算法中的通过累加来找最小路径的思想。每次的距离只能增加,不能减少才行。必须要是有向无环图才行,要不然找路径的过程就会在环中无限的循环下去,永远出不来。

算法实现

# -*- coding: utf-8 -*-
# @Time    : 2018-12-27 12:59
# @Author  : Jaedong
# @Version : 3.6
# @File    : dijkstra_algorithm.py
# @Software: PyCharm


# 狄克斯特拉算法,获取加权有向无环图中两个点的最小距离
def dijkstra_algorithm(graph, start, end):
    # 构建一个从起点开始,记录所有经过节点的路程距离的距离表
    distances = {}
    # 构建一个记录某个节点的父节点的散列表,方便路径回溯
    parents = {}

    # 初始化 distances 和 parents
    for neighbor in graph[start]:
        distances[neighbor] = graph[start][neighbor]
        parents[neighbor] = start

    # 查漏补缺,需要确定到终点的距离,如果没有的话就是不知道,不直达的话就是无限大
    if end not in distances:
        distances[end] = float('inf')
        parents[end] = None

    # 用于记录已经计算完毕到它最短距离的节点
    handled = []

    node = get_lowest_cost(distances, handled)
    while node is not None:
        # 获取到达节点的距离
        distance = distances[node]
        # 通过该节点可到达的节点(邻居节点)
        for neighbor in graph[node]:
            # 通过节点到达其邻居节点的距离等于到达该节点的距离加上该节点到其邻居节点的距离
            new_distance = distance + graph[node][neighbor]
            # 如果该节点的邻居节点在记录路程的表中,就看看是否更新一下到它的距离
            if neighbor in distances:
                # 如果通过该节点到达该节点的邻居节点的距离 小于 其它途径到达该邻居节点的距离
                # 就更新到达该节点邻居节点的距离,使到达该邻居节点的距离越小越好,路径就最优
                # 如果小于的话,就更新邻居节点的父节点为该节点,也就是选取了到达该邻居节点的
                # 更好的路径是通过该节点到达的这一条
                if new_distance < distances[neighbor]:
                    distances[neighbor] = new_distance
                    parents[neighbor] = node
            # 如果该节点的这个邻居节点并没有在距离表中,无需更新,就直接添加进去
            else:
                distances[neighbor] = new_distance
                parents[neighbor] = node
        # 记录处理过后的节点,继续往前走下去
        handled.append(node)
        node = get_lowest_cost(distances, handled)

    # 更新了所有节点的到达最小距离,那么到达end的最小距离就是distances表中end所对应的值
    distance = distances[end]
    # 通过不断的回溯的方式,得到end到达start的路径,这样,就得到了start到达end的路径
    parent = parents[end]
    paths = [parent, end]
    while parent is not start:
        paths.insert(0, parents[parent])
        parent = parents[parent]

    return distance, paths


# 获取距离表中到达所需距离最短的节点
def get_lowest_cost(distances, handled):
    shortest = float('inf')
    shortest_node = None
    for node in distances:
        if distances[node] < shortest and node not in handled:
            shortest = distances[node]
            shortest_node = node
    return  shortest_node


# 构建路径图,使用字典嵌套字典的方式(散列表嵌套散列表)
graph = {}
# 从起点开始,起点可以到达两个点:A、B
graph['Start'] = {}
# 起点到A点的距离是5  Start -> A
graph['Start']['A'] = 5
# 起点到B点的距离是3 Start -> B
graph['Start']['B'] = 3
# A可以到达两个点:C、D
graph['A'] = {}
# ... ...
graph['A']['C'] = 2
graph['A']['D'] = 2
graph['B'] = {}
graph['B']['C'] = 1
graph['B']['E'] = 6
graph['C'] = {}
graph['C']['End'] = 5
graph['D'] = {}
graph['D']['C'] = 3
graph['D']['End'] = 4
graph['E'] = {}
graph['E']['End'] = 1
graph['End'] = {}


distance, paths = dijkstra_algorithm(graph, 'Start', 'End')
print('\n最短的距离是:{}, 路径是: {}'.format(distance, paths))

结果是:

从Start到End的距离最短路径.png



上一篇:写给媳妇儿的算法(十一)——广度优先搜索
下一篇:写给媳妇儿的算法(十三)——贝尔曼-福德算法

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容

  • 专业考题类型管理运行工作负责人一般作业考题内容选项A选项B选项C选项D选项E选项F正确答案 变电单选GYSZ本规程...
    小白兔去钓鱼阅读 8,977评论 0 13
  • 1. 关于诊断X线机准直器的作用,错误的是()。 (6.0 分) A. 显示照射野 B. 显示中心线 C. 屏蔽多...
    我们村我最帅阅读 10,270评论 0 5
  • 选择题部分 1.(),只有在发生短路事故时或者在负荷电流较大时,变流器中才会有足够的二次电流作为继电保护跳闸之用。...
    skystarwuwei阅读 12,768评论 0 7
  • 配这张图是因为今天是正月十五,值得好好过的一天。每天都是值得好好过的,人总是因为传统给日子加上了仪式感,这日子就显...
    慧好聊吧阅读 2,682评论 3 13
  • 一直在等雨, 天气预报说今天会下雨,醒来做的第一件事就是开窗,看看雨有没有如期的到来。不知道何时雨会踏着它的云朵,...
    乐途兔阅读 228评论 0 1