Dijkstra 算法

Dijkstra算法是从求从某个源点V0到其余各顶点的最短路径问题。,其算法的具体思想是,有两个集合,一个是最短路径集合S,还有一个是未加入S的集合V-S,这些点待加入S中,只要加入了S中,那么这个点就是源点V0到这个点的最短路径了。其实这个算法的思想还是挺简单的,也是运用了贪心算法,严蔚敏书中和维基百科等,把这个问题讲的有点复杂了。我想结合严蔚敏的书中的表述和图例,尽量把这个问题讲的容易些。

1.     首先引入一个辅助向量D,它的每个分量D[i]表示当前所找到的从始点v到每个终点vi的最短路径的长度。它的初态为:若从vvi有弧,则D[i]为弧上的权值;否则就为∞。结合上图,例如D[4]代表v0v4的最短路径的长度,初始状态为∞,随着算法的运行,其不断更改值,最后的结果是30.

2.     假如现在求的路径的终点是vk,现在有个新加入的顶点vj,则此时最短路径只有两种情况,一种还是原来的v0vk,还有一种情况是v0vj再到vk。那么第二种情况的最短路径可以这样求:v0vj的路径长度D[j]+vjvk的弧长。这样求为了程序设计的简单。

 

3.     严的书中给了一个简单的证明,好好看还是有用的。假设S为最短路径的终点的集合,则可证明:下一条最短路径(设其终点为x)或者是弧(vx),或者是中间只经过S中的顶点而最后到达顶点x的路径。

  下面是书中给的算法步骤:(只是比较严谨而已,看不懂没关系,后面结合图示)

(1)   假设用带权的邻接矩阵arcs来表示带权有向图,arcs[i][j]表示弧<vi,vj>上的权值。若<vi,vj>不存在,则置arcs[i][j]为∞(在计算机上可用允许的最大值代替)。S为已找到从v出发的最短路径的终点的集合,它的初始状态为空集。那么,从v出发到图上其余各顶点vi可能到达的最短路径长度的初值为:

D[i] = arcs[Locate Vex(G,v)[i]    viV

(2)   选择vj,使得

D[j] = Min{D[i] | viV-S}

Vj就是当前求得的一条从v出发的最短路径的终点。令

S = S ∪{j

(3)   修改从v出发到集合V-S上任一顶点vk可大的最短路径长度。如果

D[j] + arcs[j][k] <D[k]

则修改D[k]

       D[k] = D[j] + arcs[j][k]

(4)   重复操作(2)、(3)共n-1次,即可得到结果。

下面结合书中的图讲解一下。

1.上图中第一列中,v0分别能到达的路径进行初始化,黄色标注的为最小的路径值。此时10是最小的,把v2加入最短路径集合S中。(v0默认已加入)。

2.此时的最短路径是v0,v2。这时看第二列。以v0,v2为路径,分别加入v3,v4,v5(这些点还没有进入S集合中),要是新的路径值比原来小,则更新路径长度和路径。

3.第二列的最小路径长度是30,则以v0,v4作为新的路径,把v4加入到S中去。再分别将未在S集合中的点进行测试,更新路径和路径长度。

     就这样循环就可以找到最短路径了,还是挺简单的吧,嘿嘿~

下面是书中的伪代码稍加改动,使之可以运行:

 

#include "stdafx.h"
#include <iostream>
using namespace std;

// 假设能表示的最大值,设为无穷大的边长,不能真的设为
//最大值,要不然后面在运算的时候容易溢出
const int MAX_VALUE=100000;   

const int VERTEX_NUM=6;			//邻接矩阵最大的维数

//图采用邻接矩阵存储的定义
typedef struct MGraph{
	int AdjMatrix[VERTEX_NUM][VERTEX_NUM];    //邻接矩阵
	int vexnum;            //图的顶点数量
	int arcnum;		      //图的边数,这个程序里没有用到
};

/*****************************************************************
Name:			Dijkstra

Inputs:
MGraph G - 邻接矩阵表示的图
int v0  -  表示图的起点
int P[][VERTEX_NUM] -二维数组。例如P[4][]代表v4的最短路径,这一行中,
                                          值为1代表经过这个点,值为0代表不经过这一点,见main的输出
int D[]  -  最短路径的权值

Return Value:
void

Description:		
*****************************************************************/
void Dijkstra(MGraph G,int v0,int P[][VERTEX_NUM], int D[]){

   //final为TRUE表示这个点已经在S中了,即已经是最短路径的终点了
	int final[VERTEX_NUM]={0};  

	//**********************初始化v0************************
	int v = 0;
	for ( ; v <G.vexnum; ++v)
	{
		D[v] = G.AdjMatrix[v0][v];
		for ( int w=0; w <G.vexnum; ++w)   P[v][w]=0;
		if (D[v] < MAX_VALUE) 
		{
			P[v][v0] = true;
			P[v][v] = true;   //v点自身必须要在路径里面
		}
	}//for

	D[v0] = 0; final[v0] = true;    //初始化,v0属于S集合
	//*******************初始化v0结束********************************


	for ( int i=1; i<G.vexnum;++i)          //其余的G.vexnum-1个顶点
	{
		int min=MAX_VALUE;                    //当前所知离v0顶点最近的距离
		for (int w = 0; w<G.vexnum; ++w)
			if (!final[w])                                           //w顶点在V-S中
				if (D[w]<min)  {v =w; min = D[w];}          //w顶点离v0顶点的最近距离      

				final[v] = true;               //离V0顶点最近的v加入S集

				for (int w=0; w<G.vexnum;++w)    //更新当前最短路径及距离
					if(!final[w] && (min +G.AdjMatrix[v][w]<D[w]))      //修改D[w]和P[w],w∈V-S
					{
						D[w] = min + G.AdjMatrix[v][w];
						for (int j=0; j < G.vexnum;++j)
							P[w][j] = P[v][j];
						P[w][w] = true;
					}//if
	}//for
}//





int _tmain(int argc, _TCHAR* argv[])
{
	//定义二维数组,一会初始化邻接矩阵
	//
	int arr[6][6]={100000,100000,10,100000,30,100,
		                    100000,100000,5,100000,100000,100000,
							100000,100000,100000,50,100000,100000,
							100000,100000,100000,100000,100000,10,
							100000,100000,100000,20,100000,60,
							100000,100000,100000,100000,100000,100000};
	MGraph mg;

	for( int i=0; i<6; ++i )
		for( int j=0; j<6; ++j )
			mg.AdjMatrix[i][j]=arr[i][j];    //初始化邻接矩阵

    mg.vexnum=6;   //顶点的个数是6个,这里非常无耻的赋值为6
	int P[VERTEX_NUM][VERTEX_NUM];
	int D[VERTEX_NUM];


	Dijkstra(mg,0,P,D);

	for(int i=0; i<VERTEX_NUM; i++)
		cout<<P[4][i]<<" ";

	return 0;
}


 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值