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

原创 2017年09月12日 21:30:15

概述

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

最短路径的最优子结构性质描述为:如果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;
}

截图:
这里写图片描述

版权声明:本文为博主原创文章,未经博主允许不得转载。若需转载,请注明http://blog.csdn.net/qq_30091945 举报

相关文章推荐

最短路径算法Dijkstra算法(迪杰斯特拉算法)

最短路径算法Dijkstra算法(迪杰斯特拉算法) 算法过程 算法思想 个人理解 其实就是借住已知的最短路径求下一条最短路径 不断更新起始点到各个定点的最...

贪心算法 迪杰斯特拉算法求最短路径

之前我们学习过弗洛伊德算法求最短路径,但是使用了三重循环,导致时间复杂度是O(n^3),而迪杰斯特拉算法应该是求最短路径的最好的算法了。迪杰斯特拉算法原理迪杰斯特拉算法实际上是使用贪心算法和bfs来求...

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

Java迪杰斯特拉算法(Dijkstra算法)求最短路径

Dijkstra

最短路径算法(1)—Dijkstra(迪杰斯特拉)算法

转载来源:http://www.wutianqi.com/?p=1890

最短路径算法之 Dijkstra(迪杰斯特拉)算法

Dijkstra算法: 步骤: a.  初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,即: U={其余顶点},若v与U中顶点u有边,则u的距离设置为相应的权值,若u v之间...

算法:最短路径之迪杰斯特拉(Dijkstra)算法

对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点为源点,最后一个顶点为终点。最短路径的算法主要有迪杰斯特拉(Dijkstra)算法和弗洛伊德(Floyd...

迪杰斯特拉求最短路径

迪杰斯特拉求最短路径

最短路径实现-迪杰斯特拉算法-java实现

/** * 最短路径算法:迪杰斯特拉(Dijkstra)算法 * * @author timmy1 * */public class ShortestPath { int[][] matrix;//...

最短路径——迪杰斯特拉算法

概述 用于计算一个节点到其他所有节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。Dijkstra算法能得出最短路径的最优解,但由于它遍历计算的节点很多,所以效率低。 算法...

POJ 3268 Silver Cow Party(最短路径之迪杰斯特拉算法)

One cow from each of N farms (1 ≤ N ≤ 1000) conveniently numbered 1..N is going to attend the big co...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)