跳转至

引入

二叉树的BFS层序遍历

Dijkstra 算法框架

首先,我们先看一下 Dijkstra 算法的签名

class State {
    // 图节点的 id
    int id;
    // 从 start 节点到当前节点的距离
    int distFromStart;

public:
    State(int id, int distFromStart) {
        this->id = id;
        this->distFromStart = distFromStart;
    }
};

标准的 Dijkstra 算法会把从起点 start 到所有其他节点的最短路径都算出来。

当然,如果你的需求只是计算从起点 start 到某一个终点 end 的最短路径,那么在标准 Dijkstra 算法上稍作修改就可以更高效地完成这个需求,这个我们后面再说。

state类记录状态和距离

其次,我们也需要一个 State 类来辅助算法的运行

class State {
    // 图节点的 id
    int id;
    // 从 start 节点到当前节点的距离
    int distFromStart;

public:
    State(int id, int distFromStart) {
        this->id = id;
        this->distFromStart = distFromStart;
    }
};

类似刚才二叉树的层序遍历,我们也需要用 State 类记录一些额外信息,也就是使用 distFromStart 变量记录从起点 start 到当前这个节点的距离。

visted数组防止回头路

刚才说普通 BFS 算法中,根据 BFS 的逻辑和无权图的特点,第一次遇到某个节点所走的步数就是最短距离,所以用一个 visited 数组防止走回头路,每个节点只会经过一次。

distTo数组记录最短路径权重

优先队列distFromStart优化算法

Dijkstra 可以理解成一个带 dp table(或者说备忘录)的 BFS 算法

伪代码如下:

// 返回节点 from 到节点 to 之间的边的权重
int weight(int from, int to);

// 输入节点 s 返回 s 的相邻节点
vector<int> adj(int s);

// 输入一幅图和一个起点 start,计算 start 到其他节点的最短距离
vector<int> dijkstra(int start, vector<int> graph[]) {
    // 图中节点的个数
    int V = graph.size();
    // 记录最短路径的权重,你可以理解为 dp table
    // 定义:distTo[i] 的值就是节点 start 到达节点 i 的最短路径权重
    int distTo[V];
    // 求最小值,所以 dp table 初始化为正无穷
    memset(distTo, INT_MAX, sizeof(distTo));
    // base case,start 到 start 的最短距离就是 0
    distTo[start] = 0;

    // 优先级队列,distFromStart 较小的排在前面
    priority_queue<State, vector<State>, decltype(&comparator)> pq(&comparator);

    // 从起点 start 开始进行 BFS
    pq.push(State(start, 0));

    while (!pq.empty()) {
        State curState = pq.top();
        pq.pop();
        int curNodeID = curState.id;
        int curDistFromStart = curState.distFromStart;

        if (curDistFromStart > distTo[curNodeID]) {
            // 已经有一条更短的路径到达 curNode 节点了
            continue;
        }
        // 将 curNode 的相邻节点装入队列
        for (int nextNodeID: adj(curNodeID)) {
            // 看看从 curNode 达到 nextNode 的距离是否会更短
            int distToNextNode = distTo[curNodeID] + weight(curNodeID, nextNodeID);
            if (distTo[nextNodeID] > distToNextNode) {
                // 更新 dp table
                distTo[nextNodeID] = distToNextNode;
                // 将这个节点以及距离放入队列
                pq.push(State(nextNodeID, distToNextNode));
            }
        }
    }
    vector<int> result;
    for (int i = 0; i < V; i++) {
        result.push_back(distTo[i]);
    }
    return result;
}