Dijkstra 最短路径:针对有向图,不支持负权值
// 图的相邻矩阵表示方法,还要用到最小值堆
#include <iostream>
#include <queue>
#define UNVISITED 0
#define VISITED 1
#define INFINITE 9999 //设置最大值
#define N 5 // 定义图的顶点数
using namespace std;
//GraphMatrix.h
//Edge
class Edge
{
public:
int weight; // Edge weight
int from;
int to; // form To to
Edge()
{
from = -1;
to = -1;
weight = 0;
}; // 构造函数
Edge(int f,int t,int w) // 构造函数
{
from=f;
to=t;
weight=w;
}
bool operator < (const Edge &arg)
{
return (this->weight < arg.weight);
};
bool operator == (const Edge &arg)
{
return (this->weight == arg.weight);
};
bool operator > (const Edge &arg)
{
return (this->weight > arg.weight);
};
bool operator <= (const Edge &arg)
{
return (this->weight <= arg.weight);
};
bool operator >= (const Edge &arg)
{
return (this->weight >= arg.weight);
};
};
// 图基类
class Graph
{
public:
int numVertex; //图的顶点的个数
int numEdge; //图的边的数目
int *Mark; /*Mark指针指向保存有图的顶点的标志位的数组,标志位用来标记某顶点是否被访问过*/
int *Indegree; //Indegree指针指向保存有图的顶点的入度的数组
Graph(int numVert) //构造函数
{
numVertex = numVert; //确定图的顶点的个数
numEdge = 0; //确定图的边的数目
Indegree = new int[numVertex]; /*为保存图的顶点的入度申请数组,Indegree为数组指针*/
Mark = new int[numVertex]; /*为图的顶点的标志位申请数组,Mark为数组指针*/
for (int i = 0; i < numVertex; i ++) /*确定图的顶点的标志位和入度,即所有顶点的标志位初始化为未被访问过,入度初始化为0*/
{
Mark[i] = UNVISITED;
Indegree[i] = 0;
}
};
~Graph() //析构函数
{
delete [] Mark;
delete [] Indegree;
};
virtual Edge FirstEdge(int oneVertex) // 返回与顶点oneVertex相关联的第一条边
{
Edge myEdge;
myEdge.from = oneVertex;
myEdge.to = -1;
return myEdge;
};
virtual Edge NextEdge(Edge preEdge) // 返回与边PreEdge有相同关联顶点的下一条边
{
return preEdge;
};
int VerticesNum() //返回图的顶点个数
{
return numVertex;
};
int EdgesNum() //返回图的边数
{
return numEdge;
};
int FromVertex(Edge oneEdge) // 返回oneEdge的始点
{
return oneEdge.from;
};
int ToVertex(Edge oneEdge) // 返回oneEdge的终点
{
return oneEdge.to;
};
int Weight(Edge oneEdge) // 返回oneEdge的权值
{
return oneEdge.weight;
};
bool IsEdge(Edge oneEdge) //如果oneEdge是边则返回TRUE,否则返回FALSE
{
if (oneEdge.weight > 0 && oneEdge.weight < INFINITE && oneEdge.to >= 0)
return true;
else
return false;
};
virtual void setEdge(int from, int to, int weight) = 0;
virtual void delEdge(int from, int to) = 0;
};
// 图的相邻矩阵表示法
class Graphm:public Graph
{
private:
int **matrix; //指向相邻矩阵的指针
public:
void IniGraphm(Graphm *Graphm, int A[N][N]); // 初始化
void DFS(Graph &G, int V); // 深度优先搜索
void BFS(Graph &G, int V); // 广度优先搜索
void Visit(Graph &G, int V); // 访问
public:
Graphm(int numVert):Graph(numVert) //构造函数
{
int i, j; //i, j作为for循环中的计数器
matrix = (int **)new int*[numVertex]; /*申请matrix数组,该数组有numVertex个元素,每个元素是整型指针类型*/
for (i = 0; i < numVertex; i ++) /*matrix数组的每个元素,都指向一个具有numVertex个元素的数组*/
matrix[i] = new int[numVertex];
for (i = 0; i < numVertex; i++) /*相邻矩阵的所有元素都初始化为0,如果矩阵元素matrix[i][j]不为0,则表明顶点i与顶点j之间有一条边,边的权即为matrix[i][j]的值*/
for (j = 0; j < numVertex; j ++)
matrix[i][j] = 0;
};
~Graphm() //析构函数
{
for (int i = 0; i < numVertex; i ++)
delete [] matrix[i]; //释放每个matrix[i]申请的空间
delete [] matrix; //释放matrix指针指向的空间
};
Edge FirstEdge(int oneVertex) //返回顶点oneVertex的第一条边
{
Edge myEdge; //边myEdge将作为函数的返回值
myEdge.from = oneVertex; //将顶点oneVertex作为边myEdge的始点
for (int i = 0; i < numVertex; i ++)
{
/* 下面寻找第一个使得matrix[oneVertex][i]不为0的i值,
那么边(oneVertex,i)或者弧<oneVertex,i>即为顶点oneVertex
的第一条边,将顶点i作为边myEdge的终点边myEdge的权为
矩阵元素matrix[oneVertex][i]的值*/
if (matrix[oneVertex][i] != 0)
{
myEdge.to = i;
myEdge.weight = matrix[oneVertex][i];
break;
}
}
return myEdge;
/*如果找到了顶点oneVertex的第一条边,则返回的myEdge确实是一条边;如果没有找到顶点oneVertex的第一条边,则myEdge的成员变量to为-1,根据IsEdge函数判断可知myEdge不是一条边*/
};
Edge NextEdge(Edge preEdge) //返回与边PreEdge有相同关联顶点的下一条边
{
Edge myEdge;
myEdge.from=preEdge.from; /*将边myEdge的始点置为与上一条边preEdge的始点相同*/
if(preEdge.to<numVertex)
{
//如果preEdge.to+1>=numVertex,那么就不存在下一条边了
for(int i=preEdge.to+1; i<numVertex; i++)
{
/*寻找下一个使得//matrix[preEdge.from][i]不为0的i值,那么(preEdge.from,i)或者<preEdge.from,i>即为顶点preEdge.from的下一条边*/
if(matrix[preEdge.from][i]!=0)
{
myEdge.to=i;
myEdge.weight=matrix[preEdge.from][i];
break;
}
}
}
return myEdge; /*如果找到了顶点preEdge.from的下一条边,则返回的myEdge确实是一条边;如果没有找到顶点preEdge.from的下一条边,则myEdge的成员变量to为-1,
根据IsEdge函数判断可知myEdge不是一条边*/
}
void setEdge(int from, int to, int weight) //为图设定一条边
{
if (matrix[from][to] <= 0) //如果matrix[from][to]<=0,则边(from,to)或者<from,to>将是新增的一条边,否则该边已经存在(现在只是对该边进行修改)
{
numEdge ++;
Indegree[to] ++;
}
matrix[from][to] = weight;
};
void delEdge(int from,int to) //删除图的一条边
{
if(matrix[from][to]>0)
{
/*如果matrix[from][to]>0,则边(from,to)或者<from,to>确实存在,否则该边实际上并不存在(从而不必对图的边数和顶点to的入度进行修改)*/
numEdge--;
Indegree[to]--;
}
matrix[from][to]=0;
}
};
// 函数功能:显示顶点
void Graphm::Visit(Graph &G, int v)
{
cout << 'V' << v <<" ";
}
// 函数功能:初始化图
void Graphm::IniGraphm(Graphm *Graphm, int A[N][N])
{
for (int i = 0; i < N; i ++)
{
for (int j = 0; j < N; j ++)
{
if (A[i][j] > 0)
Graphm->setEdge(i, j, A[i][j]);
}
}
}
/******************** 代码5.17 堆的类定义和筛选法 ******************/
#define FALSE 0
#define TRUE 1
template <class T>
class MinHeap //最小堆类定义
{
private:
T* heapArray; //存放堆数据的数组
int CurrentSize; //当前堆中元素数目
int MaxSize; //堆所能容纳的最大元素数目
void swap(int pos_x, int pos_y); //交换位置x和y的元素
void BuildHeap(); //建堆
public:
MinHeap(const int n); //构造函数,n表示初始化堆的最大元素数目
virtual ~MinHeap()
{
delete []heapArray;
}; //析构函数
bool isLeaf(int pos) const; //如果是叶结点,返回TRUE
int leftchild(int pos) const; //返回左孩子位置
int rightchild(int pos) const; //返回右孩子位置
int parent(int pos) const; //返回父结点位置
bool Remove(int pos, T& node); //删除给定下标的元素
bool Insert(const T& newNode); //向堆中插入新元素newNode
T& RemoveMin(); //从堆顶删除最小值
void SiftUp(int position); //从position向上开始调整,使序列成为堆
void SiftDown(int left); //筛选法函数,参数left表示开始处理的数组下标
bool isEmpty()
{
return ((CurrentSize)?false:true);
};
};
template<class T>
MinHeap<T>::MinHeap(const int n)
{
if(n<=0)
return;
CurrentSize=0;
MaxSize=n; //初始化堆容量为n
heapArray=new T[MaxSize]; //创建堆空间
//此处进行堆元素的赋值工作
BuildHeap();
}
template<class T>
bool MinHeap<T>::isLeaf(int pos) const
{
return (pos>=CurrentSize/2)&&(pos<CurrentSize);
}
template<class T>
void MinHeap<T>::BuildHeap()
{
for (int i=CurrentSize/2-1; i>=0; i--) //反复调用筛选函数
SiftDown(i);
}
template<class T>
int MinHeap<T>::leftchild(int pos) const
{
return 2*pos+1; //返回左孩子位置
}
template<class T>
int MinHeap<T>::rightchild(int pos) const
{
return 2*pos+2; //返回右孩子位置
}
template<class T>
int MinHeap<T>::parent(int pos) const
{
return (pos-1)/2; //返回父结点位置
}
template <class T>
bool MinHeap<T>::Insert(const T& newNode) //向堆中插入新元素newNode
{
if(CurrentSize==MaxSize) //堆空间已经满
return FALSE;
heapArray[CurrentSize]=newNode;
SiftUp(CurrentSize); //向上调整
CurrentSize++;
return TRUE;
}
template<class T>
void MinHeap<T>::SiftUp(int position) //从position向上开始调整,使序列成为堆
{
int temppos=position;
T temp=heapArray[temppos];
while((temppos>0)&&(heapArray[parent(temppos)]>temp))
{
heapArray[temppos]=heapArray[parent(temppos)];
temppos=parent(temppos);
}
heapArray[temppos]=temp;
}
template<class T>
void MinHeap<T>::swap(int pos_x, int pos_y) //交换位置x和y的元素
{
T temp = heapArray[pos_x];
heapArray[pos_x] = heapArray[pos_y];
heapArray[pos_y] = temp;
}
template<class T>
T& MinHeap<T>::RemoveMin() //从堆顶删除最小值
{
if(CurrentSize==0)
{
cout<<"Can't Delete";
}
else
{
swap(0,--CurrentSize); //交换堆顶和最后一个元素
if(CurrentSize>1)
SiftDown(0); //从堆顶开始筛选
return heapArray[CurrentSize];
}
}
template<class T>
bool MinHeap<T>::Remove(int pos, T& node) // 删除给定下标的元素
{
if ((pos < 0) || (pos >= CurrentSize))
return false;
node = heapArray[pos];
heapArray[pos] = heapArray[--CurrentSize]; // 用最后的元素值替代删除位置的元素
if (heapArray[parent(pos)] > heapArray[pos])
SiftUp(pos); // 当前元素小于父结点,需要上升调整
else SiftDown(pos); // 当前元素大于父结点,向下筛
return true;
}
template <class T>
void MinHeap<T>::SiftDown(int left)
{
//准备
int i=left; //标识父结点
int j= leftchild (i); //标识关键值较小的子结点
T temp=heapArray[i]; //保存父结点
//过筛
while(j<CurrentSize)
{
if((j<CurrentSize-1)&&(heapArray[j]>heapArray[j+1]))
j++; //j指向右子结点
if(temp>heapArray[j])
{
heapArray[i]=heapArray[j];
i=j;
j=leftchild(j); //向下继续
}
else break;
}
heapArray[i]=temp;
}
//[代码7.8] Dijkstra算法
class Dist //定义Dist类,下面的Dijkstra算法和Floyd算法要用到
{
public:
int index; //顶点的索引值,仅Dijkstra算法会用到
int length; //顶点之间的距离
int pre; //路径最后经过的顶点
Dist() {};
~Dist() {};
bool operator < (const Dist & arg)
{
return (length < arg.length);
}
bool operator == (const Dist &arg)
{
return (length==arg.length);
}
bool operator > (const Dist &arg)
{
return (length>arg.length);
}
bool operator <=(const Dist &arg)
{
return (length<=arg.length);
}
bool operator >= (const Dist &arg)
{
return (length>=arg.length);
}
};
//Dijkstra算法,其中参数G是图,参数s是源顶点,D是保存最短距离及其路径的数组
void Dijkstra(Graphm& G, int s, Dist* &D)
{
D = new Dist[G. VerticesNum()]; // D数组
for (int i = 0; i < G.VerticesNum(); i++) // 初始化Mark数组、D数组
{
G.Mark[i] = UNVISITED;
D[i].index = i;
D[i].length = INFINITE;
D[i].pre = s;
}
D[s].length = 0;
MinHeap<Dist> H(G.EdgesNum()); // 最小值堆(minheap)
H.Insert(D[s]);
for (int i = 0; i < G.VerticesNum(); i++)
{
bool FOUND = false;
Dist d;
while (!H.isEmpty())
{
d = H.RemoveMin();
if(G.Mark[d.index]==UNVISITED) //打印出路径信息
{
cout<< "vertex index: " <<d.index<<" ";
cout<< "vertex pre : " <<d.pre <<" ";
cout<< "V0 --> V" << d.index <<" length : " << d.length <<endl;
}
if (G.Mark[d.index] == UNVISITED) //找到距离s最近的顶点
{
FOUND = true;
break;
}
}
if (!FOUND)
break;
int v = d.index;
G.Mark[v] = VISITED; // 把该点加入已访问组
// 因为v的加入,需要刷新v邻接点的D值
for (Edge e = G.FirstEdge(v); G.IsEdge(e); e = G.NextEdge(e))
if (D[G.ToVertex(e)].length > (D[v].length+G.Weight(e)))
{
D[G.ToVertex(e)].length = D[v].length+G.Weight(e);
D[G.ToVertex(e)].pre = v;
H.Insert(D[G.ToVertex(e)]);
}
}
}
int A[N][N] = //图7.20 单源最短路径的示例
{
// v0 v1 v2 v3 v4
0, 10, 0, 30, 100,
0, 0, 50, 0, 0,
0, 0, 0, 0, 10,
0, 10, 20, 0, 60,
0, 0, 0, 0, 0,
};
int main()
{
Graphm aGraphm(N); // 建立图
aGraphm.IniGraphm(&aGraphm, A); // 初始化图
Dist *D;
Dijkstra(aGraphm, 0, D);
return 0;
}