迪杰斯特拉(Dijkstra)最短路径算法C++实现
前言
算法参考:《大话数据结构》
一、图的结构
使用邻接表来保存顶点之间连接的信息
二、C++实现
代码如下(示例):
#pragma once
#include <vector>
#include <string>
/*
*brief \邻接表的连接两个顶点的边
*param \
*/
struct ArcNode
{
// 边的信息
int weight = 0; // 权重
// ...
int vexIndex = -1; // 边指向的顶点的索引
ArcNode* next = nullptr; // 指向的下一条边
ArcNode(int weight, int vertexIndex, ArcNode* node = nullptr)
: weight(weight),
vexIndex(vertexIndex),
next(node)
{
}
};
/*
*brief \邻接表的顶点信息
*param \
*/
struct Vertex
{
std::string data; // 顶点信息
int in = 0; // 顶点入度
ArcNode* first = nullptr; // 指向的第一条边
Vertex() {}
Vertex(const std::string& d, int in, ArcNode* arc = nullptr)
: data(d),
in(in),
first(arc)
{
}
// 插入边(头插法)
void insertArc(ArcNode* node)
{
node->next = first;
first = node;
}
};
/*
*brief \邻接表
*param \
*/
struct ALGraph
{
int arcNum = 0; // 边数量
int vertexNum = 0; // 顶点数量
std::vector<Vertex> vertices;
};
/*
*brief \最短路径算法: 迪杰斯特拉
*param \
*/
class ShortestPath
{
public:
ShortestPath(int vexNum, int arcNum, const std::vector<Vertex>& vertices)
{
m_alGraph.arcNum = arcNum;
m_alGraph.vertexNum = vexNum;
m_alGraph.vertices = vertices;
}
// 求v0顶点到任意顶点的最短路径
void Dijkstra(int v0, std::vector<int>& path, std::vector<int>& distance)
{
int vertexNum = m_alGraph.vertexNum;
// 表示对应位置的顶点是否求得最短路径
std::vector<int> final(vertexNum, 0);
final[v0] = 1; // v0不需要查找
// 初始化
path = std::vector<int>(vertexNum, -1);
distance = getDistance(v0);
for (int i = 1; i < vertexNum; i++)
{
int minIndex = v0;
int min = INT_MAX / 2;
// 每次找到与v0连接最小权值的顶点索引
for (int j = 0; j < vertexNum; j++)
{
if (!final[j] && distance[j] < min)
{
minIndex = j;
min = distance[j];
}
}
final[minIndex] = 1;
// 获取minIndex到其余顶点的权值
// 判断v0经过minIndex顶点是否比直接到其余顶点的权值更小
std::vector<int> minDist = getDistance(minIndex);
for (int j = 0; j < vertexNum; j++)
{
if (!final[j] && (min + minDist[j]) < distance[j])
{
// 更新权值,v0经过minIndex比直接到j顶点的权值更小
distance[j] = min + minDist[j];
// v0到j顶点经过minIndex顶点路径最短
path[j] = minIndex;
}
}
}
}
// 释放顶点的边内存
void destoryArcNode()
{
for (int i = 0; i < m_alGraph.vertexNum; i++)
{
ArcNode* node = m_alGraph.vertices[i].first;
while (node)
{
ArcNode* temp = node;
node = node->next;
delete temp;
}
}
}
const ALGraph* getGraph() const { return &m_alGraph; }
private:
// 获取v0顶点到任意顶点的权值(邻接表)
std::vector<int> getDistance(int v0)
{
// (INT_MAT / 2)表示两顶点间没有连接,无法到达
std::vector<int> distance(m_alGraph.vertexNum, INT_MAX / 2);
distance[v0] = 0;
// 遍历顶点的所有边
ArcNode* node = m_alGraph.vertices[v0].first;
while (node)
{
distance[node->vexIndex] = node->weight;
node = node->next;
}
return distance;
}
private:
ALGraph m_alGraph;
};
三、使用方法
创建邻接表,顶点之间的关系如下图所示:

代码如下(示例):
#include "Dijsktra.h"
#include <stack>
// 打印v0到v的走过的路径以及权值
void printPath(const ALGraph* alGraph, std::vector<int> path, std::vector<int> dist, int v0, int v)
{
// 打印v0到v的路径
// 由于path保存是前驱节点,需要逆序输出
std::stack<int> stack;
if (dist[v] < INT_MAX / 2)
stack.push(v);
int i = v;
while (path[i] != -1)
{
stack.push(path[i]);
i = path[i];
}
if (dist[v] < INT_MAX / 2)
stack.push(v0);
printf("顶点(%s, %s)权值最小的路径: ", alGraph->vertices[v0].data.c_str(), alGraph->vertices[v].data.c_str());
if (stack.empty())
{
printf("不可到达");
}
else
{
while (!stack.empty())
{
int v = stack.top();
stack.pop();
printf("%s", alGraph->vertices[v].data.c_str());
if (stack.size() > 0)
printf("->");
}
}
printf("\n");
printf("顶点(%s, %s)权值为: %d\n", alGraph->vertices[v0].data.c_str(), alGraph->vertices[v].data.c_str(), dist[v]);
printf("\n");
}
int main(void)
{
// 顶点(入度在这里不需要)
Vertex a("a", 0);
Vertex b("b", 1);
Vertex c("c", 1);
Vertex d("d", 2);
Vertex e("e", 1);
Vertex f("f", 1);
// 边(边的权值和指向的顶点下标)
ArcNode* ac = new ArcNode(120, 2);
ArcNode* ad = new ArcNode(230, 3);
ArcNode* cb = new ArcNode(40, 1);
ArcNode* ce = new ArcNode(50, 4);
ArcNode* bd = new ArcNode(10, 3);
ArcNode* de = new ArcNode(60, 4);
ArcNode* fa = new ArcNode(260, 0);
// 插入边
a.insertArc(ac);
a.insertArc(ad);
c.insertArc(cb);
c.insertArc(ce);
b.insertArc(bd);
d.insertArc(de);
f.insertArc(fa);
std::vector<Vertex> vertices;
vertices.push_back(a);
vertices.push_back(b);
vertices.push_back(c);
vertices.push_back(d);
vertices.push_back(e);
vertices.push_back(f);
ShortestPath shortestPath(6, 6, vertices);
// 求v0到各个顶点最短路径
int v0 = 0;
std::vector<int> path;
std::vector<int> distance;
shortestPath.Dijkstra(v0, path, distance);
// 打印
const ALGraph* glGraph = shortestPath.getGraph();
for (int i = 0; i < glGraph->vertexNum; i++)
{
printPath(glGraph, path, distance, v0, i);
}
// 释放内存
shortestPath.destoryArcNode();
return 0;
}
输出结果:
顶点(a, a)权值最小的路径: a->a
顶点(a, a)权值为: 0顶点(a, b)权值最小的路径: a->c->b
顶点(a, b)权值为: 160顶点(a, c)权值最小的路径: a->c
顶点(a, c)权值为: 120顶点(a, d)权值最小的路径: a->c->b->d
顶点(a, d)权值为: 170顶点(a, e)权值最小的路径: a->c->e
顶点(a, e)权值为: 170顶点(a, f)权值最小的路径: 不可到达
顶点(a, f)权值为: 1073741823
1万+

被折叠的 条评论
为什么被折叠?



