关闭

[置顶] 最短路径算法(上)——迪杰斯特拉(Dijikstra)算法

标签: 数据结构迪杰斯特拉Dijikstra
1060人阅读 评论(1) 收藏 举报
分类:

概述

单源最短路径问题,即在图中求出给定顶点到其它任一顶点的最短路径。在弄清楚如何求算单源最短路径问题之前,必须弄清楚最短路径的最优子结构性质。

最短路径的最优子结构性质描述为:如果P(i,j)={Vi….Vk..Vs…Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径。下面证明该性质的正确性。

假设P(i,j)={Vi….Vk..Vs…Vj}是从顶点i到j的最短路径,则有P(i,j)=P(i,k)+P(k,s)+P(s,j)。而P(k,s)不是从k到s的最短距离,那么必定存在另一条从k到s的最短路径P’(k,s),那么P’(i,j)=P(i,k)+P’(k,s)+P(s,j)

无权图的最短路径算法

对于无权图来说,可以把它当作每条边都为1的有权图。故无权图的最短路径算法如下:
1)初始化距离数组dist和路径数组path全为-1,同时定义一个队列queue,初始化队列为空。
2)把源点vertex入队并更新dist[vertex]=0
3)当队列不空是一直循环,利用cur_vertex保存出队元素,遍历cur_vertex的每个邻接点i,若dist[i] =-1,那么将其入队,更新dist[i] = dist[cur_vertex]+1,path[i] = cur_vertex。
算法如下:

//无权图的Dijikstra
void Unweighted(int vertex){
    queue<int> queue;       //初始化队列
    queue.push(vertex);     //初始结点入队
    int cur_vertex;         //当前结点 
    this->dist[vertex] = 0; //初始结点的距离为0 
    while(!queue.empty()){
        cur_vertex = queue.front(); //队头结点出队 
        queue.pop();
        //遍历cur_vertex的每个邻接点 
        for(int i = 1  ; i < this->Nv+1 ; i++){
            if((this->G[cur_vertex][i] == 1)&& (this->dist[i] == -1)){
            //当前结点的距离是cur_vertex的距离加1 
                this->dist[i] = this->dist[cur_vertex]+1;
            //把当前结点的上一个结点设为cur_vertex;
                this->path[i] = cur_vertex; 
                queue.push(i); 
            }
        }
    }
}

Dijikstra算法

Dijikstra算法主要是针对有权图的最短路径问题提出的,且具体问题中不能出现权值为负的边,即负值圈问题,如下图所示:
这里写图片描述
对于Dijikstra算法的理解,首先得从最短路径的最优子结构说起。(这部分引用海子的博客园的Dijkstra算法(单源最短路径)一文的说法)

最短路径的最优子结构性质
该性质描述为:如果P(i,j)={Vi….Vk..Vs…Vj}是从顶点i到j的最短路径,k和s是这条路径上的一个中间顶点,那么P(k,s)必定是从k到s的最短路径。下面证明该性质的正确性。

假设P(i,j)={Vi….Vk..Vs…Vj}是从顶点i到j的最短路径,则有P(i,j)=P(i,k)+P(k,s)+P(s,j)。而P(k,s)不是从k到s的最短距离,那么必定存在另一条从k到s的最短路径P(k,s),那么P(i,j)=P(i,k)+P(k,s)+P(s,j)

//有权图的Dijikstra(遍历整个数组寻找最小路径顶点)
bool Dijikstra(int vertex){
    //根据初始结点初始化距离数组与路径数组 
    for(int i = 0 ; i < this->Nv+1 ; i++){
        //在构造函数里dist已经全部初始化为MAX
        //G存在边时为权重,没有边时为MAX 
        this->dist[i] = this->G[vertex][i];
        if(this->dist[i] < MAX){
            this->path[i] = vertex;
        }
    }
    this->dist[vertex] = 0;     //初始结点的距离为0
    this->collected[vertex] = 1;    //初始结点标记为已收录 
    while(1){
        //V是未被收录定点中dist最小者 
        int V = this->FindMinVertex(); 
        if(V == -1){//未找到这样的V则跳出循环 
            break;
        } 
        this->collected[V] = 1;//标记为已经被收录 
        //遍历图中每个顶点 
        for(int w = 1 ; w < this->Nv+1 ; w++){
            //若w是V的邻接点且未被收录 
            if(this->collected[w] == 0 && this->G[V][w] < MAX){
                if(this->G[V][w] < 0){//存在负边时 
                    return false;   //结束算法 
                }
                //若收录V使得dist[w]变小 
                if(this->dist[V] + this->G[V][w] < this->dist[w]){
                    //更新dist[w] 
                    this->dist[w] = this->dist[V] = this->G[V][w]; 
                    this->path[w] = V;//更新路径 
                } 
            } 
        } 
    }
    return true;
}

例子

对于无权图的最小路径我们基于如下的图模型:
这里写图片描述
全部代码:

#include <iostream>
#include <cstring>
#include <queue>
#include <stack>
using namespace std;

class Graph{
    private:
        int** G;                //邻接矩阵 
        int* dist;              //距离数组
        int* path;              //路径数组 
        int Nv;                 //顶点数
        int Ne;                 //边数 
    public:
        //构造函数 
        Graph(int nv , int ne){
            this->Nv = nv;
            this->Ne = ne;
            this->G = new int*[nv+1];
            this->dist = new int[nv+1];
            this->path = new int[nv+1];
            memset(this->dist,-1,sizeof(this->dist[0])*(nv+1));
            memset(this->path,-1,sizeof(this->path[0])*(nv+1));
            for(int i = 0 ; i < nv+1 ; i++){
                G[i] = new int[nv+1];
                memset(G[i],0,sizeof(G[i][0])*(nv+1));
            }
            cout<<"请输入边:"<<endl;
            for(int i = 0 ; i < ne ; i++){
                int a,b;
                cin>>a>>b;
                this->G[a][b] = 1;
                this->G[b][a] = 1;
            }
        }

        //无权图的Dijikstra
        void Unweighted(int vertex){
            queue<int> queue;       //初始化队列
            queue.push(vertex);     //初始结点入队
            int cur_vertex;         //当前结点 
            this->dist[vertex] = 0;     //初始结点的距离为0 
            while(!queue.empty()){
                cur_vertex = queue.front(); //队头结点出队 
                queue.pop();
                //遍历cur_vertex的每个邻接点 
                for(int i = 1  ; i < this->Nv+1 ; i++){
                    if((this->G[cur_vertex][i] == 1)&& (this->dist[i] == -1)){
                        this->dist[i] = this->dist[cur_vertex]+1;//当前结点的距离是cur_vertex的距离加1 
                        this->path[i] = cur_vertex; //把当前结点的上一个结点设为cur_vertex;
                        queue.push(i); 
                    }
                }
            }
        }

        //打印无权图迪杰斯特拉路径
        void Print_Unweighted(int vertex){
            for(int i = 1 ; i < this->Nv+1 ; i++){
                stack<int> stack;
                stack.push(i);
                cout<<vertex<<"到"<<i<<"的最短路径为:";
                int j = i;
                while(this->path[j] != -1){//路径上的元素一次入栈 
                    j = this->path[j];
                    stack.push(j);  
                }
                //打印路径 
                cout<<stack.top();
                stack.pop(); 
                while(!stack.empty()){
                    cout<<" -> "<<stack.top();
                    stack.pop();
                }
                cout<<endl;
            }
        }
};

int main() 
{
    cout<<"请输入顶点数与边数:"<<endl; 
    int nv ,ne;
    cin>>nv>>ne;
    Graph graph(nv,ne);
    cout<<"请输入一个起始点:"<<endl;
    int vertex;
    cin>>vertex;
    graph.Unweighted(vertex);
    graph.Print_Unweighted(vertex); 

    return 0;
}

截图:
这里写图片描述

对于有权图的最短路径算法(Dijikstra)我们基于如下图模型:
这里写图片描述
全部代码:

#include <iostream>
#include <cstring>
#include <stack>
#include <stdio.h>
using namespace std;

const int MAX = 65535;

class Graph{
    private:
        int** G;                //邻接矩阵 
        int* dist;              //距离数组
        int* path;              //路径数组
        int* collected;         //收录数组 
        int Nv;                 //顶点数
        int Ne;                 //边数 
    public:
        //构造函数 
        Graph(int nv , int ne){
            this->Nv = nv;
            this->Ne = ne;
            this->G = new int*[nv+1];
            this->dist = new int[nv+1];
            this->path = new int[nv+1];
            this->collected = new int[nv+1]; 
            for(int i = 0 ; i < this->Nv+1 ; i++){
                this->dist[i] = MAX;
            }
            memset(this->path,-1,sizeof(this->path[0])*(nv+1));
            memset(this->collected,0,sizeof(this->collected[0])*(nv+1));
            for(int i = 0 ; i < nv+1 ; i++){
                this->G[i] = new int[nv+1];
                for(int j = 0 ; j < nv+1 ; j++){
                    this->G[i][j] = MAX; 
                }
            }
            cout<<"请输入边与权重:"<<endl;
            for(int i = 0 ; i < ne ; i++){
                int v1,v2,weight;
                cin>>v1>>v2>>weight;
                this->G[v1][v2] = weight;
                this->G[v2][v1] = weight;
            }
        }

        //遍历邻接点寻找最小距离顶点
        int FindMinVertex(){
            int MinDist = MAX;  //初始化最小距离
            int v,MinV = 0;
            for(v = 1 ; v < this->Nv+1 ; v++){
                if(this->collected[v] == 0 && this->dist[v] < MinDist){
                    //v没有被收录且dist[v]更小
                    MinDist = dist[v];
                    MinV = v; 
                }
            }
            if(MinDist < MAX){//找到最小的dist 
                return MinV;    //返回对应顶点的下标 
            }else{
                return -1;  //若这样的顶点不存在则返回-1 
            } 
        } 

        //有权图的Dijikstra(遍历整个数组寻找最小路径顶点)
        bool Dijikstra(int vertex){
            //根据初始结点初始化距离数组与路径数组 
            for(int i = 0 ; i < this->Nv+1 ; i++){
                //在构造函数里dist已经全部初始化为MAX
                //G存在边时为权重,没有边时为MAX 
                this->dist[i] = this->G[vertex][i];
                if(this->dist[i] < MAX){
                    this->path[i] = vertex;
                }
            }
            this->dist[vertex] = 0;     //初始结点的距离为0
            this->collected[vertex] = 1;    //初始结点标记为已收录 
            while(1){
                //V是未被收录定点中dist最小者 
                int V = this->FindMinVertex(); 
                if(V == -1){//未找到这样的V则跳出循环 
                    break;
                } 
                this->collected[V] = 1;//标记为已经被收录 
                //遍历图中每个顶点 
                for(int w = 1 ; w < this->Nv+1 ; w++){
                    //若w是V的邻接点且未被收录 
                    if(this->collected[w] == 0 && this->G[V][w] < MAX){
                        if(this->G[V][w] < 0){//存在负边时 
                            return false;   //结束算法 
                        }
                        //若收录V使得dist[w]变小 
                        if(this->dist[V] + this->G[V][w] < this->dist[w]){
                            //更新dist[w] 
                            this->dist[w] = this->dist[V] = this->G[V][w]; 
                            this->path[w] = V;//更新路径 
                        } 
                    } 
                } 
            }
            return true;
        }

        //打印迪杰斯特拉路径
        void Print_Dijikstra(int vertex){
            for(int i = 1 ; i < this->Nv+1 ; i++){
                if(i == vertex){
                    continue;
                } 
                stack<int> stack;
                stack.push(i);
                cout<<vertex<<"到"<<i<<"的最短路径为:";
                int j = i;
                while(this->path[j] != -1){//路径上的元素一次入栈 
                    j = this->path[j];
                    stack.push(j);  
                }
                //打印路径 
                cout<<stack.top();
                stack.pop(); 
                while(!stack.empty()){
                    cout<<" -> "<<stack.top();
                    stack.pop();
                }
                cout<<endl;
            }
        }
};

int main() 
{
    cout<<"请输入顶点数与边数:"<<endl; 
    int nv ,ne;
    cin>>nv>>ne;
    Graph graph(nv,ne);
    cout<<"请输入一个起始点:"<<endl;
    int vertex;
    cin>>vertex;
    if(graph.Dijikstra(vertex)){
        graph.Print_Dijikstra(vertex);  
    }

    return 0;
}

截图:
这里写图片描述

2
0
查看评论

floyd ,dijikstra,Bellman-ford算法详解

几乎是瞬间,自己已经是大二了,真得时间过得好快,大一掠过,只有一些美好的回忆留在心头。 不扯淡了,言归正传。图论,这名字起的太大了,其实就是一些最基本的算法,用于解决图上的最短距离的算法。 第一个是floyd 算法 很简单直白的算法,是使用邻接矩阵来求最短路的算法,其实就是通过点来松弛两点之间的距离...
  • qq_32635069
  • qq_32635069
  • 2016-09-19 21:49
  • 171

最短路径问题---Dijkstra算法详解

前言 Nobody can go back and start a new beginning,but anyone can start today and make a new ending. Name:Willam Time:2017/3/81、最短路径问题介绍问题解释: 从图中的某个...
  • qq_35644234
  • qq_35644234
  • 2017-03-08 16:42
  • 27153

迷宫问题 与 最短路径 怎样记录路径的总结(dijikstra,bfs,floyd,优先队列)

这次集训做了几个关于记录路径的问题,大体基于迪杰斯特拉(dijikstra)和弗洛伊德(floyd)算法还有BFS广搜。 记录前驱要比记录后驱更保险,因为从终点往起点追溯很容易,而从起点往后追溯有很多岔路口。 以下给出几种记录路径的方法。 1、自定义队列(针对BFS)。 参考题目: 自己定义了一...
  • winter2121
  • winter2121
  • 2017-03-15 20:28
  • 687

最短路dijkstra算法详解:dijkstra(图解)(详

本人小白,如果有写的不恰当的地方,还请大家指出,共同进步学习。 ----------------------------------------------------------------------------------------------------------------------...
  • mengxiang000000
  • mengxiang000000
  • 2015-12-28 20:23
  • 11042

Dijikstra(单源最短路径)

Dijikstra(单源最短路径)求无向图中两点间的最短路径 时间复杂度O(N2)代码#include<stdio.h> #include<stdlib.h> #define max 100 #define maxint 32767typedef struct Node {...
  • qingfengpapa
  • qingfengpapa
  • 2015-05-24 19:30
  • 285

Dijistra算法模版

{} const      maxn=10000; type     link=^node;     node=record x,dis:longint; next:link; end; var {data of dijistra...
  • w745241408
  • w745241408
  • 2012-02-19 20:54
  • 265

弗洛伊德(Floyd)算法求任意两点间的最短距离

最近要搞最短路径方面的工作,把2年前搞过的北京地铁换乘算法拿出来看看,顺带整理下写出来,和大家分享下,算是抛砖引玉吧 Floyd-Warshall算法(Floyd-Warshall algorithm)是解决任意两点间的最短路径的一种算法,可以正确处理带权有向图或负权的最短路径问题 解决此问...
  • ha000
  • ha000
  • 2016-08-30 14:06
  • 1049

求任意两点间的最短路径

求图中的任意两点间的最短距离,我们可以简单的n次调用Dijikstra方法,虽然算法的效率也是N的三次方,但是用Floyd算法更简洁,执行起来比前者更高效 void Floyed(A,C,n) { for(int i=1;i for(int j=1;j  A[i][j]=C[i]...
  • hitxueliang
  • hitxueliang
  • 2012-03-06 21:18
  • 658

Dijkstra [迪杰斯特拉]算法思路(求单点到其他每个点的各个最短路径)Floyd算法:任意两点间最短距离

先给出一个无向图 用Dijkstra算法(迪杰斯特拉算法)找出以A为起点的单源最短路径步骤如下 应用Dijkstra算法计算从源顶点1到其它顶点间最短路径的过程列在下表中。 Dijkstra算法的迭代过程: Floyd算法思想: 1、从任意一条单边路径开始。所有...
  • lingzhm
  • lingzhm
  • 2015-03-30 19:50
  • 4393

迪杰斯特拉算法求任意两点间最短路径 无向图

  • 2010-01-04 21:50
  • 3.59MB
  • 下载
    个人资料
    • 访问:155737次
    • 积分:4932
    • 等级:
    • 排名:第6843名
    • 原创:329篇
    • 转载:0篇
    • 译文:0篇
    • 评论:81条
    博客专栏
    最新评论