前言
计算图的最短路径,比较常见的算法有两种,一种是计算单源最短路径的Dijkstra算法,一种是计算任意两个点对最短路径的Floyd算法。这篇文章中主要的内容是Dijkstra算法的原理和实现。
一、原理
1.算法特点:
个人认为,Dijkstra算法的原理其实就是广度优先搜索,即逐步查找整张图,不管输入节点的位置情况,最后得到的也是所有点到源点的最小路径情况。
2.算法描述:
Dijkstra算法从用户输入的某个节点编号(a)开始,然后查找出与a相连通且权值最小的点,将其加入已确定路径点的集合 (T)中。之后每次循环,查找出从源点出发,经过T中新加入的点,再到当前点,路径权值是否有变化。若有变化,则更新路径信息。之后再重复循环,找出未确定路径点集合中(没有加入集合T的点即为未确定路径点),权值最小的那个,重复上述循环直至所有点都加入集合 T 中。
3.与Prim算法比较:
Dijkstra算法 和 Prim算法的原理都是一样是 广度优先搜索,只不过前者每次循环更新的是其余点到源点的路径信息(路径权值总和最小),后者更新的是总体权值总和最小。它们都是每次加入一个点,从某个确定点开始,逐步展开至整张图。
二、代码
#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
#define MaxDis 65536
struct Dis {
string path; //源点到此点的目前最短路径(循环更新直至最后不变)
int value; //最短路径权值(最小权值)
bool visit; //是否已经确定了最短路径
Dis() {
visit = false;
value = 0;
path = "";
}
};
struct Matrix
{
int iVexNum;
int iEdgeNum;
int** iArcs;
};
void Dijkstra(Matrix g, int begin)
{
Dis* dis = new Dis[g.iVexNum]; //保存路径信息和权值信息
int i;
int count=0; //路径已确定的点的数量
for(i=1;i<g.iVexNum;i++) //初始化,所有点到begin点的距离信息
{
dis[i].value = g.iArcs[begin][i];
dis[i].path = std::to_string(begin) +","+ std::to_string(i);
}
dis[begin].value = 0; //将begin点加入已确定路径的点的集合
dis[begin].visit = true;
while (count != g.iVexNum) //循环直至所有点都加入已确定集合
{
int iTemp; //临时变量,保存当前value值最小的点的编号
int min = MaxDis; //开始时Min值设为最大
for (i = 0; i < g.iVexNum; i++) //循环找到当前最小的权值点
{
if (!dis[i].visit&&dis[i].value < min)
{
min = dis[i].value;
iTemp = i;
}
}
dis[iTemp].visit = true; //每轮循环结束后的最小权值路径对应的点,就可以加入已确定路径集合中
count++;
for (i = 0; i < g.iVexNum; i++)
{
if (!dis[i].visit && g.iArcs[iTemp][i] != MaxDis
&& (dis[iTemp].value + g.iArcs[iTemp][i] < dis[i].value))
{
//如果加入新的点后,有其他的点变成可达或者从begin前往的路径权值变小,那么就更新
dis[i].path = dis[iTemp].path + "," + std::to_string(i);
dis[i].value = dis[iTemp].value + g.iArcs[iTemp][i];
}
}
}
}
总结
代码好像也没有什么要总结的地方,可能,查找一组数据中最小值时,先将临时保存最小值的变量 min 设为最大值 65536 之类的最大值算是一个小技巧….吧?我这也是强行总结了。。