【数据结构与算法】迪杰斯特拉算法的介绍和最短路径问题程序实现

1. 迪杰斯特拉算法的介绍

迪杰斯特拉(Dijkstra)算法是典型求两点之间最短路径算法。它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止

2. 迪杰斯特拉算法的原理

执行步骤

  1. 从开始顶点出发,先将开始顶点标记为已访问。此时开始顶点就是访问顶点
  2. 得到访问顶点的各个下一个未访问顶点,离开始顶点的权重值。如果新的权重值比该下一个未访问顶点之前的权重值小,则对该下一个未访问顶点的权重值进行替换。表示找到了一条更优的路径
  3. 从访问顶点的各个下一个未访问顶点中,选取一个离开始顶点的权重值最小的顶点,将该顶点标记为已访问
  4. 将上一步标记的已访问顶点,作为访问顶点,继续从步骤2开始,直到所有顶点被访问

3. 最短路径问题介绍

最短路径问题

问题:有7个村庄A、B、C、D、E、F、G,各个村庄之间的距离(权)用边线表示,比如A -> B距离5公里。现有一个快递需要从C村庄送到D村庄,如何走路径最短。从C村庄送到另外5个村庄类似

程序如下:

import java.util.Arrays;

public class DijkstraAlgorithm {

    public static void main(String[] args) {
        char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};

        // 用10000表示距离无限大,不能连通
        final int INFINITY = 10000;
        // N个顶点形成的N * N二维数组,用来保存顶点之间的距离
        // 用INFINITY表示距离无限大,不能连通
        int[][] weights = {
                {INFINITY, 5, 7, INFINITY, INFINITY, INFINITY, 2},
                {5, INFINITY, INFINITY, 9, INFINITY, INFINITY, 3},
                {7, INFINITY, INFINITY, INFINITY, 8, INFINITY, INFINITY},
                {INFINITY, 9, INFINITY, INFINITY, INFINITY, 4, INFINITY},
                {INFINITY, INFINITY, 8, INFINITY, INFINITY, 5, 4},
                {INFINITY, INFINITY, INFINITY, 4, 5, INFINITY, 6},
                {2, 3, INFINITY, INFINITY, 4, 6, INFINITY}
        };

        // 创建Graph对象
        Graph graph = new Graph(vertexs, weights);

        // 输出weights
        graph.showWeights();

        // 测试迪杰斯特拉算法
        graph.dsj(2);
        graph.showDijkstra();
    }

}


// 各个顶点访问过程中的帮助类
class VertexVisitHelper {
    // 标记各个顶点是否已经被访问。0表示未访问,1表示已访问
    public int[] visitedVertexs;
    // 保存每个顶点在路径上的前一个顶点的index
    public int[] preVertexIndexs;
    // 保存每个顶点距离开始顶点的权重值
    public int[] awayFromStartVertexWeights;

    // 构造器
    public VertexVisitHelper(int vertexNum, int startVertexIndex) {
        this.visitedVertexs = new int[vertexNum];
        // 标记开始顶点为已访问
        this.visitedVertexs[startVertexIndex] = 1;

        this.preVertexIndexs = new int[vertexNum];
        // 初始化时,所有顶点在路径上都没有前一个顶点,用-1表示
        Arrays.fill(this.preVertexIndexs, -1);

        this.awayFromStartVertexWeights = new int[vertexNum];
        // 初始化,开始顶点到其它顶点的距离为无限大,到自身的距离为0(已经标记为已访问,距离为0不影响)
        Arrays.fill(this.awayFromStartVertexWeights, 10000);
        this.awayFromStartVertexWeights[startVertexIndex] = 0;
    }

    // 根据顶点的index,查询顶点是否被访问
    public boolean vertexIsVisited(int vertexIndex) {
        return visitedVertexs[vertexIndex] == 1;
    }

    // 更新顶点在路径上的前一个顶点的index
    public void updatePreVertexIndex(int vertexIndex, int preVertexIndex) {
        preVertexIndexs[vertexIndex] = preVertexIndex;
    }


    // 根据顶点的index, 获取该顶点距离开始顶点的权重值
    public int getAwayFromStartVertexWeight(int vertexIndex) {
        return awayFromStartVertexWeights[vertexIndex];
    }

    // 根据顶点的index,更新距离开始顶点的权重值
    public void updateAwayFromStartVertexWeight(int vertexIndex, int awayFromStartVertexWeight) {
        awayFromStartVertexWeights[vertexIndex] = awayFromStartVertexWeight;
    }

    // 从awayFromStartVertexWeights选取一个值最小的顶点index,并标记为已访问
    // 然后返回该顶点的index,以便作为新的访问顶点
    public int updateVertexIsVisited() {
        int minAwayFromStartVertexWeight = 10000;
        int vertexIsVisitedIndex = 0;
        for (int i = 0; i < awayFromStartVertexWeights.length; i++) {
            // 如果顶点已访问,则不进行处理
            if (visitedVertexs[i] == 0 && awayFromStartVertexWeights[i] < minAwayFromStartVertexWeight) {
                minAwayFromStartVertexWeight = awayFromStartVertexWeights[i];
                vertexIsVisitedIndex = i;
            }
        }

        // 更新vertexIsVisited
        visitedVertexs[vertexIsVisitedIndex] = 1;

        // 将更新的vertexIsVisitedIndex返回
        return vertexIsVisitedIndex;
    }

    // 显示迪杰斯特拉算法的执行结果
    public void show() {

        System.out.println("==========================");
        // 输出visitedVertexs
        System.out.println("各个顶点是否被访问情况:" + Arrays.toString(visitedVertexs));
        System.out.println("各个顶点在路径上的前一个顶点index情况:" + Arrays.toString(preVertexIndexs));
        System.out.println("各个顶点离开始顶点的最优权重值情况:" + Arrays.toString(awayFromStartVertexWeights));

    }

}

class Graph {
    // 顶点集合
    private char[] vertexs;
    // N个顶点形成的N * N二维数组,用来保存顶点之间的距离
    private int[][] weights;
    // 各个顶点访问过程中的帮助类
    private VertexVisitHelper vertexVisitHelper;

    // 构造器
    public Graph(char[] vertexs, int[][] weights) {
        this.vertexs = new char[vertexs.length];
        for (int i = 0; i < vertexs.length; i++) {
            this.vertexs[i] = vertexs[i];
        }

        this.weights = new int[vertexs.length][vertexs.length];
        for (int i = 0; i < vertexs.length; i++) {
            for (int j = 0; j < vertexs.length; j++) {
                this.weights[i][j] = weights[i][j];
            }
        }
    }

    // 显示图的二维数组,即显示顶点之间的距离
    public void showWeights() {
        for (int[] line : this.weights) {
            System.out.println(Arrays.toString(line));
        }
    }

    // 更新访问顶点的未访问的下一个顶点,到起始顶点的权重值
    // 更新访问顶点的未访问的下一个顶点,的前一个顶点index(即访问顶点index)
    private void updateNextVertex(int visitVertexIndex) {
        int awayFromStartVertexWeight = 10000;

        // 遍历访问顶点的相邻顶点
        for (int j = 0; j < weights[visitVertexIndex].length; j++) {
            // 访问顶点到开始顶点的距离 + 访问顶点的下一个顶点到访问顶点的距离
            awayFromStartVertexWeight = vertexVisitHelper.getAwayFromStartVertexWeight(visitVertexIndex) + weights[visitVertexIndex][j];

            // 如果访问顶点的下一个顶点没有被访问过,且距离开始顶点的权重值比之前的更小
            // 则表示找到了一条更优的路径
            if (!vertexVisitHelper.vertexIsVisited(j) && awayFromStartVertexWeight < vertexVisitHelper.getAwayFromStartVertexWeight(j)) {
                vertexVisitHelper.updateAwayFromStartVertexWeight(j, awayFromStartVertexWeight);
                vertexVisitHelper.updatePreVertexIndex(j, visitVertexIndex);
            }
        }
    }


    // 迪杰斯特拉算法实现
    public void dsj(int startVertexIndex) {
        vertexVisitHelper = new VertexVisitHelper(vertexs.length, startVertexIndex);

        // 更新访问顶点的未访问的下一个顶点,到起始顶点的权重值
        // 更新访问顶点的未访问的下一个顶点,的前一个顶点index(即访问顶点index)
        updateNextVertex(startVertexIndex);

        // 不断的进行路径查找,直到最后一个顶点
        // 在路径查找的过程中,其它顶点的最短路径也会找到
        int visitVertexIndex = 0;
        for (int j = 1; j < vertexs.length; j++) {

            // 从awayFromStartVertexWeights选取一个值最小的顶点index,并标记为已访问
            // 然后返回该顶点的index,以便作为新的访问顶点
            visitVertexIndex = vertexVisitHelper.updateVertexIsVisited();

            // 更新访问顶点的未访问的下一个顶点,到起始顶点的权重值
            // 更新访问顶点的未访问的下一个顶点,的前一个顶点index(即访问顶点index)
            // 当最后一个顶点被访问,该方法执行没意义
            updateNextVertex(visitVertexIndex);
        }
    }

    // 显示迪杰斯特拉算法的执行结果
    public void showDijkstra() {
        vertexVisitHelper.show();
    }
}

运行程序,结果如下:

[10000, 5, 7, 10000, 10000, 10000, 2]
[5, 10000, 10000, 9, 10000, 10000, 3]
[7, 10000, 10000, 10000, 8, 10000, 10000]
[10000, 9, 10000, 10000, 10000, 4, 10000]
[10000, 10000, 8, 10000, 10000, 5, 4]
[10000, 10000, 10000, 4, 5, 10000, 6]
[2, 3, 10000, 10000, 4, 6, 10000]
==========================
各个顶点是否被访问情况:[1, 1, 1, 1, 1, 1, 1]
各个顶点在路径上的前一个顶点index情况:[2, 0, -1, 5, 2, 4, 0]
各个顶点离开始顶点的最优权重值情况:[7, 12, 0, 17, 8, 13, 9]

猜你喜欢

转载自blog.csdn.net/yy8623977/article/details/127316740