最短路径:用最通俗的话讲述Dijkstra算法

Dijkstra算法


主要思路:

找到源点,找到离源点最近的点,然后根据该最近的点去更新其他离源点的距离(因为其他点可能通过该点中转)。然后离源点的次近点,找到次近点之后,再根据该次近点去更新其他点到源点的距离,以此类推,找到离源点第n近的点,然后去更新 其他可能通过该点中转后离源点更近的点 离源点的距离。

数据结构:

  • dis []int:各个点离源点的最短距离
  • v []int:各个点是否访问过

步骤:

  1. 初始化:找到源点src,计算所有点距离src的距离,生成数组dis;以及初始化访问数组vsrc置为0,其他的所有点置为-1
  2. 遍历dis,找到dis数组中的最小值,即离src最近的点u,(注意:此时点u离源点的最短距离已经可以确定,仔细想想,已经找不到任何中转点能使usrc的距离更短),将点u加入到v数组(说明u已经访问)。
  3. 根据最近的点u来更新其他点到src的最短距离,因为其他点可能通过u中转离src更近。
  4. 更新完成之后,重复步骤2和步骤3,找到dis中的最小值。
  5. 直到所有的点更新完。即找到了所有点离源点src的最短距离–dis数组

图示:

最短路径:用最通俗的话讲述Dijkstra算法_第1张图片

图片来自:平凡的L同学

代码:

//golang
package main

import (
	"fmt"
	"math"
)

var MAX = math.MaxInt64
func Dijkstra(g [][]int, n, src, dst int) int {
	e := extract(g, n)
	dis := make([]int, n) //保存与源点src的最短距离
	v := make([]int, n)   //记录是否访问
    //初始化
	for i := 0; i < n; i++ {
		dis[i] = e[src][i]
		v[i] = -1
	}
	v[src] = 0

	//Dijkstra
	for i := 0; i < n; i++ {
		min := MAX
		u := 0
		//查找离src最近的点
		for j := 0; j < n; j++ {
			if v[j] == -1 && dis[j] < min {
				min = dis[j]
				u = j
			}
		}
        
		//更新该点
		dis[u] = min
		v[u] = 0

		//拓展该点
		for j := 0; j < n; j++ {
			if e[u][j] == MAX {
				continue
			}
			if e[u][j] + dis[u] < dis[j] {
				dis[j] = e[u][j] + dis[u]
			}
		}
        
	}
	return dis[dst]
}

//解析图,转换成邻接矩阵
func extract(edge [][]int, n int) [][]int {
	e := make([][]int, n)
	//初始化
	for i := 0; i < n; i++ {
		e[i] = make([]int, n)
		for j := 0; j < n; j++ {
			e[i][j] = MAX
		}
	}
	//转化程邻接矩阵
	for i := 0; i < len(edge); i++ {
		//有向
		e[edge[i][0]][edge[i][1]] = edge[i][2]
	}
	return e
}

func main() {
    //说明: {0,1,1}表示节点0与节点1的距离为1
	g := [][]int{{0,1,1}, {0,2,2}, {1,2,2}, {1,3,3}, {1,5,10}, {1,7,50}, {3,0,4}, {3,2,8}, {3,4,5}, {4,5,1}, {4,6,2}, {5,6,1}, {6,7,3}}
	res := Dijkstra(g, 8, 1, 7)
	fmt.Println(res)
}

Tips:

  1. 如何保存各个点与源点的最短距离?

    • 使用数组 dis保存,时刻更新(访问完一个点之后更新一次)
  2. 如何确保各个点与源点的最短距离已经确定?即已是最短路径。

    • 每次查找dis数组中的最小值,该点是未访问过的点中离源点最近的,即该点的最短距离已经确定。

你可能感兴趣的:(算法,dijkstra,算法,数据结构)