Dijkstra算法是一种求解非负权图上单源最短路的算法。
如果图中有负权边,Dijkstra算法不能得到正确的答案。
记源点为 \(s\) 点、\(i\) 点到 \(s\) 点的最短路为 \(dis(i)\),已确定最短路长度的点集为 \(S\),未确定最短路长度的点集 \(T\)。则算法步骤如下:
- \(s\) 点属于 \(S\) 集合,其它点都属于 \(T\) 集合。记\(dis(s)=0\),其他点 \(dis = +\infty\)。
- 选 \(T\) 集合中 \(dis\) 最小的一个点 \(p\),移到 \(S\) 集合中。对 \(p\) 点的所有出边执行如下操作:
记那条出边的长(权值)为 \(w\)、边另一端的点为 \(q\) 点。若 \(dis(p) + w < dis(q)\),那么 \(dis(q) = dis(p) + w\)。 - 若 \(T\) 集合不为空,再次进行步骤2。
朴素实现在步骤2选点时,直接暴力遍历在 \(T\) 集合寻找最短路长度最小的结点。这样寻找极慢。用优先队列优化是容易写的优化方法,做法是:队列按 \(dis\) 值排队,大者优先;更新 \(dis(i)\) 时 \(i\) 点入队;出队 \(i\) 点时若 \(i\) 点是已在 \(S\) 集合中则跳过。
如下是用优先队列优化的Dijkstra算法。
点击查看代码
#include <vector>
#include <queue>// 相当于正无限
const unsigned int LARGE_NUMBER = 0x3f3f3f3f;// 边的结构体
struct side {unsigned int to_point, length;side(unsigned int to_point, unsigned int length) {this->to_point = to_point;this->length = length;}
};// 用于优先队列
struct updated_point {unsigned int point_id, answer;updated_point(unsigned int point_id, unsigned int answer) {this->point_id = point_id;this->answer = answer;}// 从大到小排列bool operator<(const updated_point& right) const {return this->answer > right.answer;}
};/*
point_num: 点数。
start_point: 源点编号。
sides: 边数组。sides[i]内存放编号i的点的所有边。
answers: 答案数组。
*/
void dijkstra(unsigned int point_num, unsigned int start_point, std::vector<side>* sides, unsigned int* answers) {bool* is_visited = new bool[point_num];std::priority_queue<updated_point> updated_points;// 初始化for (unsigned int i = 0; i < point_num; ++i) {answers[i] = LARGE_NUMBER;is_visited[i] = false;}answers[start_point] = 0;updated_points.push(updated_point(start_point, 0));while (!updated_points.empty()) {updated_point point = updated_points.top();updated_points.pop();// 确认不在S集合中if (is_visited[point.point_id]) {continue;}// 放入S集合is_visited[point.point_id] = true;// 遍历边for (unsigned int i = 0; i < sides[point.point_id].size(); ++i) {side out_side = sides[point.point_id][i];// 更新disif (answers[out_side.to_point] > answers[point.point_id] + out_side.length) {answers[out_side.to_point] = answers[point.point_id] + out_side.length;updated_points.push(updated_point(out_side.to_point, answers[out_side.to_point]));}}}// 清理delete[] is_visited;
}
练习题:
洛谷 P4779 【模板】单源最短路径(标准版)