您的位置:首页 > 编程语言 > C语言/C++

最短路径算法—Dijkstra(迪杰斯特拉)算法分析与实现(C/C++)及其他 + leetcode习题实践

2016-12-17 01:54 1006 查看


   最短路径求解

   
 最短路径的常用解法有迪杰克斯特拉算法Dijkstra Algorithm, 弗洛伊德算法Floyd-Warshall Algorithm, 和贝尔曼福特算法Bellman-Ford Algorithm,其中,Floyd算法是多源最短路径,即求任意点到任意点到最短路径,而Dijkstra算法和Bellman-Ford算法是单源最短路径,即单个点到任意点到最短路径。

单源最短路径(无负权值):

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

Dijkstra算法是很有代表性的最短路算法,在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等。

其基本思想是,设置顶点集合S并不断地作贪心选择来扩充这个集合。一个顶点属于集合S当且仅当从源到该顶点的最短路径长度已知。

初始时,S中仅含有源。设u是G的某一个顶点,把从源到u且中间只经过S中顶点的路称为从源到u的特殊路径,并用数组dist记录当前每个顶点所对应的最短特殊路径长度。Dijkstra算法每次从V-S中取出具有最短特殊路长度的顶点u,将u添加到S中,同时对数组dist作必要的修改。一旦S包含了所有V中顶点,dist就记录了从源到所有其它顶点之间的最短路径长度。






代码实现

// AllTest.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <stack>
using namespace std;
#define INT_MAX1 999999
typedef struct MGraph
{
int n,line;
int m[100][100];
}gg,*pG;
// g -- adjacent matrix pointer
// v0 -- the source node
// dist[] -- the distance from the ith node to the source node
// prev[] -- the previous node of the ith node
void Dijkstra (pG g,int *pre,int *dis,int v0)
{
bool visited[100];  // 判断是否已存入该点到S集合中
int n=g->n;
for (int i=1;i<=n;i++)
{
dis[i]=g->m[v0][i]; //初始dis
visited[i]=false;  // 初始都未用过该点
if (dis[i]==INT_MAX1)  //初始pre
pre[i]=0;
else
pre[i]=v0;
}
visited[v0]=true; //初始起点
dis[v0]=0;
// 依次将未放入S集合的结点中,取dis[]最小值的结点,放入结合S中
// 一旦S包含了所有V中顶点,dis就记录了从源点到所有其他顶点之间的最短路径长度
// 注意是从第二个节点开始,第一个为源点
for (int i=2;i<=g->n;i++) //1
{
int u=v0,min=INT_MAX1;
// 找出当前未使用的点j的dis[j]最小值
for (int j=1;j<=g->n;j++) //
if (!visited[j]&&dis[j]<min)
{
u=j; // u保存当前邻接点中距离最小的点的号码
min=dis[j];
}
visited[u]=true;
dis[u]=min;//其实这个不需要再赋值
//更新dis
for (int j=1;j<=g->n;j++)
if (!visited[j]&&g->m[u][j]<INT_MAX1&&dis[u]+g->m[u][j]<dis[j])
{
pre[j]=u;
dis[j]=dis[u]+g->m[u][j];
}
}

}
// 查找从源点v到终点u的路径,并输出
void research(pG g,int *pre,int v0,int u)
{
int t=pre[u];
stack<int> s;
s.push(u);
while (t!=v0)
{
s.push(t);
t=pre[t];
}
s.push(v0);
while (!s.empty())
{
int output=s.top();
s.pop();
if (output==v0)
cout<<v0;
else
cout<<"->"<<output;
}
}
int main()
{
MGraph g;
freopen("input.txt","r",stdin);
cin>>g.n;
cin>>g.line;
for (int i=1;i<=g.n;i++)
for (int j=1;j<=g.n;j++)
g.m[i][j]=INT_MAX1;
int dis[100];
generate(dis,dis+100,[](){return INT_MAX1;});
int pre[100];
generate(pre,pre+100,[](){return 1;});
for (int i=1;i<=g.line;i++)
{
int x,y,weight;
cin>>x>>y>>weight;
g.m[x][y]=weight;
//g.m[y][x]=weight;//无向图
}
for(int i=1; i<=g.n; ++i)
{
for(int j=1; j<=g.n; ++j)
printf("%8d", g.m[i][j]);
printf("\n");
}
Dijkstra(&g,pre,dis,1);
cout<<"源点到最后一个顶点的最短路径长度: "<< dis[g.n] << endl;
// 路径
cout << "源点到最后一个顶点的路径为: ";
research(&g,pre,1,g.n);
return 0;
}

数据

5

7

1 2 10

1 4 30

1 5 100

2 3 50

3 5 10

4 3 20

4 5 60

单源最短路径(有负权值)

任意两点最短路径

LeetCode 743. Network
Delay Time

Desciption

There are 
N
 network nodes, labelled 
1
 to 
N
.

Given 
times
, a list of travel times as directed edges 
times[i]
= (u, v, w)
, where 
u
 is the source node, 
v
 is
the target node, and 
w
 is the time it takes for a signal to travel from source to target.

Now, we send a signal from a certain node 
K
. How long will it take for all nodes to receive the signal?
If it is impossible, return 
-1
.

Note:

N
 will be in the range 
[1,
100]
.
K
 will be in the range 
[1,
N]
.
The length of 
times
 will be in the range 
[1,
6000]
.
All edges 
times[i] = (u, v, w)
 will have 
1
<= u, v <= N
 and 
1 <= w <= 100
.

解题思路: 

 基于Bellman-Ford算法的解法,时间复杂度是O(VE),V和E分别是结点和边的个数。这种算法是基于DP来求全局最优解,原理是对图进行V
- 1次松弛操作,这里的V是所有结点的个数(为啥是V-1次呢,因为最短路径最多只有V-1条边,所以只需循环V-1次),在重复计算中,使得每个结点的距离被不停的更新,直到获得最小的距离,这种设计方法融合了暴力搜索之美,写法简洁又不失优雅。之前提到了,Bellman-Ford算法可以处理负权重的情况,但是不能有负环存在,一般形式的写法中最后一部分是检测负环的,如果存在负环则报错。不能有负环原因是,每转一圈,权重和都在减小,可以无限转,那么最后的最小距离都是负无穷,无意义了。没有负环的话,V-1次循环后各点的最小距离应该已经收敛了,所以在检测负环时,就再循环一次,如果最小距离还能更新的话,就说明存在负环。这道题由于不存在负权重,所以就不检测了。

class Solution {
public://最短路径中的最大值
int networkDelayTime(vector<vector<int>>& times, int N, int K) {
vector<int> dist(N+1,INT_MAX);
dist[K]=0;
for (int i=1;i<N;++i)
for (auto e:times){
int u=e[0],v=e[1],w=e[2];
if (dist[u]!=INT_MAX&&dist[u]+w<dist[v])//update edge of u vertex
dist[v]=dist[u]+w;
}
int max=0;
for (int i=1;i<=N;++i)
max=(dist[i]>max?dist[i]:max);
return max==INT_MAX?-1:max;
}
};


参考网址:http://developer.51cto.com/art/201105/262210.htm
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐