C++有向帶權圖單源最短路徑
參考《算法》最短路徑章節編寫實現代碼,包括無負權邊無環的Dijkstra算法,也包括可以處理一般性問題的Bellman-Ford算法。同時採用泛型編程,圖節點可以實現任意類型,具體原理參見《算法》最短路徑章節,實現代碼如下所示:
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
/*
頂點 中間節點
VNode ENode
0 | A --> 2(C) 1(B)
1 | B --> 4(E) 3(D) 0(A)
2 | C --> 6(G) 5(F) 0(A)
3 | D --> 7(H) 1(B)
4 | E --> 7(H) 1(B)
5 | F --> 6(G) 2(C)
6 | G --> 5(F) 2(C)
7 | H --> 4(E) 3(D)
*/
const int MAX = 20;
struct ENode //鄰接表的中間節點
{
int adjvex; //對應索引
double weight; //邊的權重
ENode* next;
};
template<class T>
struct VNode //鄰接表頂點
{
T vertex; //值
ENode* firstarc; //指向第一個中間節點
};
template<class T>
class ALGraph //圖
{
private:
VNode<T> adjList[MAX]; //鄰接表數組
int vexNum; //節點數量
int arcNum; //連邊數量
bool visited[MAX]; //標記被訪問
int edgeTo[MAX]; //最短路徑連邊記錄
double distTo[MAX]; //最短距離
queue<int> relax_edge; //需要放鬆的隊列
bool onQ[MAX]; //標記是否在隊列中
public:
void CreateGraph(); //創建圖
void PrintGraph(); //打印圖
void Dijkstra(int source);
void DijkstraVisit(int &delmin, int source);
void BellmanFord(int source); //BellmanFord算法
void BellmanFord_Relax(int v); //放鬆操作
};
template<class T>
void ALGraph<T>::CreateGraph()
{
cout << "請輸入圖的頂點數:" << endl;
cin >> this->vexNum;
cout << "請輸入圖的弧數:" << endl;
cin >> this->arcNum;
cout << "請輸入頂點信息:" << endl;
for (int i = 0; i<this->vexNum; i++) //構建頂點數組
{
cin >> this->adjList[i].vertex;
this->adjList[i].firstarc = nullptr;
}
cout << "請輸入" << this->arcNum << "個弧的信息:" << endl;
for (int i = 0; i<this->arcNum; i++) //構建每條鄰接表
{
int h1, h2;
double weight;
cin >> h1 >> h2 >> weight;
ENode* temp = new ENode();
temp->adjvex = h2;
temp->weight = weight;
temp->next = this->adjList[h1].firstarc;
this->adjList[h1].firstarc = temp;
}
}
template<class T>
void ALGraph<T>::PrintGraph()
{
for (int i = 0; i<this->vexNum; i++)
{
cout << this->adjList[i].vertex << "--------->";
ENode* p = this->adjList[i].firstarc;
while (p)
{
cout << this->adjList[p->adjvex].vertex << "(" << p->weight << ")" << " ";
p = p->next;
}
cout << endl;
}
}
template<class T>
void ALGraph<T>::Dijkstra(int source)
{
int delmin = 0; //加入已知最短路徑的點
for (int i = 0; i < this->vexNum; i++)
{
visited[i] = false;
distTo[i] = i == source ? 0 : __DBL_MAX__; //初始化頂點到各點權重
}
for (int i = 0; i < this->vexNum; i++)
{
DijkstraVisit(delmin, source); //加入已知點並與改點有關的所有權重
}
for (int i = 0; i < this->vexNum; i++) //打印最短路徑
{
cout << this->adjList[this->edgeTo[i]].vertex << "--->" << this->adjList[i].vertex << " " << distTo[i] << endl;
}
}
template<class T>
void ALGraph<T>::DijkstraVisit(int& delmin, int source)
{
double mindst = __DBL_MAX__;
visited[delmin] = true;
if (delmin == source)
{
edgeTo[source] = source;
distTo[source] = 0;
}
ENode* p = this->adjList[delmin].firstarc;
while (p)
{
if (visited[p->adjvex])
{
p = p->next;
continue;
}
if (distTo[delmin] + p->weight < distTo[p->adjvex]) //如果經過delmin再到p點的距離小於原來起點到p點距離,需要更新權重
{
edgeTo[p->adjvex] = delmin; //更新經過的路徑點
distTo[p->adjvex] = distTo[delmin] + p->weight; //更新距離
}
p = p->next;
}
for (int i = 0; i < this->vexNum; i++) //找出下一次要加入已知最短路徑部分的點
{
if (!visited[i] && mindst > distTo[i])
{
mindst = distTo[i];
delmin = i;
}
}
}
template<class T>
void ALGraph<T>::BellmanFord(int source)
{
for(int i=0;i<this->vexNum;i++) //初始化edgeTo[]和位於列表標記數組
{
distTo[i] = i==source? 0:__DBL_MAX__;
onQ[i] = false;
}
relax_edge.push(source); //源點加入放鬆隊列
onQ[source] = true; //標記源點已存在於放鬆隊列
while(!relax_edge.empty()) //放鬆隊列不爲空,不斷放鬆
{
int v = relax_edge.front();
onQ[v] = false;
BellmanFord_Relax(v);
relax_edge.pop();
}
for (int i = 0; i < this->vexNum; i++) //打印最短路徑
{
cout << this->adjList[this->edgeTo[i]].vertex << "--->" << this->adjList[i].vertex << " " << distTo[i] << endl;
}
}
template<class T>
void ALGraph<T>::BellmanFord_Relax(int v)
{
ENode* p = this->adjList[v].firstarc;
while(p)
{
if(distTo[p->adjvex]>distTo[v]+p->weight) //需要放鬆
{
distTo[p->adjvex] = distTo[v]+p->weight; //更新
edgeTo[p->adjvex] = v;
if(!onQ[p->adjvex])
{
relax_edge.push(p->adjvex); //該點若不在隊列,則將其加入隊列
onQ[p->adjvex] = true;
}
}
p = p->next;
}
}
int main()
{
ALGraph<int> *graph = new ALGraph<int>();
graph->CreateGraph();
graph->PrintGraph();
graph->BellmanFord(0);
return 0;
}