这是一个经典的求图的最短路径算法。
我发现不同的教材上面所采用的不同的描述方式实在都有点费解,所以这里决定自己给出一种描述方式:
主要思想是将顶点划分为两个集合:已找到最短路径的加入S集,尚未找到的在U集。
(1)创建一个数组dist[size],记录所有顶点到源点的当前已知最短距离,初始均为无穷大。
(2)选取源点,加入S集,源点到源点的距离为0。
(3)更新新加入S集的点new_vertex所有可达的点v[i]到源点的最短距离,具体方式是比较distance(v[i], new_vertex)+dist[new_vertex]的值和dist[v[i]]的值,谁小就取谁。
(4)在所有未加入S集的点中,取dist最小的一个,加入S。
(5)重复(3)、(4)直至所有存在路径的点都已加入S。
(不存在路径的点,dist会一直为无穷大,最后是否算加入S,可灵活处理,比如我这里的代码,干脆就不加入S了。)
代码如下:
根据自己的理解可能和各种教材上的有些不一样。
用了较多stl容器,效率可能较低。
一般邻接表法表示图,弧都用链表来记录,但我觉得vector也行。
因为顶点只记录了所有从此点出发的弧,所以导致算法中一些计算很不方便,可以考虑在顶点结构中直接加入各弧头顶点的索引,以后用起来就比较方便些。
因为在创建图时,用的是vector的push_back方法,所以必须按顺序输入各顶点,不支持乱序输入。
/*
迪杰斯特拉算法
部分函数定义为inline形式纯粹为书写方便
*/
#include <iostream>
#include <vector>
#include <stack>
using std::cin;
using std::cout;
using std::endl;
using std::vector;
using std::stack;
struct CArc
{
int m_iFrom;
int m_iTo;
double m_dDistance;
public:
inline CArc(int from, int to, int dis):
m_iFrom(from),
m_iTo(to),
m_dDistance(dis)
{}
inline int GetTo(){return m_iTo;}
inline double GetDistance(){return m_dDistance;}
};
class CVertex
{
int m_iIndex;
vector<CArc> m_Arcs;
public:
inline CVertex(int index):m_iIndex(index){}
inline void AddArc(CArc arc)
{
m_Arcs.push_back(arc);
}
inline vector<CArc> GetArcs()
{
return m_Arcs;
}
int m_iFront; //专为Dijkstra算法保存已得路径设置的前一个节点索引
};
class CWDiGraph
{
vector<CVertex> m_Vertexes;
public:
void AddVertex(int m_iIndex);
void AddArc(int m_iFrom, int m_iTo, double m_dDistance);
void ShowMatrix(); //邻接矩阵形式显示图
void ShowList(); //邻接表形式显示图
void Dijkstra(int m_iFrom, int m_iTo); //采用迪杰斯特拉算法搜寻最短路径并输出
void ShowShortestPath(int m_iFrom, int m_iTo);
};
//注意必须按顺序添加每个顶点,不支持乱序输入
void CWDiGraph::AddVertex(int m_iIndex)
{
if (m_iIndex != m_Vertexes.size())
{
cout<<"input error, please import vertexes in correct order"<<endl;
getchar();
return;
}
CVertex vertex(m_iIndex);
m_Vertexes.push_back(vertex);
}
void CWDiGraph::AddArc(int m_iFrom, int m_iTo, double m_dDistance)
{
if ((m_iFrom < 0) || (m_iFrom > m_Vertexes.size())
|| (m_iTo < 0) || (m_iTo > m_Vertexes.size()))
{
cout<<"input error, please check the parameter"<<endl;
getchar();
return;
}
CArc arc(m_iFrom, m_iTo, m_dDistance);
m_Vertexes[m_iFrom].AddArc(arc);
}
void CWDiGraph::ShowMatrix()
{
for(int i=0; i<m_Vertexes.size(); ++i)
{
double *row = new double[m_Vertexes.size()];
for(int j=0; j<m_Vertexes.size(); ++j)
{
row[j] = DBL_MAX; //所有顶点距离预置为INFINITE
}
//记录可达顶点的距离
for(int j=0; j<m_Vertexes[i].GetArcs().size(); j++)
{
int to = (m_Vertexes[i].GetArcs())[j].GetTo();
row[to] = m_Vertexes[i].GetArcs()[j].GetDistance();
}
//output
for(int j=0; j<m_Vertexes.size(); ++j)
{
if (row[j] == DBL_MAX)
{
cout<<"× "; //无穷大距离用叉号表示
}
else
{
cout<<row[j]<<" ";
}
}
cout<<endl;
delete row;
row = NULL;
}
}
void CWDiGraph::ShowList()
{
//略
}
void CWDiGraph::Dijkstra(int m_iFrom, int m_iTo)
{
int size = m_Vertexes.size();
bool *found = new bool[size]; //用于记录顶点是否已加入S集合
double *dist = new double[size]; //当前最短路径距离列表
int new_vertex; //新加入S的点
for(int i=0; i<size; ++i)
{
found[i] = 0;
dist[i] = DBL_MAX;
}
found[m_iFrom] = true;
dist[m_iFrom] = 0; //源点到自身距离为0
new_vertex = m_iFrom;
for(int i=1; i<size; ++i) //注意此处不能用while (!found[m_iTo]),因为可能有些点没有路径可达,导致死循环
{
//更新与新加入顶点相连的顶点的距离值
vector<CArc> arcs = m_Vertexes[new_vertex].GetArcs();
for (int i=0; i<arcs.size(); ++i)
{
//记录顶点的最短路径父节点
m_Vertexes[arcs[i].GetTo()].m_iFront = new_vertex;
//更新最小距离值
if ((arcs[i].GetDistance() + dist[new_vertex]) < dist[arcs[i].GetTo()])
{
dist[arcs[i].GetTo()] = arcs[i].GetDistance() + dist[new_vertex];
}
}
//找出尚未加入S的距离值最小的顶点,加入S
double min_dist = DBL_MAX; //最短距离
for(int i=0; i<size; ++i)
{
if ((!found[i]) && (dist[i] < min_dist))
{
min_dist = dist[i];
new_vertex = i;
}
}
found[new_vertex] = true;
}
//Show result
if (found[m_iTo])
{
ShowShortestPath(m_iFrom, m_iTo);
}
else
{
cout<<"there's no way to vertex "<<m_iTo<<endl;
}
}
void CWDiGraph::ShowShortestPath(int m_iFrom, int m_iTo)
{
stack<int> path;
int cur_vertex = m_iTo;
do
{
path.push(cur_vertex);
cur_vertex = m_Vertexes[cur_vertex].m_iFront;
}
while (cur_vertex != m_iFrom);
cout<<cur_vertex;
while (!path.empty())
{
cur_vertex = path.top();
path.pop();
cout<<"--"<<cur_vertex;
}
cout<<endl;
}
int main()
{
CWDiGraph graph;
//输入见《数据结构》图7.34
for(int i=0; i<6; ++i)
{
graph.AddVertex(i);
}
graph.AddArc(0, 5, 100);
graph.AddArc(0, 4, 30);
graph.AddArc(0, 2, 10);
graph.AddArc(1, 2, 5);
graph.AddArc(2, 3, 50);
graph.AddArc(3, 5, 10);
graph.AddArc(4, 3, 20);
graph.AddArc(4, 5, 60);
graph.ShowMatrix();
for(int i=1; i<6; ++i)
{
graph.Dijkstra(0,i);
}
}
/*
运行结果:
× × 10 × 30 100
× × 5 × × ×
× × × 50 × ×
× × × × × 10
× × × 20 × 60
× × × × × ×
there's no way to vertex 1
0--2
0--4--3
0--4
0--4--3--5
注意点:
注意搜索以及显示最短路径时,都需考虑没有路径的情况
*/