單源最短路徑 -- Dijkstra演算法

Haskei發表於2016-11-04

求一個點(源點)到其餘點的最短路徑問題,利用f陣列記錄該節點的父節點,以便以後列印路經的時候,倒著回去的時候列印路徑

#include<iostream>        //沒有利用鄰接表儲存圖形結構的演算法,該種方法儲存圖的空間複雜度為O(n*n),遍歷每一條邊的時間複雜度為O(m)
#include<cstdio>          //演算法的時間複雜度為O(n*n)
#include<cstdlib>  
#include<cstring>         //有缺點,就是沒法解決帶有權值是負值的邊
#include<string>  
#include<queue>  
#include<algorithm>  
#include<map>  
#include<iomanip>  
#include<stack> 
#define INF 99999999  
#define MAX 20 
using namespace std;  
  
int dis[MAX];
int city[MAX][MAX];
int book[MAX];//標記陣列 ,為1代表該 dis[i]為確定的值,也就是真的最短路徑,0代表不確定該路徑是不是最短路徑
int f[MAX];//用來存放該節點的父節點是誰 
stack <int> a, b;
 
int main()
{
	int n,m,node;
	scanf("%d%d",&n,&m);//n點的個數,m邊的條數 
	
	//建立城市之前的關係圖,自身到自身距離為0,兩個城市無法到達則為INF 
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			if(i==j)
				city[i][j]=0;
			else city[i][j]=INF;
		}
	int x,y,r;
	//輸入題目給出的兩個城市之間的距離 
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&r);
		city[x][y]=r;
	}
	scanf("%d",&node);//輸入要求的那個點,求該點到其他點的最短路徑 
	
	//初始化dis陣列,裡面存放的是一開始,node點到其餘各點的路徑 
	for(int i=1;i<=n;i++) {
		dis[i]=city[node][i];
		if(dis[i] < INF)
			f[i] = 1;
	} 
	
	//初始化book陣列,其實可以把陣列定義main函式外面(即book陣列是全域性變數),book陣列裡面的每個元素都是0 
	memset(book,0,sizeof(book));
	book[node]=1;//自己到本身的距離是0,確定的,所以一開始就把book[node]標記為確定值 
	
	
	//Dijkstra演算法的核心語句 
	for(int t=1;t<n;t++)//一共需要計算多少次,因為當算到最後一個點的時候,沒有其他選擇了, 
	{                   // 該點的dis[i]就是最短路徑,確切值 
		int Min=INF,temp;
		for(int i=1;i<=n;i++)
		{
			if(book[i]==0 && dis[i]<Min)
			{
				Min=dis[i];
				temp=i;
			}
		}
		book[temp]=1;
		for(int i=1;i<=n;i++)
		{
			if(!book[i] && city[temp][i]<INF)
			{
				if(dis[i]>dis[temp]+city[temp][i])//dis[i]代表從1號頂點到i號定點的起始最短路徑,如果經過temp點一轉後 
					dis[i]=dis[temp]+city[temp][i];//路徑變短了,則更新這兩點之前的最短路徑(此時的最短路徑
					f[i] = temp;
			}                                      // 不一定是最短的),已經被book標記的dis[i]才是最短路徑,即book[i]=1 
		}
	}
	
	//列印出縮短後的路徑,city[i][j]代表i點到j點的最短距離 
	for(int i=1;i<=n;i++)
		printf("%d ",dis[i]);
	printf("\n");	

	int i = f[6];
	a.push(6);
	while(i != 1) {
		a.push(i);
		i = f[i];
	}
	printf("從1點到6點的最短路徑的走法:\n");
	printf("1 ");
	while(!a.empty()) {
		printf("%d ",a.top());
		a.pop();
	}
	printf("\n");
	return 0;
} 
/*測試資料 
6 9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 4
1 

程式執行效果
0 1 8 4 13 17 
從1點到6點的最短路徑的走法:
1 2 4 3 5 6
*/




(2)利用鄰接表來實現Dijkstra演算法,這是加入了鄰接表,是演算法的時間複雜度降低

建圖的空間複雜度為O(m),遍歷每一條邊的時間複雜度為O(m)

#include<iostream>
#include<cstring>
#include<string>
#include<queue>
#include<algorithm>
#include<map>
#include<iomanip>
#define inf 0x3f3f3f3f
#define maxn 20
using namespace std;
struct node{
    int to;
    int w;
    int next;
};
node edge[maxn];//儲存邊的資訊陣列
int book[maxn];//標記一下該條邊是否已經確定是最小邊了
int head[maxn];//存放i節點的第一條邊在edge陣列裡面的位置
int dis[maxn];//存放num點到每個點的最短距離
int n,m,num;//num為要求的點,即該點到其他點的最短距離

int main()
{
    scanf("%d%d%d",&n,&m,&num);
    memset(head,-1,sizeof(head));//初始化為-1的是head陣列
    memset(dis, inf, sizeof(dis));
    memset(book, 0,sizeof(book));
    for(int i=0;i<m;i++)//鄰接表的建立過程
    {
        int front;
        scanf("%d%d%d",&front,&edge[i].to,&edge[i].w);
        edge[i].next=head[front];
        head[front]=i;
    }
    int k=head[num];//以num為節點,遍歷與它相連的每一條邊
    dis[num]=0;//到本身的距離為0
    book[num]=1;
    while(k!=-1)//也算是對dis陣列初始化的一部分
    {
        dis[edge[k].to]=edge[k].w;
        k=edge[k].next;
    }
    
    for(int i=1;i<n;i++)//一共要計算n-1個點,因為剩下最後一個點後,該點的dis距離就是最短的
    {
        int Min=inf,t;
        for(int j=1;j<=n;j++)//遍歷dis陣列裡面的每一條邊,找出離num點最近的點
        {
            if(book[j]==0 && Min>dis[j])
            {
                Min=dis[j];
                t=j;
            }
        }
        book[t]=1;
        int l=head[t];
        while(l!=-1)//遍歷t點所連線的每一條邊,看能否鬆弛
        {
            //edge[l].w代表第l條邊的權值,dis[t]代表num點到t點的最短距離
            //edge[l].to代表第l條邊的終點,dis[edge[l].to]代表num點到edge[l].to的距離
            if(!book[edge[l].to] && edge[l].w+dis[t]<dis[edge[l].to])
                dis[edge[l].to]=edge[l].w+dis[t];
            l=edge[l].next;//l邊的下一條邊,即與t點相連的下一條邊
        }
    }
    printf("\n");
    for(int i=1;i<=n;i++)
    {
        printf("%d ",dis[i]);
    }
    printf("\n");
    
    return 0;
}
/*
 輸入資料
 6 9 1
 1 2 1
 1 3 12
 2 3 9
 2 4 3
 3 5 5
 4 3 4
 4 5 13
 4 6 15
 5 6 4
 
 輸出結果
 0 1 8 4 13 17 
 */



(3)繼續對上面的第二種演算法進行優化,在判斷離dis陣列最近距離的點的時候利用堆排序,是該部分演算法的時間複雜度由O(N)降低到O(logN)

相關文章