Dijkstra 算法

最短路径算法的基础知识,参见 http://blog.csdn.net/pacosonswjtu/article/details/49894021

Dijkstra算法 涉及到的 优先队列的操作实现(该优先队列的数据类型不是 int , 而是 Distance),详情参见http://blog.csdn.net/pacosonswjtu/article/details/49923389


【1】Dijkstra 算法相关

1.1)贪婪算法一般分阶段去求解一个问题, 在每个阶段它都把当前出现的当做是最好的去处理:

    • 1.1.1)贪婪算法荔枝(使用最少数目的纸币找零钱):
      说找零钱, 大部分人首先数出面值1元的纸币,然后是面值5角的纸币、2角的纸币、1角的纸币等等;这种贪婪算法使用最少数目的纸币找零钱;
  • 1.1.2)贪婪算法的主要问题: 该算法不能总是成功,为了找还15角的零钱,如添加面值1元2角的纸币(这仅仅是举例说明)可破坏这种找零钱算法, 因为此时它给出的答案(一个面值1元2角的纸币+1个面值2角的纸币+一个面值1角的纸币==3个)不是最优的(1个面值1元的纸币+1个面值5角的纸币==2个);

1.2)Dijkstra 算法:解决单源最短路径问题的一般方法叫做 Dijkstra算法, 它的解法是贪婪算法最好的例子;

    • 1.2.1) Dijkstra 算法像无权最短路径算法一样, 按阶段进行;在每个阶段, 该算法选择一个顶点v, 它在所有未知顶点中具有最小的dv, 同时算法声明从s到v的最短路径是已知的。阶段的其余工作由dw值的更新工作组成;
  • 1.2.2)利用反证法证明得到, 只要没有边的值为负, 该算法总能够顺利完成,如果任何一边出现负值, 则算法可能得出错误的答案;
  • 1.2.3) Dijkstra算法描述(转自天勤计算机考研高分笔记——数据结构)
    设有两个顶点集合S 和 T, 集合S中存放图中已找到最短路径的顶点,集合T存放图中剩余顶点。初始状态时, 集合S 中只包含源点V0, 然后不断从集合T中选取到顶点V0 路径长度最短的顶点Vu 并将其并入到集合S中。集合S每并入一个新的顶点Vu, 都要修改顶点V0到 集合T中顶点的最短路径长度值。不断重复这个过程, 直到集合T的顶点全部并入到 S中为止;

Attention)在理解“集合S每并入一个新的顶点Vu,都要修改顶点V0到集合T中顶点的最短路径长度值”的时候需要注意:

  • A1)在Vu被选入S中后, Vu被确定为最短路径上的顶点, 此时Vu就像V0到达T中顶点的中转站 ,多了一个中转站, 就会多一些达到T中顶点的新路径,而这些新路径有可能比之前V0到T中顶点的路径还要短,因此需要修改原有V0到T中其他顶点的路径长度。此时对于T中的一个顶点Vk, 有两种情况:一种是V0不经过Vu 到达Vk的路径长度为a, 另一个是V0经过Vu到达Vk的长度为b。 如果a<=b, 则什么也不做;如果 a>b , 则用b来代替a。 用同样的方法处理T中其他顶点, 当T中所有顶点都被处理完后, 会出现一组新的 V0到T中各个顶点的路径,这些路径中有一条最短的, 对应了T中一个顶点, 就是新的 Vu, 将其并入S。重复上述过程, 最后T中所有的顶点都会被并入到S中, 此时就可以得到 V0到图中所有顶点的最短路径;

【2】Dijkstra算法实现

2.1)图是稠密的: 通过使用扫描表来找出最小值dv, 那么每一步将花费 O(|V|)时间找到最小值, 从而整个算法过程将花费 O(|V|^2)时间查找最小值;每次更新dw的时间是常数, 而每条边最多有一次更新,总计为 O(|E|),因此总的运行时间为
O(|E| + |V|)=O(|V|^2);
2.2)图是稀疏的:边数 |E|=Θ(|V|) , 那么扫描法就太慢了,不适用于稀疏图;

  • 2.2.1)一种处理方法是把更新处理成 DecreaseKey 操作: 此时, 查找最小值的时间为 O(log|V|), 即为执行那些更新的时间, 它相当于执行那些 DecreaseKey操作的时间。由此得出运行时间为 O(|E|log|V| + |V|log|V|)=O(|E|log|V|),它是对前面稀疏图的界的改进;由于优先队列不是有效地支持 Find操作, 由此 di 的每个值在优先队列的位置将需要保留并当 di 在优先队列中改变时更新。如果优先队列使用二叉堆实现的 话,那么将会很难办;如果使用配对堆(pairing heap, 见第12章),则程序不会太差;
  • 2.2.2)另一种方法是在每次执行第9行时把w和新值dw插入到优先队列中去。(这里仅仅提供了一个idea,可以不去细究,因为Solution多种多样)这样,在优先队列中的每个顶点就可能有多于一个的代表。当 DeleteMin操作吧最小的顶点从优先队列中删除时, 必须检查以肯定它不是已经知道的。这种方法虽然从软件观点来看是优越的,而且编程容易得多,但是,队列的大小可能达到 |E| 那么大。由于|E| <= |V|^2 意味着 log|E| <=2log|V| , 因此这并不影响渐进时间界。这样,我们仍然得到一个O(|E|log|V|)算法。不过,空间需求的确增加了, 在某些应用中这可能是严重的。不仅如此, 因为该方法需要 |E| 次而不仅仅是 |V| 次 DeleteMin, 所以在实践中运行很慢;
  • 2.2.3)图在大多数情况下都是非常稀疏的:注意,对于一些诸如计算机邮件和大型公交传输的典型问题, 它们的图都是非常稀疏的, 因为大多数顶点只有少数几条边。因此,在许多应用中 使用优先队列来解决这种问题 是很重要的;
  • 2.2.4)使用斐波那契堆实现 Dijkstra算法, 如果使用不同的数据结构,那么 Dijkstra算法可能会有更好的时间界。我们将看到另外的优先队列数据结构,叫做斐波那契堆(Fibonacci heap)。使用这种数据结构的运行时间为 O(|E| + |V|log|V|)。斐波那契堆具有良好的 理论时间界,不过,它需要相当数量的系统开销。因此,尚不清楚在实践中是否使用 斐波那契堆比使用具有二叉堆的Dijkstra 算法更好;

【3】看个荔枝:



【4】source code + printing results

Attention)

    • A1)代码的打印结果 和 手动模拟结果做个比较,以验证我的代码可行性: 注意将我的打印结果和章节【3】中的“有权最短路径Dijkstra算法步骤解析”中的各个步骤的binary heap 和 table内容(存储在进行Dijkstra算法过程中的节点相关数据)做个比较,很直观地演示了 Dijkstra算法的步骤;
  • A2)出现的问题: 本源代码用到了 优先队列(二叉堆)来选取最小的 distance所在的vertex编号,很方便,不过有个问题就是,当起始顶点(我们这里是v1)到后面的邻接顶点比之前的邻接顶点还要小(章节【3】中的v3被声明为已知后,v6的Distance更新为8,就是这种情况),那么就需要更新优先队列里面的v6的distance(由9更新为8),但是优先队列对于 find 操作不是很有效。
  • A3)如何解决优先队列对find操作不是很有效的情况: 这里, 我们引入了另一个int类型的数组indexOfVertexInHeap who stores index of vertexs in heap and let every element be -1 initially;比如,v6存放在 heap的第5个位置上,那么 indexOfVertexInHeap[6]=5,对的,就是这样sample, 后面,我们需要更新 heap里面的某个vertex的distance,直接用 indexOfVertexInHeap 导出该vertex在heap中的位置,然后直接更新就可以了,Bingo!

4.1)download source code:
Dijkstra算法源代码(优先队列实现):https://github.com/pacosonTang/dataStructure-algorithmAnalysis/tree/master/chapter9/p228_dijkstra
4.2)source code at a glance(for complete code, please click given link above):

#include "dijkstra.h"

//allocate the memory for initializing unweighted table
WeightedTable *initWeightedTable(int size)
{
	WeightedTable* table;
	int i;

	table = (WeightedTable*)malloc(sizeof(WeightedTable) * size);
	if(!table)
	{
		Error("out of space ,from func initWeightedTable");
		return NULL;
	}

	for(i = 0; i < size; i++)
	{
		table[i] = makeEmptyWeightedTable();
		if(!table[i])
			return NULL;
	}

	return table;
} 

// allocate the memory for every element in unweighted table
WeightedTable makeEmptyWeightedTable()
{
	WeightedTable element;

	element = (WeightedTable)malloc(sizeof(struct WeightedTable));
	if(!element)
	{
		Error("out of space ,from func makeEmptyWeightedTable");
		return NULL;
	}
	element->known = 0; // 1 refers to accessed , also 0 refers to not accessed
	element->distance = MaxInt;
	element->path = -1; // index starts from 0 and -1 means the startup vertex unreaches other vertexs

	return element;
}

// allocate the memory for storing index of  vertex in heap and let every element -1
int *makeEmptyArray(int size)
{
	int *array;
	int i;

	array = (int*)malloc(size * sizeof(int));
	if(!array)
	{
		Error("out of space ,from func makeEmptyArray");
		return NULL;
	}
	for(i=0; i<size; i++)
		array[i] = -1;

	return array;
}

//computing the unweighted shortest path between the vertex under initIndex and other vertexs
void dijkstra(AdjTable* adj, int size, int startVertex, BinaryHeap bh)
{
	int adjVertex;
	int tempDistance;
	WeightedTable* table;
	int vertex;
	AdjTable temp;
	Distance tempDisStruct;
	int *indexOfVertexInHeap;
	int indexOfHeap;

	table = initWeightedTable(size);
	tempDisStruct = makeEmptyDistance();
	indexOfVertexInHeap = makeEmptyArray(size);

	tempDisStruct->distance = table[startVertex-1]->distance;
    tempDisStruct->vertexIndex = startVertex-1;
	insert(tempDisStruct, bh, indexOfVertexInHeap); // insert the (startVertex-1) into the binary heap	

	table[startVertex-1]->distance = 0;// update the distance
	table[startVertex-1]->path = 0;// update the path of starting vertex

	while(!isEmpty(bh))
	{
		//vertex = deQueue(queue); // if the queue is not empty, conducting departing queue
		vertex = deleteMin(bh)->vertexIndex; // return the minimal element in binary heap

		table[vertex]->known = 1; // update the vertex as accessed, also responding known 1
		temp = adj[vertex]->next;
		while(temp)
		{
			adjVertex = temp->index; // let each adjVertex adjacent to vertex enter the queue

			//enQueue(queue, adjVertex);
			tempDistance = table[vertex]->distance + temp->weight; // update the distance
			if(tempDistance < table[adjVertex]->distance)
			{
				table[adjVertex]->distance = tempDistance;
				table[adjVertex]->path = vertex; //update the path of adjVertex, also responding path evaluated as vertex							

				// key, we should judge whether adjVertex was added into the binary heap
				//if true , obviously the element has been added into the binary heap(so we can‘t add the element into heap once again)
				if(indexOfVertexInHeap[adjVertex] != -1)
				{
					indexOfHeap = indexOfVertexInHeap[adjVertex];
					bh->elements[indexOfHeap]->distance = tempDistance; // update the distance of corresponding vertex in binary heap
				}
				else
				{
					tempDisStruct->distance = table[adjVertex]->distance;
					tempDisStruct->vertexIndex = adjVertex;
					insert(tempDisStruct, bh, indexOfVertexInHeap); // insert the adjVertex into the binary heap
				}
			}
			temp = temp->next;
		}
		printDijkstra(table, size, startVertex);
		printBinaryHeap(bh);
		printf("\n");
	}		

	printf("\n");
} 

//print unweighted table
void printDijkstra(WeightedTable* table, int size, int startVertex)
{
	int i;
	char *str[4] =
	{
		"vertex",
		"known",
		"distance",
		"path"
	};

	printf("\n\t === storage table related to Dijkstra alg as follows: === ");
	printf("\n\t %6s%6s%9s%5s", str[0], str[1], str[2], str[3]);
	for(i=0; i<size; i++)
	{
		if(i != startVertex-1 && table[i]->path!=-1)
			printf("\n\t %-3d   %3d   %5d      v%-3d  ", i+1, table[i]->known, table[i]->distance, table[i]->path+1);
		else if(table[i]->path == -1)
			printf("\n\t %-3d   %3d   %5d      %-3d  ", i+1, table[i]->known, table[i]->distance, table[i]->path);
		else
			printf("\n\t *%-3d  %3d   %5d      %-3d  ", i+1, table[i]->known, table[i]->distance, 0);
	}
}

int main()
{
	AdjTable* adj;
	BinaryHeap bh;
	int size = 7;
	int capacity;
	int i;
	int j;
	int column = 4;
	int startVertex;

	int adjTable[7][4] =
	{
		{2, 4, 0, 0},
		{4, 5, 0, 0},
		{1, 6, 0, 0},
		{3, 5, 6, 7},
		{7, 0, 0, 0},
		{0, 0, 0, 0},
		{6, 0, 0, 0}
	};

	int weight[7][7] =
	{
		{2, 1, 0, 0},
		{3, 10, 0, 0},
		{4, 5, 0, 0},
		{2, 2, 8, 4},
		{6, 0, 0, 0},
		{0, 0, 0, 0},
		{1, 0, 0, 0}
	};

	printf("\n\n\t ====== test for dijkstra alg finding weighted shortest path from adjoining table ======\n");
	adj = initAdjTable(size);		

	printf("\n\n\t ====== the initial weighted adjoining table is as follows:======\n");
	for(i = 0; i < size; i++)
	 	for(j = 0; j < column; j++)
			if(adjTable[i][j])
				insertAdj(adj, adjTable[i][j]-1, i, weight[i][j]); // insertAdj the adjoining table over

	printAdjTable(adj, size);

	capacity = 7;
	bh = initBinaryHeap(capacity+1);
	//conducting dijkstra alg to find the unweighted shortest path starts
	startVertex = 1; // you should know our index for storing vertex starts from 0
	dijkstra(adj, size, startVertex, bh);

	return 0;
}

4.3)printing results:


转自http://www.cnblogs.com/pacoson/p/4977652.html

时间: 2024-10-09 11:04:18

Dijkstra 算法的相关文章

畅通project续HDU杭电1874【dijkstra算法 || SPFA】

http://acm.hdu.edu.cn/showproblem.php?pid=1874 Problem Description 某省自从实行了非常多年的畅通project计划后.最终修建了非常多路.只是路多了也不好,每次要从一个城镇到还有一个城镇时,都有很多种道路方案能够选择,而某些方案要比还有一些方案行走的距离要短非常多.这让行人非常困扰. 如今,已知起点和终点,请你计算出要从起点到终点.最短须要行走多少距离. Input 本题目包括多组数据.请处理到文件结束. 每组数据第一行包括两个正

ACM: HDU 2544 最短路-Dijkstra算法

HDU 2544最短路 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Description 在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt.但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗? Input 输入包括多组数据.每组数据第一行是两个整数N.M(N<=100,M<

ACM: HDU 3790 最短路径问题-Dijkstra算法

HDU 3790 最短路径问题 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Description 给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的. Input 输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p.最后一行是

邻接表实现Dijkstra算法以及DFS与BFS算法

//============================================================================ // Name : ListDijkstra.cpp // Author : fffff // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //==========================

Dijkstra算法(求解单源最短路)详解 + 变形 之 poj 1860 Currency Exchange

/* 求解单源最短路问题:Dijkstra算法(该图所有边的权值非负) 关键(贪心): (1)找到最短距离已经确定的节点,从它出发更新与其相邻节点的最短距离: (2)此后不再关心(1)中“最短距离已经确定的节点”. 时间复杂度(大概的分析,不准确): “找到最短距离已经确定的节点” => O(|V|) "从它出发更新与其相邻节点的最短距离" => 邻接矩阵:O(|V|),邻接表:O(|E|) 需要循环以上两个步骤V次,所以时间复杂度:O(V^2) 即:在|E|较小的情况下,

51nod-迷宫问题(Dijkstra算法)

Dijkstra算法 你来到一个迷宫前.该迷宫由若干个房间组成,每个房间都有一个得分,第一次进入这个房间,你就可以得到这个分数.还有若干双向道路连结这些房间,你沿着这些道路从一个房间走到另外一个房间需要一些时间.游戏规定了你的起点和终点房间,你首要目标是从起点尽快到达终点,在满足首要目标的前提下,使得你的得分总和尽可能大.现在问题来了,给定房间.道路.分数.起点和终点等全部信息,你能计算在尽快离开迷宫的前提下,你的最大得分是多少么? Dijkstra算法是一个经典的算法--他是荷兰计算机科学家D

Dijkstra算法

Dijkstra算法是一个经典的算法--他是荷兰计算机科学家Dijkstra于1959年提出的单源图最短路径算法.也是一个经典的贪心算法.所谓单源图 是规定一个起点的图,我们的最短路径都是从这个起点出发计算的.算法的适用范围是一个无向(或者有向图),全部边权都是非负数. 算法描写叙述: 节点集合V = {}空集合,距离初始化. 节点编号0..n – 1, 起点编号0≤ s < n. 距离数组 起点 d[s] = 0 其它 d[i] = ∞, 0 ≤ i < n,  i ≠ s. 循环n次 找到

算法描述》关于SPFA和Dijkstra算法的两三事

本来我是想把这两个算法分开写描述的,但是SPFA其实就是Dijkstra的稀疏图优化,所以其实代码差不多,所以就放在一起写了. 因为SPFA是Dijkstra的优化,所以我想来讲讲Dijkstra. 什么是Dijkstra Dijkstra是一种求单源最短路的基础算法,时间复杂度在不加堆优化的情况下是o(n^2)的,加了堆优化就能简化到o(nlogn),而且算法稳定性很强(从这点上来说比SPFA好多了,具体怎么好下面再讲),基础思路如下: 首先,把所有点到源的距离设为最大,然后把源加入队列,接着

Dijkstra算法求单源最短路径

1.最短路径 在一个连通图中,从一个顶点到另一个顶点间可能存在多条路径,而每条路径的边数并不一定相同.如果是一个带权图,那么路径长度为路径上各边的权值的总和.两个顶点间路径长度最短的那条路径称为两个顶点间的最短路径,其路径长度称为最短路径长度. 最短路径在实际中有重要的应用价值.如用顶点表示城市,边表示两城市之间的道路,边上的权值表示两城市之间的距离.那么城市A到城市B连通的情况下,哪条路径距离最短呢,这样的问题可以归结为最短路径问题. 求最短路径常见的算法有Dijkstra算法和Floyd算法