Dijkstra算法原理及实现

算法原理

1. 简介

Dijkstra算法是从一个顶点到其余各顶点的最短路径(单源最短路径)算法,解决的是有向图中最短路径问题。算法的主要特点是使用了广度优先搜索策略,以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低。

2. 原理
  1. 首先,引入一个辅助向量 D D ,它的每个分量 D[i]保存当前所找到的从起始点 v v 到其它每个顶点vi的长度;一个保存已经找到了最短路径的顶点的集合: T T
  2. D的初始状态为: 若从 v v vi 有弧,则 D[i] D [ i ] 为弧上的权值否则置 D[i] D [ i ] 为∞;
    T T 的初始状态为{v}
  3. D D 数组选择最小值,则该值就是源点v到该值对应的顶点的最短路径,并且把该点加入到 T T 中,OK,此时完成一个顶点
  4. 然后,我们需要看看新加入的顶点是否可以到达其他顶点并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点D中的值
  5. D D 中找出最小值,重复上述动作,直到T中包含了图的所有顶点

算法演示

对于如下图,求顶点v1到其他各点的最短路径
这里写图片描述

  1. 初始化数 D=[0,inf,10,inf,30,100] D = [ 0 , i n f , 10 , i n f , 30 , 100 ] , T={v1} T = { v 1 }

  2. 当前离起始点 v1 v 1 最近的点是 v3 v 3 , 那么我们可以确定 v1 v 1 v3 v 3 的最近距离就是当前 D[2] D [ 2 ] 的值, 并将 v3 v 3 加入 T T 中 (为什么呢?因为目前离 v1顶点最近的是 v3顶点,并且这个图所有的边都是正数,那么肯定不可能通过第三个顶点中转,使得 v1顶点到 v3顶点的路程进一步缩短了。因为 v1顶点到其它顶点的路程肯定没有 v1到 v3顶点短.)

  3. OK,既然确定了一个顶点的最短路径,下面我们就要根据这个新入的顶点v3的出度,发现以 v3 v 3 的出度的有: {v4} { v 4 } ,那么我们看看路径: v1v3v4 v 1 – v 3 – v 4 的长度是否比 v1v4 v 1 – v 4 短,其实这个已经是很明显的了,因为 D[3] D [ 3 ] 代表的就是 v1v4 v 1 – v 4 的长度为无穷大,而 v1v3v4 v 1 – v 3 – v 4 的长度为:10+50=60,所以更新 D[3] D [ 3 ] 的值,得到如下结果:

    D=[0,inf,10,60,30,100] D = [ 0 , i n f , 10 , 60 , 30 , 100 ] , T={v1,v3} T = { v 1 , v 3 }

    (因此 D[3] D [ 3 ] 要更新为 60。这个过程有个专业术语叫做“松弛”。即 v1 v 1 顶点到 v4 v 4 顶点的路程即 D[3] D [ 3 ] ,通过 v3v4 v 3 − v 4 这条边松弛成功。这便是 Dijkstra 算法的主要思想:通过“边”来松弛 v1 v 1 顶点到其余各个顶点的路程)

  4. 然后,我们又从除 v1 v 1 v3 v 3 外的其他值中寻找最小值,发现 D[4] D [ 4 ] 的值最小,通过之前是解释的原理,可以知道 v1v5 v 1 到 v 5 的最短距离就是 D[4] D [ 4 ] 的值,然后,我们把 v5 v 5 加入到集合T中,然后,考虑 v5 v 5 的出度是否会影响我们的数组 D D 的值,v5有两条出度: {v4,v6} { v 4 , v 6 } ,然后我们发现: v1v5v4 v 1 – v 5 – v 4 的长度为:50,而 v1v4 v 1 − v 4 的值为60,所以我们要更新 D[3] D [ 3 ] 的值.另外, v1v5v6 v 1 − v 5 − v 6 的长度为:90,而 v1v6 v 1 − v 6 为100,所以我们需要更新 D[5] D [ 5 ] 的值。更新结果如下:

    D=[0,inf,10,50,30,90] D = [ 0 , i n f , 10 , 50 , 30 , 90 ] , T={v1,v3,v5} T = { v 1 , v 3 , v 5 }

  5. 继续从 D D 中选择未确定的顶点的值中选择一个最小的值,发现D[3]的值是最小的,所以把 v4 v 4 加入到集合 T T 中,考虑v4的出度,v4有一条出度: {v6} { v 6 } ,然后我们发现: v1v5v4v6 v 1 – v 5 – v 4 – v 6 的长度为:60,而 v1v6 v 1 − v 6 的值为90,所以我们要更新 D[5] D [ 5 ] 的值, 结果如下

    D=[0,inf,10,50,30,60] D = [ 0 , i n f , 10 , 50 , 30 , 60 ] , T={v1,v3,v5,v4} T = { v 1 , v 3 , v 5 , v 4 }

  6. 用同样的方法确定了 v6 v 6 v2 v 2 的最短路径,得到的最终结果如下:

    D=[0,inf,10,50,30,60] D = [ 0 , i n f , 10 , 50 , 30 , 60 ] , T={v1,v3,v5,v4,v6,v2} T = { v 1 , v 3 , v 5 , v 4 , v 6 , v 2 }

算法实现

# coding:utf-8
def dijkstra(G, v):
    """
    :param G: graph, type of dict
    :param v: original node, type of str
    :return: signal source shortest path,
    type of list
    """
    # initialize
    n = len(G)
    D = [float('inf')] * n
    D[node2ind(v)] = 0
    S = set(G.iterkeys())
    # iteration
    while S:
        v = None
        for node in S:
            if v == None or D[node2ind(node)] < D[node2ind(v)]:
                v = node
        S.remove(v)
        for node in G[v]:
            ind = node2ind(node)
            # 如果集合为空或者满足三角不等式,则更新D
            if D[node2ind(v)] + G[v][node] < D[ind]:
                D[ind] = D[node2ind(v)] + G[v][node]
    return D

def node2ind(node):
    """get index of node"""
    ind = int(node[-1]) - 1
    return ind

def ind2node(ind):
    """get node given index"""
    node = 'v' + str(ind + 1)
    return node


if __name__ == '__main__':
    G = {
        'v1':{'v3':10, 'v5':30, 'v6':100},
        'v2':{'v3':5},
        'v3':{'v4':50},
        'v4':{'v6':10},
        'v5':{'v4':20, 'v6':60},
        'v6':{}
         }
    v = 'v1'
    res = dijkstra(G, v)
    print(res)

"""打印的结果"""
[0, inf, 10, 50, 30, 60]

复杂度分析

该算法时间复杂度为 O(n2) O ( n 2 ) , 但是,如果边数远小于 n2 n 2 ,对此可以考虑用堆这种数据结构进行优化,取出最短路径的复杂度降为 O(1) O ( 1 ) ;每次调整的复杂度降为 O(elogn) O ( e l o g n ) e e 为该点的边数,所以复杂度降为O((m+n)logn), m m 为总的边数

堆优化算法步骤

  1. 将与源点相连的点加入堆,并调整堆。
  2. 选出堆顶元素u(即代价最小的元素),从堆中删除,并对堆进行调整。

  3. 处理与 u u 相邻的,未被访问过的,满足三角不等式的顶点
    1):若该点在堆里,更新距离,并调整该元素在堆中的位置。
    2):若该点不在堆里,加入堆,更新堆。
  4. 若取到的u为终点,结束算法;否则重复步骤2、3。
堆优化算法实现
def dijkstra_heap(G, v):
    # initialize
    n = len(G)
    D = [float('inf')] * n
    D[node2ind(v)] = 0
    T = set()
    heap = []
    # iteration
    while len(T) < n:
        for node in G[v]:
                ind = node2ind(node)
                # 如果集合为空或者满足三角不等式,则更新D
                if len(T) == 0 or D[node2ind(v)] + G[v][node] < D[ind]:
                    D[ind] = D[node2ind(v)] + G[v][node]
                    heappush(heap, (D[node2ind(v)] + G[v][node], node))
        T.add(v)
        # find node minimum length to v
        if len(T) < n:
            node = heappop(heap)[1]
            while node in T:
                # 如果堆中没有元素了
                if not heap:
                    return D
                node = heappop(heap)[1]
            v = node
    return D

def node2ind(node):
    """get index of node"""
    ind = int(node[-1]) - 1
    return ind

def ind2node(ind):
    """get node given index"""
    node = 'v' + str(ind + 1)
    return node


if __name__ == '__main__':
    G = {
        'v1':{'v3':10, 'v5':30, 'v6':100},
        'v2':{'v3':5},
        'v3':{'v4':50},
        'v4':{'v6':10},
        'v5':{'v4':20, 'v6':60},
        'v6':{}
         }
    v = 'v1'
    res = dijkstra_heap(G, v)
    print(res)

"""打印的结果是"""
[0, inf, 10, 50, 30, 60]
  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 最短路径Dijkstra算法是一种用于计算带权有向中单源最短路径的经典算法。它的基本原理是通过不断更新节点到源节点的距离,从而找到到达目标节点最短的路径。 算法从源节点开始,先将源节点到它的邻接节点的距离初始化为无穷大。然后将源节点到它的邻接节点的距离更新为通过源节点的距离加上源节点到邻接节点的权重。接着,从所有未访问节点中选择距离最小的节点,将该节点标记为已访问,并更新与该节点相邻的节点的距离。重复这个过程,直到所有节点都被访问完毕或没有可更新的距离。最后,就可以得到源节点到所有其他节点的最短路径。 在MATLAB中,可以通过邻接矩阵来实现Dijkstra算法。邻接矩阵是一个二维矩阵,矩阵的每个元素表示两个节点之间的权重。首先,需要初始化一个距离数组,用于存储源节点到其他节点的临时最短路径长度。然后,通过遍历邻接矩阵,将源节点到它的邻接节点的距离初始化为邻接矩阵中对应的权重。接着,再初始化一个标记数组,用于标记节点是否已经被访问过。然后,循环遍历未访问节点,选择距离最小的节点,并将该节点标记为已访问。随后,更新与该节点相邻的节点的距离,如果存在更短的路径。这个过程一直重复,直到所有节点都被访问完毕。最后,通过距离数组就可以得到源节点到其他所有节点的最短路径。 总结来说,最短路径Dijkstra算法能够找到带权有向中的单源最短路径。在MATLAB中,通过邻接矩阵和相应的数据结构,可以完美地实现这个算法。 ### 回答2: 迪杰斯特拉算法是一种用于求解上两个节点之间最短路径的经典算法。 其原理如下: 1. 创建一个数组dist[],数组中的元素表示源节点到中其他顶点的最短距离,开始时将源节点的距离设为0,其他节点的距离设为无穷大。 2. 创建一个集合visited[],用来记录已经找到最短路径的节点。 3. 选择dist[]中距离源节点最近的节点,将其标记为visited,并更新其邻接节点的距离。若经过这个节点到邻接节点的距离比当前记录的距离小,则更新dist[]数组中的距离。 4. 重复第3步,直到所有节点都被标记为visited,或者dist[]中没有未被访问的节点。 5. 最终,dist[]数组中记录了源节点到所有其他节点的最短路径。 在MATLAB中实现迪杰斯特拉算法,可以按照以下步骤进行: 1. 初始化dist[]数组和visited[]数组。 2. 在dist[]数组中将源节点的距离设置为0,其他节点的距离设置为无穷大。 3. 在一个循环中,首先选择dist[]数组中距离源节点最近的节点v,并将其标记为visited。 4. 遍历节点v的所有邻接节点u,如果经过节点v到达节点u的距离比当前记录的距离小,则更新dist[]数组中的距离。 5. 重复步骤3和步骤4,直到所有节点都被标记为visited。 6. 最后,dist[]数组中记录了源节点到其他节点的最短路径。 以上是最短路径迪杰斯特拉算法原理及在MATLAB中的实现方式。该算法在实际应用中被广泛使用,用于解决很多网络和路径规划问题。 ### 回答3: Dijkstra算法是一种用于求解带权重最短路径算法。它通过计算从起点到各个顶点的最短路径长度,并逐步找到最短路径算法原理如下: 1. 创建一个空的优先队列和一个空的最短路径集合。优先队列用于存放待搜索的顶点,节点的优先级按照到起点的距离从小到大排列。 2. 将起点加入优先队列,并设置距离起点的距离为0。 3. 重复以下步骤,直至优先队列为空: - 从优先队列中取出距离起点最近的顶点v。 - 将顶点v加入最短路径集合中。 - 对于v的所有邻接顶点u,更新u的最短距离,如果经过顶点v到达u的距离小于当前最短距离,则更新最短距离,并将u加入优先队列。 Matlab实现如下: ```matlab function [dist, path] = dijkstra(graph, source) num_nodes = size(graph, 1); dist = inf(1, num_nodes); % 初始化距离数组,设置为正无穷大 dist(source) = 0; % 设置起点的距离为0 visited = false(1, num_nodes); % 记录节点是否被访问过的数组 path = zeros(1, num_nodes); % 记录最短路径的数组 for i = 1:num_nodes % 从未访问的节点中选出距离最小的节点 [~, u] = min(dist.*~visited); visited(u) = true; % 标记该节点为已访问 % 更新与u相邻节点的最短距离 for v = 1:num_nodes if graph(u, v) > 0 && dist(v) > dist(u) + graph(u, v) dist(v) = dist(u) + graph(u, v); path(v) = u; % 更新最短路径 end end end end ``` 以上是Dijkstra算法的简要原理和用Matlab实现的代码。通过该算法,可以得到从起点到其他各个顶点的最短路径距离和路径信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值