2020年春招编程题-Dijkstra算法

2020年春招暂告一段落了。总结一下今年春招的编程题,今年好多厂的笔试题都出现了关于图的最短路径的编程题。

先还原一下题目:
假设某地区有五个城市,为了方便玩家在不同的城市活动,在每个城市都有传送点可以到达另外某个城市,这些城市之间满足以下条件:

  1. 任意两个城市之间不一定可以直接传送
  2. 可直接传送的任意两个城市传送时间会受距离的影响,距离越远传送时间越长
  3. 传送是单向的,即如果V1可以直接传送到V2,那么V2不可以直接传送到V1

请设计一个程序,能够计算出V1到其他各个城市的最短时间。

这个题目很明显,就是求图中最短路径,今天我们就讲讲求图中最短路径的算法–Dijkstra(迪杰斯特拉)算法。


迪杰斯特拉算法采用的是贪心策略。具体的思想如下:

  • 首先设置一个距离表dis,用来记录源点到各个顶点的距离。
  • 先从未访问顶点集合U中找到距离源点路径最短的顶点,放入已访问顶点集合V。
  • 当已访问顶点集合V更新之后,更新距离表dist。
  • 重复这个过程,直到已访问顶点集合包括了图中所有V1可以到达的顶点。

举个例子,如下图所示的一个有向图中,设V1为起始点(源点)。2020年春招编程题-Dijkstra算法_第1张图片
初始化的距离表如下所示,此时已访问顶点集合还是空的。
2020年春招编程题-Dijkstra算法_第2张图片
当把V1顶点放入已访问顶点集合中,此时V3,V5,V6都是V1的邻接顶点,而V2和V4并没有与V1邻接,所以与V1的距离置为∞。
2020年春招编程题-Dijkstra算法_第3张图片
在与V1的邻接的V3,V5,V6顶点中,V3是距离V1最近的顶点,所以,把V3放入已访问顶点,并更新距离表。
2020年春招编程题-Dijkstra算法_第4张图片
更新后的距离表可以看到,V3的访问标志位被置为True,说明V3放入已访问顶点集合中了;V4与V1的距离变成了60,这是为什么呢?因为当V3放入顶点集合中,V1可以先访问V3,再访问V4。简单的说就是V1通过V3可以走到V4这个地方了,所以距离为10+50=60。

这时候再在V2,V4,V5,V6中选择距离V1最近的,V5,就是你了!把V5放入已访问顶点集合中去,此时的距离表变为:
2020年春招编程题-Dijkstra算法_第5张图片
咦~V4和V6到V1的距离咋变了呢?
这是因为由于V5顶点加入到已访问顶点集合中去,V1到V4的路径就变成两条V1->V3->V4,V1->V5->V4,这两条路我们肯定要选择最短的啊,经过比较发现V1->V5->V4这条路径最短,所以V1到V4的距离更新为30+20=60。同理,V1到V6的两条路径V1->V6,V1->V5->V6,我们比较发现V1->V5->V6会更短,所以V1到V6的距离更新为30+60=90。

这时候再在V2,V4,V6中选择距离V1最近的,去吧,V4!把V4放入已访问顶点集合中去,此时的距离表变为:
2020年春招编程题-Dijkstra算法_第6张图片
由于V4加入到已访问顶点集合中去,V1到V6的路径就更新为V1->V5->V4->V6,距离更新为60。

然后再把V6加入到已访问顶点集合中去
2020年春招编程题-Dijkstra算法_第7张图片
对于V2,它本来就是个不合群的顶点,V2只有一条往V3去的路,所以V1是不能够到达V2的。

以下是python代码的实现

import sys
class Dis:
    # path 表示源点距离该点的路径
    # value 表示源点距离该点的距离
    # visited 表示该点是否被访问

    def __init__(self, path = '', value = 0, visited = False):
        self.path = path
        self.value = value
        self.visited = visited

    # 自定义打印函数
    def __str__(self):
        return str(self.visited) + " " + self.path + " " + str(self.value)

def Dijkstra(n,arrs:list):
    # n表示顶点个数

    int_max = sys.maxsize      #python中的INT_MAX最大int
    arc = [([int_max] * n) for i in range(n)]       # 初始化一个无穷大的二维数组


    # 将顶点之间的权重关系写入二维数组arc中
    for v in arrs:
        start = v[0]
        end = v[1]
        weight = v[2]
        arc[start-1][end-1] = weight

    begin = 1       #设置起始位置,这里我设置V1为源点

    # 构造dis表
    dis = []
    for _ in range(n):
        dis.append(Dis())
    # 根据二维数组arc 初始化dis表
    for i in range(n):
        dis[i].path = str(begin) + "-->" + str(i+1)
        dis[i].value = arc[begin-1][i]

    dis[begin - 1].visited = True
    dis[begin - 1].value = 0



    # 遍历每个点的路径关系
    for _ in range(n):

        # temp用于保存当前dis组中最短路径的下标
        # min_dist记录的当前点到源点的最短路径
        temp = 0
        min_dist = int_max

        # 找到距离源点最近的顶点,并加入到已经找到的最短路径的集合中(visited置为True)
        for i in range(n):
            if dis[i].visited == False and dis[i].value < min_dist:
                min_dist = dis[i].value
                temp = i
        dis[temp].visited = True


        # 更新路径表
        for i in range(n):
            if dis[i].visited == False and arc[temp][i] != int_max and (dis[temp].value + arc[temp][i]) < dis[i].value:
                dis[i].value = dis[temp].value + arc[temp][i]
                dis[i].path = dis[temp].path + "-->"+str(i+1)

    for i in dis:
        print(i)
    print('')


if __name__ == '__main__':
    while 1:
        try:
            # 处理输入数据
            n, m = map(int, input().split())
            arrs=[]
            for i in range(m):
                arrs.append(list(map(int, input().split())))
            Dijkstra(n,arrs)
        except:
            break

输入:

6 8
1 3 10
1 5 30
1 6 100
2 3 5
3 4 50
4 6 10
5 6 60
5 4 20

输出:
2020年春招编程题-Dijkstra算法_第8张图片


最后闲聊一句,迪杰斯特拉算法算是个比较有难度的算法,机考一般都是一个小时两道编程题,或者两个小时五道编程题,在这里面加一道迪杰斯特拉算法题,还是有点难度的。在这里由衷佩服机考高分的大佬们!
2020年春招编程题-Dijkstra算法_第9张图片

你可能感兴趣的:(python,算法)