1. 程式人生 > >最短路徑演算法—Dijkstra(迪傑斯特拉)演算法分析與實現(C/C++)及其他 + leetcode習題實踐

最短路徑演算法—Dijkstra(迪傑斯特拉)演算法分析與實現(C/C++)及其他 + leetcode習題實踐

   最短路徑求解

 最短路徑的常用解法有迪傑克斯特拉演算法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 743Network 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:

  1. N will be in the range [1, 100].
  2. K will be in the range [1, N].
  3. The length of times will be in the range [1, 6000].
  4. 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;
    }
};