Dijkstra 邻接矩阵 单源点最短路径

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">一、定义</span>
<span style="font-family: 新宋体; font-size: 14px; orphans: 2; text-align: -webkit-auto; widows: 2; background-color: rgb(255, 255, 255);">     最短路径定义:在图中假设边有权值,从一点u到另一点v,如果有多条路径,其中路径权值和最小的路径是最短路径(一条路径的权值等于这条路径上所有边权值加和)</span>

     单源点最短路径定义:给定一个源点s,求s到所有点的最短路径。 二、解决的问题      Dijkstra解决的是单源点最短路径问题,解决的是满足特定条件的图的最短路径问题,条件如下:            1. 边权值 >= 0

     Dijkstra算法是选出的最短路径权值是递增的,如果出现负权值的边,Dijkstra不能保证结果正确。

三、用到的技术      松弛:      最短路径算法都用到“松弛”技术,即是否能对当前s到v的距离进行改善?表示为:if d[v] > d[u]+w[u,v],不同算法对边松弛的次数和顺序不同。       四、算法详解      给出算法描述(参考<<算法导论>>第三版P383)            Dijkstra(G,w,s)      1 INITIALIZE-SINGLE-SOUREC(G,s)      2 S=
Ø
          
3 Q=G.V
     4 while Q!=
Ø
     5      u=EXTRACT-MIN(Q)
     6      S=S
∪{u}
     7      for each vertex v∈G.adj[u]
     8           RELAX(u,v,w)


     符号解释:
     INITIALIZE-SINGLE-SOUREC(G,s): 初始化操作,主要是两点,使源点s到所有顶点距离d[s,v]等于∞,以及对指示顶点是否已得到最短路径的数组finished[i]置false,(特别的: 初始化d[s] = 0)      S: 大S表示已经得到最短路径的顶点集合      Q: 表示还未得到最短路径的顶点集合      s: 小s表示源点      
EXTRACT-MIN(Q): 从未得到最短路径的顶点集合中选出一个当前d[s,v]最小的顶点v’,
v’就是这一轮选出的顶点,表示
v’已经找到最短路径      RELAX: 进行松弛操作,查看是否存在更短的路径,改善距离d[v]

     算法解释:      第1行:初始化      第2行:已得到最短路径顶点集合S置空      第3行:未得到最短路径顶点集合Q初始化为图中所有顶点      第5-6行:
从未得到最短路径的顶点集合中选出一个当前d[s,v]最小的顶点v’,加入S集合,
v’就是这一轮选出的顶点,表示
v’已经找到最短路径
     第7-8行:对上一步得到的v’,对v’与其邻接点中未访问过的边进行松弛,即查看通过v’到达其邻接点是否更近
     重复4-8,V次,找出s到所有顶点的最短路径


     Dijkstra 算法每轮选出一个顶点的最短路径,最短路径是以递增的过程不断选择出来。
     
     下面用图和表来演示Dijkstra如何求得最短路径。
     原图:                                      邻接矩阵:
     
《Dijkstra 邻接矩阵 单源点最短路径》                 
《Dijkstra 邻接矩阵 单源点最短路径》            给定源点V0,初始化得到表格Table 1                                   Table 1  初始化
              
《Dijkstra 邻接矩阵 单源点最短路径》      i:表示第几次迭代      V
finished:表示当前次迭代选出的顶点(就是已经求得最短路径的顶点)      S:表示已经得到最短路径的顶点集合      右下方的单元格中,上方表示当前V0到Vi的临时最短距离,下方表示临时最短路径

     i=0  第0次迭代           选出当前距离最小顶点V0,将V0加入S集合,对V0出发的边进行松弛(if d[V] > d[V0] + w[V0,V’],更新,V’是V0邻接点),得到Table 2                                               Table 2  第0次迭代后结果          
《Dijkstra 邻接矩阵 单源点最短路径》     
《Dijkstra 邻接矩阵 单源点最短路径》                    第0次迭代后,发现通过V0到达V2、V4、V5的距离更近,更新表格           1) 更新距离 d[i] = d[V0] + w[V0,Vi]           2) 更新被更新顶点的最短路径,先将V0的最短路径复制过来,再加上(V0,Vi)           V0最短路径是V0,加上(V0,V2)构成V2当前最短路径V0->V2,加上(V0,V4)构成V4当前最短路径V0->V4,
加(V0,V5)构成V5当前最短路径V0->V5            i=1  第1次迭代          选出当前距离最小顶点V2,将V2加入S集合,对V2出发的边进行松弛(if d[V] > d[V2] + w[V2,V’],更新,V’是V2邻接点),得到Table 3                                                 Table 3 第1次迭代后的结果          
《Dijkstra 邻接矩阵 单源点最短路径》       
《Dijkstra 邻接矩阵 单源点最短路径》                第1次迭代后,发现通过V2到达V3的距离更近,更新表格           1) 更新距离 d[i] = d[V2] + w[V2,Vi]           2) 更新被更新顶点的最短路径,先将V2的最短路径复制过来,再加上(V2,Vi)           V2最短路径是V0->V2,加上(V2,V3)构成V3当前最短路径V0->V2->V3

     i=2  第2次迭代          选出当前距离最小顶点V4,将V4加入S集合,对V4出发的边进行松弛(if d[V] > d[V4] + w[V4,V’],更新,V’是V4邻接点),得到Table 4                                            Table 4  第2次迭代后结果          
《Dijkstra 邻接矩阵 单源点最短路径》 
《Dijkstra 邻接矩阵 单源点最短路径》                        第2次迭代后,发现通过V4到达V3、V5的距离更近,更新表格           1) 更新距离 d[i] = d[V3] + w[V4,Vi]           2) 更新被更新顶点的最短路径,先将V4的最短路径复制过来,再加上(V4,Vi)           V4最短路径是V0->V4,加上(V4,V3)构成V3当前最短路径V0->V4->V3,加上(V4,V5)构成V5当前最短路径V0->V4->V5

     i=3  第3次迭代          选出当前距离最小顶点V3,将V3加入S集合,对V3出发的边进行松弛(if d[V] > d[V3] + w[V3,V’],更新,V’是V3邻接点),得到Table 5                                           Table 5  第3次迭代后的结果         
《Dijkstra 邻接矩阵 单源点最短路径》   
《Dijkstra 邻接矩阵 单源点最短路径》                        第3次迭代后,发现通过V3到达V5的距离更近,更新表格           1) 更新距离 d[i] = d[V3] + w[V3,Vi]           2) 更新被更新顶点的最短路径,先将V3的最短路径复制过来,再加上(V3,Vi)           V3最短路径是V0->V4->V3,加上(V3,V5)构成V5当前最短路径V0->V4->V3->V5

     i=4  第4次迭代          选出当前距离最小顶点V5,将V5加入S集合,对V5出发的边进行松弛(if d[V] > d[V5] + w[V5,V’],更新,V’是V5邻接点),得到Table 6                                             Table 6  第3次迭代后的结果          
《Dijkstra 邻接矩阵 单源点最短路径》     
《Dijkstra 邻接矩阵 单源点最短路径》                        第4次迭代后,发现没有可以被更新的顶点

     i=5  第5次迭代          只剩下一个顶点V1还没有求得最短路径,检查d[V1],发现V0不能到达V1,所以V0到V1没有最短路径            至此,我们就求得了V0到所有点的最短路径及距离。

     松弛过程中,对已经得到最短路径的顶点,不再更新距离和最短路径。

五、时间复杂度分析      使用邻接矩阵的Dijkstra算法,时间复杂度在于两点:      1) 每次迭代中,从未得到最短路径的顶点中选择一个距离值最小的顶点      2) 每次得到一个顶点Vi的最短路径后,对Vi出发的边进行松弛

     第1点,使用邻接矩阵表示图中,每次迭代都需要遍历一次d[i]来拿到最小值(就是从一个数组中选出一个最小值),这个操作时间复杂度是O(V),总共迭代V次,所以第1点时间复杂度是O(V*V)      第2点,每次迭代中,由于只松弛本次迭代选出的顶点Vi的边,把所有迭代选出的所有顶点松弛的边加起来,这个操作就是松弛所有的边一次,时间复杂度是O(E)

           总体计算起来,时间复杂度是O(V*V),V是图中顶点的个数

六、实现      
Dijkstra 实现分两种,邻接矩阵和邻接表,细分为:
     1) 邻接矩阵
     2) 邻接表+二叉堆
     3) 邻接表+斐波那契堆


     1是邻接矩阵实现,2,3是邻接表实现,这里只实现邻接矩阵的Dijkstra算法。           实现的代码中有很多地方没有验证,功能比较单一,效率有待优化。这里实现的只是有向图,边权值>=0,代码目的只是供初学者参考,有其他需要的读者自行实现。      
C/C++ 代码:

#include <iostream>
#include <string>
#include <vector>
#include <utility>
#include <cstdlib>

using namespace std;

const int INFINITE = 100000000;

class DG_AdjMatrix{
public:
	//初始化图,手动输入
	DG_AdjMatrix()
	{
		cout << "输入有图顶点个数:";
		cin >> m_vexNum;
		cout << "输入有向图边数:";
		cin >> m_arcNum;

		
		//先开辟空间
		m_nodeName = new string[m_vexNum];
		m_adjMatrix = new int*[m_vexNum];
		for (int i = 0; i < m_vexNum; ++i)
		{
			m_adjMatrix[i] = new int[m_vexNum];
		}
		for (int i = 0; i < m_vexNum; ++i)
		{
			for (int j = 0; j < m_vexNum; ++j)
			{
				m_adjMatrix[i][j] = INFINITE;
			}
		}

		//初始化顶点和边
		cout << endl;
		for (int i = 0; i < m_vexNum; ++i)
		{
			cout << "输入第[" << i+1 << "]个顶点名称: ";
			cin >> m_nodeName[i];
		}
		string x, y;
		int weight;
		int locateX, locateY;
		cout << endl;
		for (int i = 0; i < m_arcNum; ++i)
		{
			cout << "输入第[" << i+1 << "]条边依附的顶点和权值: ";
			cin >> x >> y >> weight;
			locateX = LocateNode(x);
			locateY = LocateNode(y);
			m_adjMatrix[locateX][locateY] = weight;
			//m_adjMatrix[locateX][locateY] = weight; //无向图
		}
	}

	int LocateNode(string nodeName)
	{
		for (int i = 0; i < m_vexNum; ++i)
		{
			if (m_nodeName[i] == nodeName)
			{
				return i;
			}
		}
		cout << "不存在顶点 " << nodeName << endl;
		return -1;
	}

	//输出邻接矩阵
	void PrintAdjMatrix()
	{
		cout << "vexnum: " << m_vexNum << endl;
		cout << "arcnum: " << m_arcNum << endl;

		//print adjMatrix
		cout << "      ";
		for (int i = 0; i < m_vexNum; ++i)
		{
			cout << m_nodeName[i] << "      ";
		}
		cout << endl;
		for (int i = 0; i < m_vexNum; ++i)
		{
			cout << m_nodeName[i];
			for (int j = 0; j < m_vexNum; ++j)
			{
				if (m_adjMatrix[i][j] != INFINITE)
				{
					cout << "      " << m_adjMatrix[i][j];
				}
				else
				{
					cout << "      "  << "-";
				}
			}
			cout << endl;
		}
		cout << endl;
	}

	int GetVexNum()
	{
		return this->m_vexNum;
	}

	int GetArcNum()
	{
		return this->m_arcNum;
	}

	string GetNodeName(int location)
	{
		if (location >= 0 && location < m_vexNum) //自己做实验可以优化
		{
			return this->m_nodeName[location];
		}
		else
		{
			cout << "不存在这样的顶点" << endl;
			return "";
		}
	}

	int** GetAdjMatrix()
	{
		return this->m_adjMatrix;
	}

	/************************************************************************/
	/*            dijkstra求单源点最短路径——邻接矩阵                                                     */
	/************************************************************************/
	void Dijkstra_AdjMatrix_ShortestPath(DG_AdjMatrix G)
	{
		string sourceVex;
		cout << "输入源点: ";
		cin >> sourceVex;
		while (LocateNode(sourceVex) == -1)
		{
			cout << endl;
			cout << "输入源点: ";
			cin >> sourceVex;
		}
		int src = LocateNode(sourceVex);
			
		int *d = new int[G.GetVexNum()];
		vector<pair<bool, vector<string>>> finished(G.GetVexNum());
		for (int i = 0; i < G.GetVexNum(); ++i)
		{
			d[i] = INFINITE;
			finished[i].first = false;
		}
		int presentNode;
		int minDistance;

		d[src] = 0;
		//剩下V-1次,每次选出一个顶点,确定一条最短路径
		for (int i = 0; i < G.GetVexNum(); ++i)
		{
			minDistance = INFINITE;
			for (int w = 0; w < G.GetVexNum(); ++w)
			{
				if (!finished[w].first && d[w] < minDistance)
				{
					minDistance = d[w];
					presentNode = w;
				}
			}
			finished[presentNode].first = true; 
			//最后一次如果某点不可达,presentNode仍是上一次循环值
			//一个优化:如果只剩下不可达的顶点,通过判断跳出循环

			//更新距离
			for (int w = 0; w < G.GetVexNum(); ++w)
			{
				if (!finished[w].first)
				{  
					if (minDistance+G.GetAdjMatrix()[presentNode][w] < d[w])//更新距离
					{
						d[w] = minDistance + G.GetAdjMatrix()[presentNode][w];

						finished[w].second.clear();
						finished[w].second.insert(
							finished[w].second.begin(),
							finished[presentNode].second.begin(),
							finished[presentNode].second.end());
						finished[w].second.push_back(G.GetNodeName(w));
					}
					else if(d[w] != INFINITE && finished[w].second.size()==0)//为了产生路径
					{
						finished[w].second.push_back(G.GetNodeName(w));
					}
				}
			}
		}


		//输出所有最短路径和距离
		for (int i = 0; i < G.GetVexNum(); ++i)
		{
			cout << GetNodeName(src) << " -> " << GetNodeName(i) << "  ";
			if (d[i] != INFINITE)
			{
				cout << " " << d[i] << "  ";
				cout << GetNodeName(src);
				for (vector<string>::iterator ix = finished[i].second.begin(); 
					ix != finished[i].second.end(); ++ix)
				{
					cout << "->" << *ix;
				}
				cout << endl;
			}
			else
			{
				cout << " -  ";
				cout << "unreachable";
				cout << endl;
			}
		}
	}

private:
	int    **m_adjMatrix; //邻接矩阵
	string *m_nodeName;   //结点名称
	int    m_vexNum;      //顶点数
	int    m_arcNum;      //边数
};

int main()
{
	DG_AdjMatrix g;
	g.PrintAdjMatrix();
	g.Dijkstra_AdjMatrix_ShortestPath(g);

	system("pause");
	return 0;
}

     运行截图:     
《Dijkstra 邻接矩阵 单源点最短路径》

    原文作者:Dijkstra算法
    原文地址: https://blog.csdn.net/sailensi/article/details/48975563
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞