Dijkstra算法模板及应用

致谢

着重感谢labuladong的算法,东哥 https://labuladong.github.io/algo/

迪杰斯特拉算法

无权图

广度优先算法BFS,层序遍历,while循环相当于纵向遍历,while中内嵌的for循环进行每一层的横向遍历,所以层数depth/step在该场景下会使用到

BFS 算法框架也是 while 循环嵌套 for 循环的形式,也用了一个 step 变量记录 for 循环执行的次数,无非就是多用了一个 visited 集合记录走过的节点,防止走回头路罢了。当你每次从队列中拿出节点 cur 的时候,从 startcur 的最短权重就是 step 记录的步数。

// 输入起点,进行 BFS 搜索
int BFS(Node start) {
    Queue<Node> q; // 核心数据结构
    Set<Node> visited; // 避免走回头路
    
    q.offer(start); // 将起点加入队列
    visited.add(start);

    int step = 0; // 记录搜索的步数
    while (q not empty) {
        int sz = q.size();
        /* 将当前队列中的所有节点向四周扩散一步 */
        for (int i = 0; i < sz; i++) {
            Node cur = q.poll();
            printf("从 %s 到 %s 的最短距离是 %s", start, cur, step);

            /* 将 cur 的相邻节点加入队列 */
            for (Node x : cur.adj()) {
                if (x not in visited) {
                    q.offer(x);
                    visited.add(x);
                }
            }
        }
        step++;
    }
}

加权图

到了「加权图」的场景,事情就没有这么简单了,因为你不能默认每条边的「权重」都是 1 了,这个权重可以是任意正数(Dijkstra 算法要求不能存在负权重边)。

在这里插入图片描述

如果沿用 BFS 算法中的 step 变量记录「步数」,显然红色路径一步就可以走到终点,但是这一步的权重很大正确的最小权重路径应该是绿色的路径,虽然需要走很多步,但是路径权重依然很小

「加权图」中的最短路径问题,「步数」已经没有参考意义了,「路径的权重之和」才有意义,所以这个 for 循环可以被去掉。

没有 for 循环,你也没办法维护 depth 变量了。如果你想同时维护 depth 变量,让每个节点 cur 知道自己在第几层,可以想其他办法,比如新建一个 State 类,记录每个节点所在的层数。

class State {
    // 记录 node 节点的深度
    int depth;
    TreeNode node;

    State(TreeNode node, int depth) {
        this.depth = depth;
        this.node = node;
    }
}

// 输入一棵二叉树的根节点,遍历这棵二叉树所有节点
void levelTraverse(TreeNode root) {
    if (root == null) return 0;
    Queue<State> q = new LinkedList<>();
    q.offer(new State(root, 1));

    // 遍历二叉树的每一个节点
    while (!q.isEmpty()) {
        State cur = q.poll();
        TreeNode cur_node = cur.node;
        int cur_depth = cur.depth;
        printf("节点 %s 在第 %s 层", cur_node, cur_depth);

        // 将子节点放入队列
        if (cur_node.left != null) {
            q.offer(new State(cur_node.left, cur_depth + 1));
        }
        if (cur_node.right != null) {
            q.offer(new State(cur_node.right, cur_depth + 1));
        }
    }
}

标准的 Dijkstra 算法会把从起点 start 到所有其他节点的最短路径都算出来。从起点 start 到节点 6 的最短路径权重的值就是 distTo[6]

类似刚才二叉树的层序遍历,我们也需要用 State 类记录一些额外信息,也就是使用 distFromStart 变量记录从起点 start 到当前这个节点的距离(在有权图中表示累积权重)。

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

加权图中的 Dijkstra 算法和无权图中的普通 BFS 算法不同,在 Dijkstra 算法中,你第一次经过某个节点时的路径权重,不见得就是最小的,所以对于同一个节点,我们可能会经过多次,而且每次的 distFromStart 可能都不一样。如下图,取 distFromStart 最小的那次,不就是从起点 start 到节点 5 的最短路径权重了。

在这里插入图片描述

伪代码

伪代码也很重要,在解决邻接表与临界矩阵类问题时,只需要在对应的伪代码处放入相应的实现即可

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

// 用 State 类记录一些额外信息,也就是使用 distFromStart 变量记录从起点 start 到当前这个节点的距离
// 在 Dijkstra 算法中,你第一次经过某个节点时的路径权重,不见得就是最小的,所以对于同一个节点,我们可能会经过多次,
// 而且每次的 distFromStart 可能都不一样
class State 
{
public:
    // 图节点的 id
    int id;
    // 从 start 节点到当前节点的距离
    int distFromStart;
    // 构造函数,对该对象进行初始化,记录图中某个节点对应的id和累积权重
    State(int id, int distFromStart) : id(id), distFromStart(distFromStart) {}
};

class MyComp
{
public:
    // 将累积权重小的放在堆顶
    bool operator()(State a, State b)
    {
        return a.distFromStart > b.distFromStart;
    }
};

// 伪代码

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

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

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

    // 优先级队列,distFromStart 较小的排在前面
    priority_queue<State, vector<State>, MyComp> pq;

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

    while (!pq.empty()) {
        State curState = pq.top();
        pq.pop();

        // 当前队首节点的ID
        int curNodeID = curState.id;
        // 当前队首节点的距离(累积权重),从start到该节点
        int curDistFromStart = curState.distFromStart;

        if (curDistFromStart > distTo[curNodeID]) {
            // 已经有一条更短的路径到达 curNode 节点了
            continue;
        }

        // 将 curNode 的相邻节点装入队列
        // 遍历对应的邻接表adj,将curNodeID对应的相邻节点装入队列中
        for (int nextNodeID : adj(curNodeID)) 
        {
            // 看看从 curNode 达到 nextNodeID 的距离是否会更短
            int distToNextNode = distTo[curNodeID] + weight(curNodeID, nextNodeID);

            // 从 curNode 达到 nextNodeID 的距离更短,更新dp table,并将nextNode入队,等待下一次出队处理
            if (distToNextNode < distTo[nextNodeID]) 
            {
                // 更新 dp table
                distTo[nextNodeID] = distToNextNode;
                // 将这个节点以及距离放入队列,等待下一次出队处理
                pq.push(State(nextNodeID, distToNextNode));
            }
        }
    }
    
    return distTo;
}

int main() {

    
    return 0;
}

1、没有 visited 集合记录已访问的节点,所以一个节点会被访问多次,会被多次加入队列,那会不会导致队列永远不为空,造成死循环

  • 算法逻辑就是在不断的最小化 distTo 数组中的元素:如果你能让到达 nextNodeID 的距离更短,那就更新 distTo[nextNodeID] 的值,让你入队,否则的话对不起,不让入队。

  • 因为两个节点之间的最短距离(路径权重)肯定是一个确定的值,不可能无限减小下去,所以队列一定会空,队列空了之后,distTo 数组中记录的就是从 start 到其他节点的最短距离。

2、为什么用优先级队列 PriorityQueue 而不是 LinkedList 实现的普通队列?为什么要按照 distFromStart 的值来排序

  • Dijkstra 算法使用优先级队列,主要是为了效率上的优化,类似一种贪心算法的思路。让你觉得哪条路径更有「潜力」成为最短路径中的一部分

3、如果我只想计算起点 start 到某一个终点 end 的最短路径,是否可以修改算法,提升一些效率

// 输入一幅图graph和一个起点 start,计算 start 到其他节点的最短距离
// 注意这里的参数,根据不同的题改变
int dijkstra(int start, vector<list<vector<int>>>& graph, int end) 
{
    ...

    while (!pq.empty()) {
        ...

        // 标准 Dijkstra 算法会算出 start 到所有其他节点的最短路径,
        // 你只想计算到 end 的最短路径,相当于减少计算量
        if(curNodeID == end) return curDistFromStart;// 在这里加一个判断就行了,其他代码不用改


        ...
    }
    // 如果运行到这里,说明从 start 无法走到 end
    return INT_MAX;
}

完整代码

743.网络延迟时间

n 个网络节点,标记为 1n

给你一个列表 times,表示信号经过 有向 边的传递时间。 times[i] = (ui, vi, wi),其中 ui 是源节点,vi 是目标节点, wi 是一个信号从源节点传递到目标节点的时间。

现在,从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1

在这里插入图片描述

#include <iostream>
#include <vector>
#include <list>
#include <queue>

using namespace std;

// 用 State 类记录一些额外信息,也就是使用 distFromStart 变量记录从起点 start 到当前这个节点的距离
// 在 Dijkstra 算法中,你第一次经过某个节点时的路径权重,不见得就是最小的,所以对于同一个节点,我们可能会经过多次,
// 而且每次的 distFromStart 可能都不一样
class State 
{
public:
    // 图节点的 id
    int id;
    // 从 start 节点到当前节点的距离
    int distFromStart;
    // 构造函数,对该对象进行初始化,记录图中某个节点对应的id和累积权重
    State(int id, int distFromStart) : id(id), distFromStart(distFromStart) {}
};

class MyComp
{
public:
    // 将累积权重小的放在堆顶
    bool operator()(State a, State b)
    {
        return a.distFromStart > b.distFromStart;
    }
};


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

    // 优先级队列,distFromStart 较小的排在前面
    priority_queue<State, vector<State>, MyComp> pq;


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

    while (!pq.empty()) {
        State curState = pq.top();
        pq.pop();

        // 当前队首节点的ID
        int curNodeID = curState.id;
        // 当前队首节点的距离(累积权重),从start到该节点
        int curDistFromStart = curState.distFromStart;

        // // 标准 Dijkstra 算法会算出 start 到所有其他节点的最短路径,
        // // 你只想计算到 end 的最短路径,相当于减少计算量
        // vector<int> dijkstra(int start, vector<list<vector<int>>>& graph, int end) 
        // if(curNodeID == end) return curDistFromStart;// 在这里加一个判断就行了,其他代码不用改


        if (curDistFromStart > distTo[curNodeID]) {
            // 已经有一条更短的路径到达 curNode 节点了
            continue;
        }

        // 将 curNode 的相邻节点装入队列
        for (vector<int> neighbor : graph[curNodeID]) 
        {
            // 看看从 curNode 达到 nextNodeID 的距离是否会更短
            // int distToNextNode = distTo[curNodeID] + weight(curNodeID, nextNodeID);
            int nextNodeID = neighbor[0];
            int distToNextNode = distTo[curNodeID] + neighbor[1];

            // 从 curNode 达到 nextNodeID 的距离更短,更新dp table,并将nextNode入队,等待下一次出队处理
            if (distToNextNode < distTo[nextNodeID]) 
            {
                // 更新 dp table
                distTo[nextNodeID] = distToNextNode;
                // 将这个节点以及距离放入队列,等待下一次出队处理
                pq.push(State(nextNodeID, distToNextNode));
            }
        }
    }
    return distTo;
}

// 计算从节点k到其它节点的最短路径(即最少累积权重)
// main函数调用入口处 这里的参数和返回值根据题意进行修改
// 参数:edges表示带权图  n表示有n个网络节点[1..n]  k表示从节点k出发到其他所有节点的最短路径
int mainProcess(vector<vector<int>>& edges, int n, int k)
{
    // 节点号从1开始,所以要创建大小为 n+1 的邻接表(大小为n+1,数组从下标0开始构建,但是我们只需要用到[1..n])
    // 根据题意修改邻接表的大小:(如果图的节点号从0开始创建大小为 n 的邻接表)
    // [1...n]
    vector<list<vector<int>>> graph(n + 1);// 邻接表初始化
    
    // 构造图(图可以用邻接表和邻接矩阵表示,这里采用邻接表)
    for(vector<int>& edge : edges)
    {
        int from = edge[0];
        int to = edge[1];
        int weight = edge[2];
        // 向第from个邻接表对应的链表上添加数组元素,邻接表存储图结构,同时存储权重信息
        // graph[from]对应list类型
        // from -> list<(to, weight)>
        graph[from].push_back(vector<int>{to, weight});
    }

    // 启动dijkstra算法计算节点k为起点到达其它节点的最短路径
    vector<int> distTo = dijkstra(k, graph);

    // 找到最长的那一条最短路径
    int res = 0;
    // 如果图的节点号从0开始,那么这里遍历就从0开始,从1开始就是[1..distTo.size())
    for (int i = 1; i < distTo.size(); i++) 
    {
        // 表示无法从start位置到达i节点,直接返回-1
        if(distTo[i] == INT_MAX) 
        {
            // 有节点不可达,返回 -1
            return -1;
        }
        res = max(res, distTo[i]);
    }
    return res;
}


int main() {

    int n = 3;
    vector<vector<int>> nums{{1, 2, 5}, {1, 3, 6}, {2, 3, 1}};
    int ret = mainProcess(nums, n, 1);
    cout << ret << endl;



    return 0;
}

1631.最小体力消耗路径

你准备参加一场远足活动。给你一个二维 rows x columns 的地图 heights ,其中 heights[row][col] 表示格子 (row, col) 的高度。一开始你在最左上角的格子 (0, 0) ,且你希望去最右下角的格子 (rows-1, columns-1) (注意下标从 0 开始编号)。你每次可以往 四个方向之一移动,你想要找到耗费 体力 最小的一条路径。

一条路径耗费的 体力值 是路径上相邻格子之间 高度差绝对值最大值 决定的。

请你返回从左上角走到右下角的最小 体力消耗值

在这里插入图片描述

#include <iostream>
#include <vector>
#include <queue>

using namespace std;

// 用 State 类记录一些额外信息
// 如果是一维邻接表,就记录成id形式,标记图中节点
// 如果是二维邻接矩阵形式的题,就声明为x,y变量,标记图中对应的节点

// 在 Dijkstra 算法中,你第一次经过某个节点时的路径权重,不见得就是最小的,所以对于同一个节点,我们可能会经过多次,
// 而且每次的 distFromStart 可能都不一样
class State 
{
public:
    // 二维坐标 (x, y) 是图中的一个节点
    int x;
    int y;

     // 从起点 (0, 0) 到当前位置的最小体力消耗(距离)
    int distFromStart;
    // 构造函数,对该对象进行初始化,记录图中某个节点对应的id和累积权重
    State(int x, int y, int distFromStart) : x(x), y(y), distFromStart(distFromStart) {}
};

class MyComp
{
public:
    // 将累积权重小的放在堆顶
    bool operator()(State a, State b)
    {
        return a.distFromStart > b.distFromStart;
    }
};

// 创建二维数组表示节点在矩阵中向上、下、左、右 这四个方向前进一步时坐标的变化
vector<vector<int>> dirs{{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// 输入节点, 返回节点在邻接矩阵中的相邻节点
vector<vector<int>> adj(vector<vector<int>>& heights, int x, int y)
{
    vector<vector<int>> neighbors;
    int m = heights.size();
    int n = heights[0].size();

    // 将该节点[x][y]向上下左右四个方向移动
    // 当前坐标pos加上坐标的改变值就得到不同方向前进一步后的坐标
    for(vector<int>& dir : dirs)
    {
        int r = x + dir[0];
        int c = y + dir[1];

        // 先判断向不同方向前进后得到的坐标是否越界,再判断对应位置的值是否符合条件
        if(r >= 0 && r < m && c >= 0 && c < n)
        {
            neighbors.push_back(vector<int>{r, c});
        }
    }
    return neighbors;
}

// Dijkstra 算法,计算 (0, 0) 到 (m - 1, n - 1) 的最小体力消耗
// Dijkstra 算法这里的参数和返回值根据题意进行修改
int dijkstra(vector<vector<int>>& heights) 
{
    int m = heights.size();
    int n = heights[0].size();

    // 记录最短路径的权重,你可以理解为 dp table
    // 定义:distTo[i][j]的值为从 (0, 0) 到 (i, j) 的最小体力消耗
    vector<vector<int>> distTo(m, vector<int>(n, INT_MAX));// 求最小值,所以 dp table 初始化为正无穷
    
    // base case,[start][start] 到 [start][start] 的最短距离就是 0
    distTo[0][0] = 0;

    // 优先级队列,distFromStart 较小的排在前面
    priority_queue<State, vector<State>, MyComp> pq;


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

    while (!pq.empty()) {
        State curState = pq.top();
        pq.pop();

        // 当前队首节点的在图中的位置
        int curX = curState.x;
        int curY = curState.y;
        // 当前队首节点的距离(累积权重),从[start][start]到该节点
        int curDistFromStart = curState.distFromStart;

        // // 标准 Dijkstra 算法会算出 start 到所有其他节点的最短路径,
        // // 你只想计算到 end 的最短路径,相当于减少计算量
        if(curX == m - 1 && curY == n - 1) return curDistFromStart;// 在这里加一个判断就行了,其他代码不用改


        if (curDistFromStart > distTo[curX][curY]) {
            // 已经有一条更短的路径到达 [curX][curY] 节点了
            continue;
        }

        // 将 [curX][curY] 节点 的相邻节点装入队列
        for (vector<int>& neighbor : adj(heights, curX, curY)) 
        {
            // 看看从 curNode 达到 nextNodeID 的距离 是否 会更短
            // int distToNextNode = distTo[curNodeID] + weight(curNodeID, nextNodeID);
            int nextNodeX = neighbor[0];
            int nextNodeY = neighbor[1];
            // 根据题意计算从 (curX, curY) 达到 (nextX, nextY) 的消耗
            int distToNextNode = max(distTo[curX][curY], abs(heights[nextNodeX][nextNodeY] - heights[curX][curY]));

            // 从 curNode 达到 nextNodeID 的距离更短,更新dp table,并将nextNode入队,等待下一次出队处理
            if (distToNextNode < distTo[nextNodeX][nextNodeY]) 
            {
                // 更新 dp table
                distTo[nextNodeX][nextNodeY] = distToNextNode;
                // 将这个节点以及距离放入队列,等待下一次出队处理
                pq.push(State(nextNodeX, nextNodeY, distToNextNode));
            }
        }
    }
    //  运行到这里,说明(0, 0) 无法到 (m - 1, n - 1),返回INT_MAX,一般情况下使用不到
    return INT_MAX;
}

// main函数调用入口处 这里的参数和返回值根据题意进行修改
int mainProcess(vector<vector<int>>& heights)
{
    // 如果权值在矩阵里,就需要创建对应图的邻接表
    // 如果直接给出邻接矩阵以及权值的定义,那么就不需要再创建邻接表
    
    // 启动dijkstra算法计算节点k为起点到达其它节点的最短路径
    int res = dijkstra(heights);

    return res;
}


int main() {
    // Dijkstra 算法,计算 (0, 0) 到 (m - 1, n - 1) 的最小体力消耗

    vector<vector<int>> nums{{1,2,2}, {3,8,2}, {5,3,5}};
    int ret = mainProcess(nums);
    cout << ret << endl;


    return 0;
}

未知.星球之间的最少消耗

存在N个星球(编号从1到N),每两个星球之间有连通门,从X星球到Y星球需要通过连通门,并且需要消耗一定的能量

  • 求连通所有星球所需要的最少能量消耗,如果无法连通所有星球,输出-1

    • 最小生成树,待补充
  • 求从星球1到达星球N所耗费的最少能量,如果无法到达星球N,输出-1

    •   // 其中 nums[i] =[Xi,Yi,Mi] 表示星球Xi和星球Yi之间可以开启一个星际门,并消耗 Mi 能量。
        vector<vector<int>> nums{{1, 2, 5}, {1, 3, 6}, {2, 3, 1}};
      
  • 求从星球1到达星球N所耗费最少能量所在的路径

#include <iostream>
#include <vector>
#include <list>
#include <queue>

using namespace std;

// 用 State 类记录一些额外信息,也就是使用 distFromStart 变量记录从起点 start 到当前这个节点的距离
// 在 Dijkstra 算法中,你第一次经过某个节点时的路径权重,不见得就是最小的,所以对于同一个节点,我们可能会经过多次,
// 而且每次的 distFromStart 可能都不一样
class State 
{
public:
    // 图节点的 id
    int id;
    // 从 start 节点到当前节点的距离
    int distFromStart;
    // 构造函数,对该对象进行初始化,记录图中某个节点对应的id和累积权重
    State(int id, int distFromStart) : id(id), distFromStart(distFromStart) {}
};

class MyComp
{
public:
    // 将累积权重小的放在堆顶
    bool operator()(State a, State b)
    {
        return a.distFromStart > b.distFromStart;
    }
};



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

    // 优先级队列,distFromStart 较小的排在前面
    priority_queue<State, vector<State>, MyComp> pq;


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

    while (!pq.empty()) {
        State curState = pq.top();
        pq.pop();

        // 当前队首节点的ID
        int curNodeID = curState.id;
        // 当前队首节点的距离(累积权重),从start到该节点
        int curDistFromStart = curState.distFromStart;

        // // 标准 Dijkstra 算法会算出 start 到所有其他节点的最短路径,
        // // 你只想计算到 end 的最短路径,相当于减少计算量
        // vector<int> dijkstra(int start, vector<list<vector<int>>>& graph, int end) 
        // if(curNodeID == end) return curDistFromStart;// 在这里加一个判断就行了,其他代码不用改


        if (curDistFromStart > distTo[curNodeID]) {
            // 已经有一条更短的路径到达 curNode 节点了
            continue;
        }

        // 将 curNode 的相邻节点装入队列
        for (vector<int> neighbor : graph[curNodeID]) 
        {
            // 看看从 curNode 达到 nextNodeID 的距离是否会更短
            // int distToNextNode = distTo[curNodeID] + weight(curNodeID, nextNodeID);
            int nextNodeID = neighbor[0];
            int distToNextNode = distTo[curNodeID] + neighbor[1];

            // 从 curNode 达到 nextNodeID 的距离更短,更新dp table,并将nextNode入队,等待下一次出队处理
            if (distToNextNode < distTo[nextNodeID]) 
            {
                // 更新 dp table
                distTo[nextNodeID] = distToNextNode;
                // 将这个节点以及距离放入队列,等待下一次出队处理
                pq.push(State(nextNodeID, distToNextNode));
            }
        }
    }
    return distTo;
}

// 计算从节点k到其它节点的最短路径(即最少累积权重)
// main函数调用入口处 这里的参数和返回值根据题意进行修改
// 参数:edges表示带权图  n表示有n个网络节点[1..n]  k表示从节点k出发到其他所有节点的最短路径
int mainProcess(vector<vector<int>>& edges, int n, int k)
{
    // 节点号从1开始,所以要创建大小为 n+1 的邻接表(大小为n+1,数组从下标0开始构建,但是我们只需要用到[1..n])
    // 根据题意修改邻接表的大小:(如果图的节点号从0开始创建大小为 n 的邻接表)
    // [1...n]
    vector<list<vector<int>>> graph(n + 1);// 邻接表初始化
    
    // 构造图(图可以用邻接表和邻接矩阵表示,这里采用邻接表)
    for(vector<int>& edge : edges)
    {
        int from = edge[0];
        int to = edge[1];
        int weight = edge[2];
        // 向第from个邻接表对应的链表上添加数组元素,邻接表存储图结构,同时存储权重信息
        // graph[from]对应list类型
        // from -> list<(to, weight)>
        graph[from].push_back(vector<int>{to, weight});
    }

    // 启动dijkstra算法计算节点k为起点到达其它节点的最短路径
    vector<int> distTo = dijkstra(k, graph);

    // 找到最长的那一条最短路径
    int res = 0;
    // 如果图的节点号从0开始,那么这里遍历就从0开始,从1开始就是[1..distTo.size())
    for (int i = 1; i < distTo.size(); i++) 
    {
        // 表示无法从start位置到达i节点,直接返回-1
        if(distTo[i] == INT_MAX) 
        {
            // 有节点不可达,返回 -1
            return -1;
        }
        res = max(res, distTo[i]);
    }
    return res;

}


int main() {

	
    vector<vector<int>> nums{{1, 2, 1}, {1, 3, 1}, {2, 3, 3}};
	// 图中星球个数
    int n = 3;
    int ret = mainProcess(nums, n, 1);
    cout << ret << endl;


    return 0;
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dijkstra算法是一种用于解决带权图的单源最短路径问题的贪心算法。它维护一个距离起点的最短路径已知的顶点集合,通过不断地扩展这个集合,最终得到从起点到所有顶点的最短路径。 Dijkstra算法的基本思想是,维护一个集合S,表示已经求出最短路径的顶点集合。一开始,S只包含起点。然后,每次从集合V-S中选取一个距离起点最近的顶点u,将其加入集合S中,并更新与u相邻的所有顶点的最短路径。 具体实现上,我们可以使用一个数组dis[]来存储每个顶点到起点的最短路径长度,数组vis[]表示该顶点是否已经被加入到集合S中。每次选取距离起点最近的顶点u后,我们遍历u的所有邻居v,并更新dis[v]的值,如果dis[v]发生了改变,我们就将v加入到一个优先队列中,等待下一次选择。 以下是Dijkstra算法的伪代码实现: ``` int n; // 顶点数 int dis[N]; // 存储起点到每个顶点的最短距离 bool vis[N]; // 标记每个顶点是否已经加入集合S中 vector<pair<int, int>> adj[N]; // 存储每个顶点的邻居 void dijkstra(int s) { // s为起点编号 memset(dis, 0x3f, sizeof dis); // 将dis数组初始化为无穷大 dis[s] = 0; // 起点到自身的距离为0 priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> q; q.push({0, s}); // 将起点加入队列中 while (!q.empty()) { auto t = q.top(); q.pop(); int u = t.second; if (vis[u]) continue; // 如果该点已经在集合S中,直接跳过 vis[u] = true; // 将u加入集合S中 for (auto [v, w] : adj[u]) { // 遍历u的所有邻居 if (dis[v] > dis[u] + w) { // 如果从u到v的距离更短 dis[v] = dis[u] + w; // 更新dis数组 q.push({dis[v], v}); // 将v加入队列中 } } } } ``` 其中,priority_queue是一个优先队列,用于存储待选顶点。我们使用了STL中的pair来表示顶点与其到起点的距离。优先队列默认按照pair的第一个元素排序,因此我们需要自定义一个比较函数,将pair按照第二个元素(距离)排序。 Dijkstra算法的时间复杂度为O(ElogV),其中E为边数,V为顶点数。在实际应用中,Dijkstra算法的效率很高,能够处理大规模的图。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值