一、简介
迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径。
它的主要特点是以起始点为中心向外层层扩展(广度优先遍历思想),直到扩展到终点为止。以参考链接博文的网络节点图为例。
二、基本思想
- 首先设定一个排序容器pq,用来存储节点,可以方便我们寻找出距离起点最近的点(优先队列);设定一个距离记录容器dis,用来更新每个节点到起点的最小距离;设定一个路径记录容器pre用来更新距起点的最短路径;
- 首先指定一个起点,假设为D,放入排序容器pq中;
- 只要pq不为空就开始循环;
- 然后遍历所有D节点的邻居节点(即CE),并计算每个邻居节点距离起点D的距离(最近节点的距离+邻居节点的距离和当前该邻居节点距起点的距离,两者取小值),更新为距离起点更小的值,放入距离容器dis中;
- 同时距离若更小,则在路径记录容器pre更新邻居节点和当前节点的路径关系(距离更小才更新);
- 同时距离若更小,则将更新距离后的邻居节点放入排序容器pq中(距离更小才更新);
- 然后再从排序容器pq中选出距离起点最近的点,重复步骤3-7
- 知道寻找出目标节点或pq为空。
三、模拟流程
四、C++代码示例
#include <iostream>
#include <queue>
#include <vector>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
#include <climits>
struct Node {
std::string name;
std::unordered_map<std::string, int> neighbors;
Node(std::string nm): name(nm) {};
Node(std::string nm, std::unordered_map<std::string, int> nbhd): name(nm), neighbors(nbhd) {};
};
// 初始化节点
std::vector<Node*> initialize_nodes() {
std::vector<Node*> vec;
vec.push_back(new Node("A", {{"B", 12}, {"F", 16}, {"G", 14}}));
vec.push_back(new Node("B", {{"A", 12}, {"F", 7}, {"C", 10}}));
vec.push_back(new Node("C", {{"B", 10}, {"F", 6}, {"E", 5}, {"D", 3}}));
vec.push_back(new Node("D", {{"C", 3}, {"E", 4}}));
vec.push_back(new Node("E", {{"G", 8}, {"F", 2}, {"C", 5}, {"D", 4}}));
vec.push_back(new Node("F", {{"G", 9}, {"A", 16}, {"B", 7}, {"C", 6}, {"E", 2}}));
vec.push_back(new Node("G", {{"A", 14}, {"F", 9}, {"E", 8}}));
return vec;
};
// 清理内存防止泄露
void cleaup_nodes(std::vector<Node*> nodes){
if (nodes.empty())
return;
for (auto node : nodes) {
delete node;
}
};
// 寻找节点是否存在
Node* find_node(const std::vector<Node*>& nodes, const std::string& name) {
for (auto node : nodes) {
if (node->name == name) {
return node;
}
}
return nullptr;
};
void dijkstra(const std::vector<Node*>& nodes, const std::string& start, const std::string& target) {
std::unordered_map<std::string, int> distances; // 距离起点的距离
std::unordered_map<std::string, std::string> previous;
auto cmp = [&](const std::pair<int, std::string>& a, const std::pair<int, std::string>& b) {
return a.first > b.first;
};
std::priority_queue<std::pair<int, std::string>, std::vector<std::pair<int, std::string>>, decltype(cmp)> pq(cmp);// 优先队列
// 默认每个点距离起点无穷远
for (const auto& node : nodes) {
distances[node->name] = INT_MAX;
}
// 起点距离为0
distances[start] = 0;
pq.push({0, start});
while (!pq.empty()) {
// 选取最短的路径节点
auto [current_distance, current_name] = pq.top();
pq.pop();
// 找到则直接退出循环
if (current_name == target)
break;
Node* current_node = find_node(nodes, current_name);
if (!current_node)
continue;
for (const auto& [neighbor_name, weight] : current_node->neighbors) {
int new_distance = current_distance + weight; // 更新其邻居点到起点的距离
if (new_distance < distances[neighbor_name]) { // 如果这条路比之前的更短
distances[neighbor_name] = new_distance; // 更新邻居节点到起点的距离
previous[neighbor_name] = current_name; // 更新父节点
pq.push({new_distance, neighbor_name}); // 把更近的路径塞入队列中自动排序
}
}
}
std::vector<std::string> path;
for (std::string at = target; at != ""; at = previous[at]) {
path.push_back(at);
if (at == start)
break;
}
std::reverse(path.begin(), path.end());
// 打印输出结果
if (path.front() == start) {
std::cout << "从" << start << "到" << target << "的最短路径: ";
for (const auto& name : path) {
if (name == start){
std::cout << name;
}else{
std::cout << "-->" << name ;
}
}
std::cout << "\n总距离: " << distances[target] << std::endl;
} else {
std::cout << "无路径从" << start << "到" << target << std::endl;
}
}
int main() {
std::vector<Node*> nodes = initialize_nodes();
dijkstra(nodes, "D", "A");
cleaup_nodes(nodes);
return 0;
}
参考链接: