头文件"AdjGraph.h"
#include<iostream>
#define INFINITY 0x3f3f3f3f
#define VISITED 1
#define UNVISITED 0
using namespace std;
class AdjGraph{
public:
int **edge;//邻接矩阵,值为每条边的权值
int *mark;//记录顶点是否加入V-U
int *D;//该点到源点的最短路径长度
int vertexNum,edgeNum;//顶点数目,边数目
int s;//源点
AdjGraph(int v){
vertexNum=v;
edgeNum=0;
edge=new int* [vertexNum];
for(int i=0;i<vertexNum;i++)
edge[i]=new int [vertexNum];
mark=new int [vertexNum];
D=new int [vertexNum];
for(int i=0;i<vertexNum;i++)
mark[i]=UNVISITED;
for(int i=0;i<vertexNum;i++)
for(int j=0;j<vertexNum;j++)
edge[i][j]=INFINITY;
}
~AdjGraph(){
for(int i=0;i<vertexNum;i++)
delete [] edge[i];
delete edge;
delete mark;
delete D;
}
//设置源点s
void setsource(int s){
this->s=s;
}
//设置边信息
void setedge(int v,int u,int w){
edge[v][u]=w;
edgeNum++;
}
void Dijkstra(){
int pos=s;
int min;
//从源点s开始遍历
mark[s]=VISITED;
for(int i=0;i<vertexNum;i++)
D[i]=edge[s][i];
//遍历除源点外的所有顶点,循环vertexNum-1次
for(int i=1;i<vertexNum;i++){
min=INFINITY;
for(int j=0;j<vertexNum;j++){
if(mark[j]==UNVISITED&&min>D[j]){
min=D[j];
pos=j;
}
}
mark[pos]=VISITED;
//更新权值,与Prim不同,D[k]保存k到源点的权值,并且为之前路径的累加
for(int k=0;k<vertexNum;k++)
if(mark[k]==UNVISITED&&D[k]>D[pos]+edge[pos][k])
D[k]=D[pos]+edge[pos][k];
}
}
//输出pos到源点s的权
int output(int pos){
return D[pos];
}
};
源文件"main.cpp"
#include<iostream>
#include"AdjGraph.h"
#define INFINITY 0x3f3f3f3f
using namespace std;
int main(){
AdjGraph AG(6);
AG.setsource(0);
AG.setedge(0,1,12);
AG.setedge(0,2,10);
AG.setedge(0,4,30);
AG.setedge(0,5,100);
AG.setedge(1,2,5);
AG.setedge(2,3,50);
AG.setedge(3,5,10);
AG.setedge(4,3,20);
AG.setedge(4,5,60);
AG.Dijkstra();
for(int i=(AG.s+1)%AG.vertexNum;i!=AG.s;i=(i+1)%AG.vertexNum){
if(AG.output(i)==INFINITY){
cout<<AG.s<<"->"<<i<<":"<<"no path"<<endl;
continue;
}
cout<<AG.s<<"->"<<i<<":"<<AG.output(i)<<endl;
}
return 0;
}
关于对"#define INFINITY 0x3f3f3f3f",很多时候我们需要设置一个无穷大的常量,如果问题中各数据的范围明确,那么无穷大的设定不是问题,在不明确的情况下,很多程序员都取0x7fffffff作为无穷大,因为这是32-bit int的最大值。如果这个无穷大只用于一般的比较(比如求最小值时min变量的初值),那么0x7fffffff确实是一个完美的选择,但是在更多的情况下,0x7fffffff并不是一个好的选择。
很多时候我们并不只是单纯拿无穷大来作比较,而是会运算后再做比较,例如在大部分最短路径算法中都会使用的松弛操作: if (d[u]+w[u][v]<d[v]) d[v]=d[u]+w[u][v];
我们知道如果u,v之间没有边,那么w[u][v]=INF,如果我们的INF取0x7fffffff,那么d[u]+w[u][v]会溢出而变成负数,我们的松弛操作便出错了,更一般的说,0x7fffffff不能满足“无穷大加一个有穷的数依然是无穷大”,它变成了一个很小的负数。
除了要满足加上一个常数依然是无穷大之外,我们的常量还应该满足“无穷大加无穷大依然是无穷大”,至少两个无穷大相加不应该出现灾难性的错误,这一点上0x7fffffff依然不能满足我们。
所以我们需要一个更好的家伙来顶替0x7fffffff,最严谨的办法当然是对无穷大进行特别处理而不是找一个很大很大的常量来代替它(或者说模拟它),但是这样会让我们的编程过程变得很麻烦。在我读过的代码中,最精巧的无穷大常量取值是0x3f3f3f3f,我不知道是谁最先开始使用这个精妙的常量来做无穷大,于是我自己也尝试了一下,发现非常好用,而当我对这个常量做更深入的分析时,就发现它真的是非常精巧了。
0x3f3f3f3f的十进制是1061109567,也就是10^9级别的(和0x7fffffff一个数量级),而一般场合下的数据都是小于10^9的,所以它可以作为无穷大使用而不致出现数据大于无穷大的情形。
另一方面,由于一般的数据都不会大于10^9,所以当我们把无穷大加上一个数据时,它并不会溢出(这就满足了“无穷大加一个有穷的数依然是无穷大”),事实上0x3f3f3f3f+0x3f3f3f3f=2122219134,这非常大但却没有超过32-bit int的表示范围,所以0x3f3f3f3f还满足了我们“无穷大加无穷大还是无穷大“。