图-最短路径—Dijkstra算法和Floyd算法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35923749/article/details/84672112

1.定义概览

Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法是很有代表性的最短路径算法,算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块。

时间复杂度是O(n2)

注意:该算法要求图中不存在负权边

2.算法思想:

       Dijkstra算法采用的是一种贪心的策略,声明一个数组distance来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合:T。

      初始化,原点 s 的路径权重被赋为 0 (distance[s] = 0,如果是有向图也可以设置为无穷大,因为有向图需要区别是否有到达自己的路径)。若对于顶点 s 存在能直接到达的边(s,m),则把distance[m]设为weight(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。同时,把顶点s加入集合T。 
     然后,从distance数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到T中,OK,此时完成一个顶点。 
     接着,我们需要看看新加入的顶点是否可以到达其他顶点,并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在distance中的值。 
    重复上述动作,直到T中包含了图的所有顶点。

3、Dijkstra算法示例演示
下面我求下图,从任一源点(因为原博主的图是选择v1作为源点,咋们就用它吧,个人比较懒,懒得去自己画图了,只要自己能讲清楚算法实现,部分拿来主义也未尝不可,本人实现的算法是可以自定义源点的)到其他各个顶点的最短路径

è¿éåå¾çæè¿°

首先第一步,我们先声明一个dis数组,该数组初始化的值为: 

è¿éåå¾çæè¿°
我们的顶点集T的初始化为:T={v1}

既然是求 v1顶点到其余各个顶点的最短路程,那就先找一个离 V1 号顶点最近的顶点。通过数组 dis 可知当前离v1顶点最近是 v3顶点,则 v1顶点到 v3顶点的最短路程就是当前 dis[2]值,将V3加入到T中。 

你以为这次循环中已经结束了吗?no,接着,既然确定了一个顶点的最短路径,下面我们就要根据这个新入的顶点V3是否有出度,发现以v3 为起点的有: < v3,v4 >,那么我们看看路径:v1–v3–v4的长度是否比源点v1–v4短,因为dis[3]代表的就是v1–v4的长度为无穷大,而v1–v3–v4的长度为:10+50=60,所以更新dis[3]的值,得到如下结果: 

è¿éåå¾çæè¿°
因此 distance[3]要更新为 60。这个过程有个专业术语叫做“松弛”。即 v1顶点到 v4顶点的路程即 distance[3],通过 < v3,v4> 这条边松弛成功。这便是 Dijkstra 算法的主要思想:通过“边”来松弛v1顶点到其余各个顶点的路程。

然后,我们又从distance中查找除了已经访问的V1和V3外的其他点中的最小值,发现distance[4]的值最小,通过之前是解释的原理,可以知道v1到v5的最短距离就是dis[4]的值,然后,我们把v5加入到集合T中,然后,考虑v5的出度是否会影响我们的数组dis的值,v5有两条出度:< v5,v4>和 < v5,v6>,然后我们发现:v1–v5–v4的长度为:50,而dis[3]的值为60,所以我们要更新dis[3]的值.另外,v1-v5-v6的长度为:90,而distance[5]为100,所以我们需要更新distance[5]的值。更新后的dis数组如下图: 

è¿éåå¾çæè¿°
然后,继续从dis中选择未确定的顶点的值中选择一个最小的值,发现dis[3]的值是最小的,所以把v4加入到集合T中,此时集合T={v1,v3,v5,v4},然后,考虑v4的出度是否会影响我们的数组dis的值,v4有一条出度:< v4,v6>,然后我们发现:v1–v5–v4–v6的长度为:60,而dis[5]的值为90,所以我们要更新dis[5]的值,更新后的dis数组如下图: 

è¿éåå¾çæè¿°
然后,我们使用同样原理,分别确定了v6和v2的最短路径,最后dis的数组的值如下: 

è¿éåå¾çæè¿°
因此,从图中,我们可以发现v1-v2的值为:∞,代表没有路径从v1到达v2。所以我们得到的最后的结果为:
 

V1 to V2 最短距离: 无穷大!
V1 to V3 最短距离: 10--------------------路径是:V1------V3
V1 to V4 最短距离: 60--------------------路径是:V1------V3---V4
V1 to V5 最短距离: 50--------------------路径是:V1------V5
V1 to V6 最短距离: 70--------------------路径是:V1------V4---V3---V6

4、Dijkstra算法的代码实现(java)原博主是c++实现

package com.practice.graph;

/**
 * Created by 凌 on 2018/11/29.
 * 描述:最短路径算法(一):Dijkstra算法
 * https://www.cnblogs.com/wsw-seu/p/8185285.html
 * https://blog.csdn.net/qq_35644234/article/details/60870719
 */
public class Dijkstra {
    /**
     * 源点 0 到顶点 i 的最短路径
     */
    private int[] distances;
    /**
     *邻接矩阵
     */
    private int[][] adjacencyMatrix;
    /**
     * 图的顶点个数
     */
    private int vertexNum;
    /**
     * 图的顶点表示
     */
    private String[] vertex;
    /**
     * 图的边数
     */
    private int edgeNum;
    /**
     * 开始遍历图的起点
     */
    private int startVertex;
    /**
     * 标记结点是否已经遍历
     */
    boolean[] visited;
    /**
     * 从源结点开始访问的最短路径
     */
    int[] path;

    public Dijkstra(int[][] adjacencyMatrix, int vertexNum, String[] vertex, int edgeNum) {
        this.adjacencyMatrix = adjacencyMatrix;
        this.vertexNum = vertexNum;
        this.vertex = vertex;
        this.edgeNum = edgeNum;
        this.visited = new boolean[vertexNum];
        this.distances = new int[vertexNum];
        this.path = new int[vertexNum];
    }

    /**
     * 设置开始遍历图的起点,将字符串转成下标
     * @param startVertex
     */
    public void setStartVertex(String startVertex){
        for (int i = 0; i < vertexNum; i++) {
            if (startVertex.equals(vertex[i])){
                this.startVertex = i;
                break;
            }
        }
    }
    public int[] getDistances() {
        return distances;
    }

    /**
     * dijkstra最短路径算法
     * @param startVertex
     */
    public void dijkstra(String startVertex){
        setStartVertex(startVertex);
        //初始化边距
        for (int i = 0; i < vertexNum; i++) {
            distances[i] = adjacencyMatrix[this.startVertex][i];
        }
        visited[this.startVertex]=true;
        path[0]=this.startVertex;
        //同起点的最短距离
        int min_cost;
        //权值最小的那个顶点的下标。(求好了)
        int min_cost_index=0;
        //主循环,除了源点,还剩下n-1个点
        for (int i = 0; i < vertexNum-1; i++) {
            min_cost = Integer.MAX_VALUE;
            // 找出当前未使用的点j的dist[j]最小值
            for (int j = 0; j < vertexNum; j++) {
                if (!visited[j] && distances[j]<min_cost){
                    min_cost = distances[j];
                    min_cost_index = j;
                }
            }
            //找到某一个点的最短距离
            visited[min_cost_index] = true;
            /**
             * 在通过新加入的min_cost_index点路径找到离v0点更短的路径,并且调整前驱;
             * 现在加入min_cost_index的点,获取与min_cost_index的点相关联的点(即跟min_cost_index点有路径的点),
             * 然后看看源点到这些点的距离,是否比新加入点到这些点的距离更近,
             * 有则更新distance,和path路径
             */
            for (int j = 0; j < vertexNum; j++) {
                //注意:这里不需要判断是否已经被访问,既然是更新,就是查看所有跟这个新加入的点相关联的点的路径长度
                if (adjacencyMatrix[min_cost_index][j] < Integer.MAX_VALUE ){
                    int min_temp = min_cost+adjacencyMatrix[min_cost_index][j];
                    if (min_temp < distances[j]){
                        distances[j] = min_temp;
                        path[j] = min_cost_index;
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        String[] vertex={"V1", "V2", "V3", "V4", "V5", "V6"};
        int vertexNum=vertex.length;
        int edgeNum=8;
        int[][] adjacencyMatrix={{0, Integer.MAX_VALUE, 10, Integer.MAX_VALUE, 50, Integer.MAX_VALUE},
                { Integer.MAX_VALUE, 0, 5, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE},
                { Integer.MAX_VALUE, Integer.MAX_VALUE, 0, 50, Integer.MAX_VALUE, Integer.MAX_VALUE},
                { Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, 10},
                { Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 20, 0, Integer.MAX_VALUE},
                { Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, 0}};
        Dijkstra dijkstra = new Dijkstra(adjacencyMatrix,vertexNum, vertex,edgeNum);

        String startVertex="V1";
        dijkstra.dijkstra(startVertex);

        int[] distance=dijkstra.getDistances();
        for (int i = 0; i < vertexNum; i++) {
            if (i != dijkstra.startVertex){
                //路径是反的,从目标点向前不断找前驱的过程
                if (distance[i] == Integer.MAX_VALUE){
                    System.out.printf(startVertex+" to " + vertex[i] +" 最短距离: 无穷大!");
                }else{
                    System.out.printf(startVertex+" to " + vertex[i] +" 最短距离: "+ distance[i]);
                    System.out.printf("--------------------路径是:"+vertex[dijkstra.startVertex]+"---");
                    int t = dijkstra.path[i];
                    while (t != dijkstra.startVertex){
                        System.out.printf("---"+vertex[t]);
                        t= dijkstra.path[t];
                    }
                    System.out.printf("---"+vertex[i]);
                }
                System.out.println("");

            }
        }
    }
}

参考:https://blog.csdn.net/qq_35644234/article/details/60870719

http://www.cnblogs.com/biyeymyhjob/archive/2012/07/31/2615833.html

猜你喜欢

转载自blog.csdn.net/qq_35923749/article/details/84672112