解决最短路径的Dijkstra算法详解,附加Java代码

1. 最短路径问题

最短路径问题是生活中经常碰到的一类问题,如机器人路径规划,数学竞赛以及真实的工程施工问题;甚至是我们程序员笔试必刷算法题。其实问题很简单,就是有很多个节点,我们要计算出一个初始点到各个节点的最短距离(或者得到最短路径的表示),如下图,怎么找出A点到其它各个点的最短路径及距离? 人眼扫描得到的结果:A到B、C、D……G距离为 [4, 14, 7, 7, 10, 12],人眼对付7个点还是绰绰有余的,什么?你扫描的结果和我不一样?那咱们还是分析一下原理,然后写个代码来处理一下。

2. Dijkstra算法 

2.1 基本原理

假设我们是要计算其它各点和A点的最短距离

  1. 我们选取A点作为出发点,那么很明显,我们只可以从和A点有直接联系的B、E点中选择一个最短路径点。先取出距离A点路径长度为4的B点,B点确定,那么我们记录B点以及B点和A的距离为{B(4)}
  2. 此时我们要从和A有直接关系的E点以及通过B和A有间接关系的C、D点中选择最短路径点;E和D距离A都是7,我们随便取一个,假如选择D点,D点确定,记录D点以及D点和A的距离为{B(4)、D(7)}
  3. 接下来我们从和A有直接关系的E点以及通过B和A有间接关系的C,通过B、D和A有间接关系的F、G中选择最短路径点;此时E距离A最近为7,E点确定,记录E点以及E点和A的距离为{B(4)、D(7)、E(7)}。
  4. ……

2.2 代码编写

我们大脑很容易分析这种7个点的最短路径,那如果是成百上千个点,错综复杂,我们大脑可能是不够用,通过代码是最有效的解决办法,但代码逻辑也有点难理解,接下来就好好地理解一下。

2.2.1 数据初始化

我们先将图中的各个点以及它们之间的路径距离转化成二维数组,如下图。

2.2.2  结果记录初始化

为了保存结果,我们得对记录结果的数组进行初始化。主要有两个:一个是记录哪些点到A点的距离已经确定下来的数组,另一个是记录每个点到A点的距离。

2.2.3 初始点的选择

我们这里计算各个点到A点的最短距离。

2.2.4 循环计算每个点和A点的距离,更新distance数组以及isVisit数组

2.2.5 运行结果

2.2.6 代码的话,由于变量和数组有点多,所以不太容易理解,而且也不太好分析,所以需要同学们自己耐心对照2.1基本原理理解,因为2.1的思路还是比较简单清晰,理解思路之后,再来理解代码好一些

3. 完整的Java代码如下

public static void main(String[] args) {
        int inf = 100;
        // 初始化节点数为7
        int node = 7;
        // 各个点之间的路径长度,inf表示不连通
        int[][] map = {
                       // 从左往右,从上往下依次表示A、B、C、D、E、F、G
                       {inf, 4,   inf, inf, 7,   inf, inf},
                       {4,   inf, 15,  3,   inf, inf, inf},
                       {inf, 15,  inf, inf, inf, inf, 2  },
                       {inf, 3,   inf, inf, 5,   3,   5  },
                       {7,   inf, inf, 5,   inf, 6,   inf},
                       {inf, inf, inf, 3,   6,   inf, inf},
                       {inf, inf, 2,   5,   inf, inf, inf}};

        // 记录哪些点已经确定了,确定为true,否则为默认值false
        boolean[] isVisit = new boolean[node];
        // 记录每个点距离初始点的距离
        int[] distance = new int[node];
        // 初始值为inf,表示无穷大
        Arrays.fill(distance, inf);

        // 选择A作为初始点,0下标代表A,
        int initNode = 0;
        // A点到自己的距离为0
        distance[initNode] = 0;
        // 同时将isVisit数组元素A的下标0设置为true,表示A点已确定
        isVisit[initNode] = true;

        int currNode = initNode;
        int certainDistance = distance[currNode];
        // 只需计算其余6个点距离A点的距离,每次循环得到一个点,循环6次即可
        for (int loop = 1; loop < node; loop++) {
            // 循环计算和currNode有关系节点的距离,第一次currNode是A点
            for (int i = 0; i < map[currNode].length; i++) {
                // 以探索点距初始点的距离加上各点和探索点的距离更新distance矩阵
                int tmp = certainDistance + map[currNode][i];
                if(tmp < distance[i]){
                    distance[i] = tmp;
                }
            }
            // 挑选还未访问,并且距离初始点最近的点作为探索点
            currNode = getMin(distance, isVisit, inf);
            // 将该探索点标记为true“已访问”,
            isVisit[currNode] = true;
            // 并且得到该点和A点的距离
            certainDistance = distance[currNode];
        }

        char[] str = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        for (int i = 0; i < node; i++) {
            System.out.println(str[i] + "到" + str[initNode] + "的距离为 " + distance[i]);
        }
    }

    static int getMin(int[] dis, boolean[] isVisit, int inf){
        int min = -1;
        int minVal = inf;
        for (int i = 0; i < dis.length; i++) {
            // 扫描isVisit还未访问的节点,并且该节点是distance矩阵最小的数
            if((!isVisit[i]) && (dis[i] < minVal)){
                minVal = dis[i];
                min = i;
            }
        }
        return min;
    }

猜你喜欢

转载自blog.csdn.net/yldmkx/article/details/109962795
今日推荐