数据结构——单源最短路径Dijkstra算法

                          单源最短路径Dijkstra算法

       在现实世界中,经常有从一个城市到另一个城市选择最短路径的场景,这种场景也可以是网络数据传输过程中从一个网络节点到另一个网络节点进行路由。可以把这种场景建模成一个有向网,而耗费就是边上的权值,那么这类场景的问题就是在网中一点到另一点的最短路径问题了。
       如果已经知道从节点s到节点e的最短路径是(vs,...,vk,ve)那么vs到vk的最短路径一定就是(vs...vk)。用反证法,如果vs到vk的最短路径不是(vs...vk),设(vs...vk)为path1,而是另一条path2,很明显len(path1)>len(path2),那么从s到e的最短路径就是path2+len(k,e)了,因为path2+len(k,e)
      这个结论表示最短路径具有最优子结构的性质,设dist(i)表示起始节点到节点i的最短距离,则可以得到dist(e) = min{dist(k)+weight(k,e)},这里的k是图中的某个节点,并且这个节点以e为邻接节点。根据这个式子,可以得到寻找最短路径的策略如下:
      初始如果i!=s,则设置dist(i) 等于远大于weight的数
      把s的邻接节点i的dist(i)设置成weight(s,i)
      设置一个path的数组,path[i]表示最短路径中节点i的前一个节点
      找出除s外所有节点中dist(v)最小的邻接节点设为v,并将v标记为已访问,再找v的邻接节点vk,如果dist(vk)>dist(v)+weight(v,vk),那么设置dist(vk) = dist(v)+weight(v,vk),并且设置path[vk] = v。
      然后再从除s外的所有节点找一个未被访问且dist(v)最小的节点v,再寻找v的邻接节点vk,如果dist(vk)>dist(v)+weight(v,vk),那么设置dist(vk) = dist(v)+weight(v,vk),并且设置path[vk] = v。
     这样每次都会求得一层dist,从s向e的方向推动,可知在最坏的情况下,也就是网退化成了一条链,那么需要这样循环n-1次最终能求出dist(e)

     下面是用C++实现的Dijkstra算法
#include "stdafx.h"
#include 
using namespace std;

const int MAX = 1e5;

/**
* 求当前节点的第一个邻接节点
*/
int FirstAdjVex(int **weights,int size,int cv)
{
	if(cv < 0 || cv >= size)
	{
		return -1;
	}
	for (int i = 0;i < size;i++)
	{
		if (weights[cv][i])
		{
			return i;
		}
	}
	return -1;
}

/**
* 计算当前节点cv基于av的下一个邻接节点
*/
int NextAdjVex(int **weights,int size,int cv,int av)
{
	if (cv < 0 || cv >= size || av < 0 || av >= size - 1)
	{
		return -1;
	}

	for (int i = av + 1;i < size;i++)
	{
		if (weights[cv][i])
		{
			return i;
		}
	}
	return -1;
}


void Dijkstra(int **weights,int size,int *paths,int *dist,int v0)
{
	bool *tags = new bool[size];//访问标志
	for (int i = 0;i < size;i++)
	{
		tags[i] = false;
	}
	tags[v0] = true;
	//初始化path
	for (int i = 0;i < size;i++)
	{
		if (i != v0&&weights[v0][i] == 0) {//不是v0且不是v0的邻接节点
			dist[i] = MAX;
		}
		else 
		{
			dist[i] = weights[v0][i];
		}
		if (i != v0 && dist[i] < MAX)//把v0的邻接节点的path设置成v0
		{
			paths[i] = v0;
		}
		else 
		{
			paths[i] = -1;
		}
	}
	for (int i = 1;i < size;i++)
	{
		int v = v0;
		int minDist = MAX;
		for (int j = 0;j < size;j++)//从节点集中找出一个节点v,使dist[v]最小且v未被访问过
		{
			if(!tags[j] && dist[j] < minDist)
			{
				v = j;
				minDist = dist[j];
			}
		}
		tags[v] = true;
		for (int j = FirstAdjVex(weights, size, v);j != -1;j = NextAdjVex(weights,size,v,j))//遍历v的邻接节点
		{
			if (!tags[j] && minDist + weights[v][j] < dist[j])
			{
				dist[j] = minDist + weights[v][j];
				paths[j] = v;
			}
		}
	}
}
int main()
{
	//构造有向图
	int size = 5;
	int **weights = new int*[size];
	for (int i = 0;i < size;i++)
	{
		weights[i] = new int[size];
		for (int j = 0;j < size;j++)
		{
			weights[i][j] = 0;
		}
	}
	weights[0][1] = 100;
	weights[0][2] = 30;
	weights[0][4] = 10;
	weights[2][1] = 60;
	weights[2][3] = 60;
	weights[3][1] = 10;
	weights[4][3] = 50;
	int v0 = 0;
	int *paths = new int[size];//存放路径
	int *dist = new int[size];//存入v0到每个节点的最短路径的长度
	Dijkstra(weights, size, paths, dist,v0);
	for (int i = 0;i < size;i++)
	{
		if (i != v0)
		{
			cout << v0 << " ---> " << i << " dist: " << dist[i] << endl;
			for (int j = i;j != v0;j = paths[j]) 
			{
				cout << j << " <-- ";
			}
			cout << v0 << endl;
		}
	}
	system("pause");
	return 0;
}
执行结果如下:
数据结构——单源最短路径Dijkstra算法_第1张图片
测试代码中一共4组数据,共8行,每一组的第一行表示起始节点0到这个节点的距离,每二行是节点0到这个节点的最短路径

你可能感兴趣的:(学习,数据结构)