最短路径算法(上)——迪杰斯特拉(Dijikstra)算法
来源:互联网 发布:mac删除最近使用记录 编辑:程序博客网 时间:2024/06/05 10:27
概述
单源最短路径问题,即在图中求出给定顶点到其它任一顶点的最短路径。在弄清楚如何求算单源最短路径问题之前,必须弄清楚最短路径的最优子结构性质。
最短路径的最优子结构性质描述为:如果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。
算法如下:
//无权图的Dijikstravoid 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;}
截图:
- 最短路径算法(上)——迪杰斯特拉(Dijikstra)算法
- HDU 3790最短路径问题 dijikstra算法
- 最短路径算法(1)—Dijkstra(迪杰斯特拉)算法
- 迪杰斯特拉算法(dijkstra)——最短路径
- 经典算法——迪杰斯特拉(Dijkstra)最短路径
- 最短路径算法(3)—Floyd(弗洛伊德)算法
- 最短路径——迪杰斯特拉算法
- 最短路径——迪杰斯特拉算法
- 最短路径—迪杰斯特拉算法
- 最短路径算法—Dijkstra(迪杰斯特拉)
- 最短路径算法—Dijkstra(迪杰斯特拉)
- 最短路径(迪杰斯特拉算法)
- 最短路径(迪杰斯特拉算法)
- 迪杰斯特拉(最短路径)算法
- 图(最短路径算法————迪杰斯特拉算法和弗洛伊德算法)
- 最短路径算法Dijkstra算法(迪杰斯特拉算法)
- 最短路径(五)—最短路径算法对比分析
- 最短路径算法(floyd算法)
- Vector容器删除元素
- 最全Pycharm教程(11)——Pycharm调试器之断点篇
- CCF CSP 认证真题部分题解
- spoj1811 Longest Common Substring(LCS)最长公共子串
- 【兴趣书签】SOHO生活——理想的生活方式
- 最短路径算法(上)——迪杰斯特拉(Dijikstra)算法
- Cocos creator中出现Uncaught TypeError: Cannot read property 'node' of undefined
- mysql的水平拆分和垂直拆分
- 5分钟内的Maven
- linux命令替换
- 尼姆博弈
- java集合框架TreeSet的使用和HashSet的使用
- 数组基础
- 什么是Rup,介绍一下Rup。