1. 程式人生 > >單源最短路徑--貪心演算法

單源最短路徑--貪心演算法

一個點(源點)到其餘各個頂點的最短路徑。也叫做“單源最短路徑”Dijkstra。
Dijkstra的主要思想:每次找到離源點最近的一個頂點,然後以該頂點為中心進行擴充套件,最終得到源點到其餘所有點的最短路徑
用flag標示該點是否在離源點最近的集合中
演算法步驟:
1.初始時,S只包含源點,即P={v},v的距離為0。U包含除v外的其他頂點,即:Q={其餘頂點},若v與U中頂點u有邊,則<u,v>正常有權值,若u不是v的出邊鄰接點,則<u,v>權值為∞。
2.從U中選取一個距離v最小的頂點k,把k,加入P中(該選定的距離就是v到k的最短路徑長度)。
3.以k為新考慮的中間點,修改U中各頂點的距離;若從源點v到頂點u的距離(經過頂點k)比原來距離(不經過頂點k)短,則修改頂點u的距離值,修改後的距離值的頂點k的距離加上邊上的權。
4.重複步驟2和3直到所有頂點都包含在P中
時間複雜度是O(N^2)。其中每次找到離1號頂點最近的頂點的時間複雜度是O(N),這裡可以用“堆”來優化使降低到O(logN),
  另外對於邊數M少於N^2的稀疏圖來說(M<<N^2的圖稱為稀疏圖,而M較大的圖稱為稠密圖),我們可以用鄰接表來代替鄰接矩陣儲存,使得整個時間複雜度優化到O(N+M)logN。

在最壞的情況下M就是N^2,這樣的話(N+M)logN要比N^2還要大,但是大多數情況下並不會有那麼多邊,因此(M+N)logN要比N^2小很多。


C++實現:

#include<iostream>
using namespace std;

int main()
{
	int edgs;
	int points;
	int dis[10];
	int flag[10];
	int infinity = 9999999;
	cin>>points>>edgs;
	int edg[10][10];

	//初始化有向圖的邊
	for(int i=1;i<=points;i++)
	{
		for(int j=1;j<=points;j++)
		{
			if(i==j)
			{
				edg[i][j]=0;
			}
			else
			{
				edg[i][j]=infinity;
			}
		}	
	}

	//給有向圖的邊賦權值
	int point1,point2,quanzhi;
	for(i=1;i<=edgs;i++)
	{
		cin>>point1>>point2>>quanzhi;
		edg[point1][point2]=quanzhi;
	}
	for(i=1;i<=points;i++)
	{
		dis[i]=edg[1][i];	
	}

	//設定標記,對訪問過的頂點i賦予flag[i]=1,沒訪問的i賦予flag[i]=0
	for(i=1;i<=points;i++)
	{
		flag[i]=0;
	}
	flag[1]=1;
	int min,u;

	//核心演算法
	for(i=1;i<=points;i++)
	{
		min=infinity;
		for(int j=1;j<points;j++)//源點到源點不用比較,總的迴圈次數少一次
		{
			if(flag[j]==0&&dis[j]<min)//核心思想:依次比較出離源點最近的點
			{
				min=dis[j];
				u=j;
			}
		}
		flag[u]=1;

		//找出離源點最近的點後開始更新dis裡面的源點到各個點的值是否最小	
		for(int v=1;v<=points;v++)
		{
			if(edg[u][v]<infinity)
			{
				if(dis[v]>dis[u]+edg[u][v]) //dis[1][v] 主要是找出離源點最近點的出邊來比較
				{
					dis[v]=dis[u]+edg[u][v];
				}					
			}
		}
	}
	
	for(i=1;i<=points;i++)
	{
		cout<<dis[i]<<" ";
	}
	cout<<endl;
}

結果: