【2023/3/12~3/16 Leetcode】图练习集锦

学习链接:

  • 图论基础及遍历算法
  • 环检测及拓扑排序算法
  • 二分图判定算法【DFS\BDS】
  • 并查集(UNION-FIND)算法
  • KRUSKAL 最小生成树算法
  • Prim 最小生成树算法
  • DIJKSTRA 算法模板及应用
  • Dijkstra算法模板讲解
  • BellmanFord和SPFA算法详解
  • Bellman-ford 算法

图的遍历框架

// 注意:cpp 代码由 chatGPT 根据我的 java 代码翻译,旨在帮助不同背景的读者理解算法逻辑。
// 本代码还未经过力扣测试,仅供参考,如有疑惑,可以参照我写的 java 代码对比查看。

// 记录被遍历过的节点
vector<bool> visited;
// 记录从起点到当前节点的路径
vector<bool> onPath;

/* 图遍历框架 */
void traverse(Graph graph, int s) {
    if (visited[s]) return;
    // 经过节点 s,标记为已遍历
    visited[s] = true;
    // 做选择:标记节点 s 在路径上
    onPath[s] = true;
    for (int neighbor : graph.neighbors(s)) {
        traverse(graph, neighbor);
    }
    // 撤销选择:节点 s 离开路径
    onPath[s] = false;
}

1.克隆图

题目来源:133. 克隆图
题解:

/*
// Definition for a Node.
class Node {
public:
    int val;
    vector neighbors;
    Node() {
        val = 0;
        neighbors = vector();
    }
    Node(int _val) {
        val = _val;
        neighbors = vector();
    }
    Node(int _val, vector _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
};
*/

class Solution {
public:
    unordered_map<Node*, Node*> visited;
    Node* cloneGraph(Node* node) {
        if(node==nullptr) return node;
        if(visited.find(node)!=visited.end()){
            return visited[node];
        }
        Node* clonenode=new Node(node->val);
        visited[node]=clonenode;
        for(auto& neighbor:node->neighbors)
        {
            clonenode->neighbors.emplace_back(cloneGraph(neighbor));
        }
        return clonenode;
    }
};

2.收集树上所有苹果的最少时间

题目来源:1443. 收集树上所有苹果的最少时间
题解:

class Solution {
public:

    //g 图
    //vis 判断节点是否被访问过,遍历过的节点不能再遍历,
    //同时因为是无向图,vis间接的确定了父子节点的关系,在子节点访问其子节点的时候不可能再去访问父节点
    //cost 只有以0节点刚进去的时候cost=0,在之后访问0节点的子节点时,cost都等于2(来回)

    int dfs(int curn, vector<vector<int> >& g, vector<bool>& vis, vector<bool>& hasApple, int cost)
    {
        if(vis[curn])
            return 0;
        vis[curn]=true;
        int childcost=0;
        for(auto next:g[curn])
        {
            childcost+=dfs(next,g,vis,hasApple,2);//遍历当前节点的所有子节点
            //如果childcost=0的话代表所有的子节点都没有苹果
        }

        //对应上面的情况1,所有子节点里都无苹果,且该节点本身也无苹果,走到该节点的开销=0
        if(!hasApple[curn] && childcost==0)
            return 0;

        //对应上面的情况2,3 走到该节点的开销为所有摘子节点里的苹果的开销+走到该节点的开销cost
        //如果childcost=0的话,对应情况3
        //如果childcost!=0的话,对应情况2
        return childcost+cost;
    }


    int minTime(int n, vector<vector<int>>& edges, vector<bool>& hasApple) {
        vector<vector<int>> g(n);
        vector<bool> vis(n,false);

        for(auto edge:edges)
        {
            g[edge[0]].push_back(edge[1]);
            g[edge[1]].push_back(edge[0]);
        }

        return dfs(0,g,vis,hasApple,0);//第一层由0节点出发,开销是0
    }

};

环检测[DFS]

题解:

// 注意:cpp 代码由 chatGPT 根据我的 java 代码翻译,旨在帮助不同背景的读者理解算法逻辑。
// 本代码还未经过力扣测试,仅供参考,如有疑惑,可以参照我写的 java 代码对比查看。

class Solution {
public:
    // 记录一次递归堆栈中的节点
    vector<bool> onPath;
    // 记录遍历过的节点,防止走回头路
    vector<bool> visited;
    // 记录图中是否有环
    bool hasCycle = false;

    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int>> graph = buildGraph(numCourses, prerequisites);

        visited = vector<bool>(numCourses);
        onPath = vector<bool>(numCourses);

        for (int i = 0; i < numCourses; i++) {
            // 遍历图中的所有节点
            traverse(graph, i);
        }
        // 只要没有循环依赖可以完成所有课程
        return !hasCycle;
    }

    void traverse(vector<vector<int>>& graph, int s) {
        if (onPath[s]) {
            // 出现环
            hasCycle = true;
        }

        if (visited[s] || hasCycle) {
            // 如果已经找到了环,也不用再遍历了
            return;
        }
        // 前序代码位置
        visited[s] = true;
        onPath[s] = true;
        for (int t : graph[s]) {
            traverse(graph, t);
        }
        // 后序代码位置
        onPath[s] = false;
    }

    vector<vector<int>> buildGraph(int numCourses, vector<vector<int>>& prerequisites) {
        // 代码见前文
    }
};

环检测[BFS]

题解:

// 注意:cpp 代码由 chatGPT 根据我的 java 代码翻译,旨在帮助不同背景的读者理解算法逻辑。
// 本代码还未经过力扣测试,仅供参考,如有疑惑,可以参照我写的 java 代码对比查看。

#include
#include

using namespace std;

// 主函数
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
    // 建图,有向边代表「被依赖」关系
    vector<vector<int>> graph = buildGraph(numCourses, prerequisites);
    // 构建入度数组
    vector<int> indegree(numCourses, 0);
    for (auto edge : prerequisites) {
        int from = edge[1], to = edge[0];
        // 节点 to 的入度加一
        indegree[to]++;
    }

    // 根据入度初始化队列中的节点
    queue<int> q;
    for (int i = 0; i < numCourses; i++) {
        if (indegree[i] == 0) {
            // 节点 i 没有入度,即没有依赖的节点
            // 可以作为拓扑排序的起点,加入队列
            q.push(i);
        }
    }

    // 记录遍历的节点个数
    int count = 0;
    // 开始执行 BFS 循环
    while (!q.empty()) {
        // 弹出节点 cur,并将它指向的节点的入度减一
        int cur = q.front();
        q.pop();
        count++;
        for (int next : graph[cur]) {
            indegree[next]--;
            if (indegree[next] == 0) {
                // 如果入度变为 0,说明 next 依赖的节点都已被遍历
                q.push(next);
            }
        }
    }

    // 如果所有节点都被遍历过,说明不成环
    return count == numCourses;
}


// 建图函数
vector<vector<int>> buildGraph(int n, vector<vector<int>>& edges) {
    // 见前文
}

拓扑排序[DFS]

题解:

// 注意:cpp 代码由 chatGPT 根据我的 java 代码翻译,旨在帮助不同背景的读者理解算法逻辑。
// 本代码还未经过力扣测试,仅供参考,如有疑惑,可以参照我写的 java 代码对比查看。

class Solution {
public:
    // 记录后序遍历结果
    vector<int> postorder;
    // 记录是否存在环
    bool hasCycle = false;
    vector<bool> visited, onPath;

    // 主函数
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int>> graph = buildGraph(numCourses, prerequisites);
        visited = vector<bool>(numCourses, false);
        onPath = vector<bool>(numCourses, false);
        // 遍历图
        for (int i = 0; i < numCourses; i++) {
            traverse(graph, i);
        }
        // 有环图无法进行拓扑排序
        if (hasCycle) {
            return {};
        }
        // 逆后序遍历结果即为拓扑排序结果
        reverse(postorder.begin(), postorder.end());
        vector<int> res(numCourses);
        for (int i = 0; i < numCourses; i++) {
            res[i] = postorder[i];
        }
        return res;
    }

    // 图遍历函数
    void traverse(vector<vector<int>>& graph, int s) {
        if (onPath[s]) {
            // 发现环
            hasCycle = true;
        }
        if (visited[s] || hasCycle) {
            return;
        }
        // 前序遍历位置
        onPath[s] = true;
        visited[s] = true;
        for (int t : graph[s]) {
            traverse(graph, t);
        }
        // 后序遍历位置
        postorder.push_back(s);
        onPath[s] = false;
    }

    // 建图函数
    vector<vector<int>> buildGraph(int numCourses, vector<vector<int>>& prerequisites) {
        // 代码见前文。
    }
};

拓扑排序[BFS]

题解:

// 注意:cpp 代码由 chatGPT 根据我的 java 代码翻译,旨在帮助不同背景的读者理解算法逻辑。
// 本代码还未经过力扣测试,仅供参考,如有疑惑,可以参照我写的 java 代码对比查看。

vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
    // 建图,和环检测算法相同
    vector<vector<int>> graph(numCourses);
    for (auto edge : prerequisites) {
        int from = edge[1], to = edge[0];
        graph[from].push_back(to);
    }
    // 计算入度,和环检测算法相同
    vector<int> indegree(numCourses, 0);
    for (auto edge : prerequisites) {
        int from = edge[1], to = edge[0];
        indegree[to]++;
    }

    // 根据入度初始化队列中的节点,和环检测算法相同
    queue<int> q;
    for (int i = 0; i < numCourses; i++) {
        if (indegree[i] == 0) {
            q.push(i);
        }
    }

    // 记录拓扑排序结果
    vector<int> res;
    // 开始执行 BFS 算法
    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        // 弹出节点的顺序即为拓扑排序结果
        res.push_back(cur);
        for (int next : graph[cur]) {
            indegree[next]--;
            if (indegree[next] == 0) {
                q.push(next);
            }
        }
    }

    if (res.size() != numCourses) {
        // 存在环,拓扑排序不存在
        return vector<int>{};
    }
    
    return res;
}

// 建图函数
vector<vector<int>> buildGraph(int n, vector<vector<int>>& edges) {
    // 见前文
}

3.被围绕的区域

题目来源:130.被围绕的区域
题解:

class Solution {
public:
    int n, m;

    void dfs(vector<vector<char>>& board, int x, int y) {
        if (x < 0 || x >= n || y < 0 || y >= m || board[x][y] != 'O') {
            return;
        }
        board[x][y] = 'A';
        dfs(board, x + 1, y);
        dfs(board, x - 1, y);
        dfs(board, x, y + 1);
        dfs(board, x, y - 1);
    }

    void solve(vector<vector<char>>& board) {
        n = board.size();
        if (n == 0) {
            return;
        }
        m = board[0].size();
        for (int i = 0; i < n; i++) {
            dfs(board, i, 0);
            dfs(board, i, m - 1);
        }
        for (int i = 1; i < m - 1; i++) {
            dfs(board, 0, i);
            dfs(board, n - 1, i);
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (board[i][j] == 'A') {
                    board[i][j] = 'O';
                } else if (board[i][j] == 'O') {
                    board[i][j] = 'X';
                }
            }
        }
    }
};

4.验证二叉树

题目来源:验证二叉树
题解:

class Solution {
public:
    bool validateBinaryTreeNodes(int n, vector<int>& leftChild, vector<int>& rightChild) {
        vector<int> indeg(n);
        for(int i=0;i<n;i++){
            if(leftChild[i]!=-1)
            {
                indeg[leftChild[i]]++;
            }
            if(rightChild[i]!=-1){
                indeg[rightChild[i]]++;
            }
        }
        int root=-1;
        for(int i=0;i<n;i++){
            if(!indeg[i]){
                root=i;
                break;
            }
        }
        if(root==-1)
            return false;
        vector<bool> visited(n,false);
        queue<int> q;
        visited[root]=true;
        q.push(root);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            if(leftChild[u]!=-1){
                if(visited[leftChild[u]])
                    return false;
                visited[leftChild[u]]=true;
                q.push(leftChild[u]);
            }
            if(rightChild[u]!=-1){
                if(visited[rightChild[u]])
                    return false;
                visited[rightChild[u]]=true;
                q.push(rightChild[u]);
            }
        }
        int cnt=0;
        for(int i=0;i<n;i++){
            if(visited[i]==true)
                cnt++;
        }
        if(cnt==n)
            return true;
        else 
            return false;
    }
};

5.岛屿数量

题目来源:200.岛屿数量
题解:

class Solution {
public:
    bool validateBinaryTreeNodes(int n, vector<int>& leftChild, vector<int>& rightChild) {
        vector<int> indeg(n);
        for(int i=0;i<n;i++){
            if(leftChild[i]!=-1)
            {
                indeg[leftChild[i]]++;
            }
            if(rightChild[i]!=-1){
                indeg[rightChild[i]]++;
            }
        }
        int root=-1;
        for(int i=0;i<n;i++){
            if(!indeg[i]){
                root=i;
                break;
            }
        }
        if(root==-1)
            return false;
        vector<bool> visited(n,false);
        queue<int> q;
        visited[root]=true;
        q.push(root);
        while(!q.empty()){
            int u=q.front();
            q.pop();
            if(leftChild[u]!=-1){
                if(visited[leftChild[u]])
                    return false;
                visited[leftChild[u]]=true;
                q.push(leftChild[u]);
            }
            if(rightChild[u]!=-1){
                if(visited[rightChild[u]])
                    return false;
                visited[rightChild[u]]=true;
                q.push(rightChild[u]);
            }
        }
        int cnt=0;
        for(int i=0;i<n;i++){
            if(visited[i]==true)
                cnt++;
        }
        if(cnt==n)
            return true;
        else 
            return false;
    }
};

6.移除最多的同行或同列石头

题目来源:947. 移除最多的同行或同列石头
题解:

// 并查集特殊模板2
class UnionFind{
    // 本题因为是按行列进行查找,优化可以直接采用哈希表
public:
    // 哈希表映射关系[key,parent]
    unordered_map<int,int> parent;
    // 总共有多少不连通的并查集
    int count = 0;
    // 并查集查找首领节点操作
    int Find(int index){
        // 一开始构建并查集时候,假如key值不在并查集中则构建哈希表映射,count++
        if(parent.find(index) == parent.end()){
            parent[index] = index;
            count++;
        }
        // 查找并查集的首领节点并优化
        if(parent[index] != index)
            parent[index] = Find(parent[index]);
        return parent[index];
    }
    // 并查集合并操作
    void Uniod(int index1,int index2){
        int parent1 = Find(index1);
        int parent2 = Find(index2);
        // *这步骤很重要,直接把两节点首领一样的结果返回过滤,否则会让count多减1*
        if(parent1 == parent2)
            return;
        parent[parent1] = parent2;
        count--;
    }
};
class Solution {
public:
    // 思路:将石头行列的数值构建并查集,因此行或列需要加10001区分开
    // 合并的意思所有横坐标为 x 的石头和所有纵坐标为 y 的石头都属于同一个并查集
    // 最后返回石头数量 - 并查集的数量,就是题目要求的最多
    int removeStones(vector<vector<int>>& stones) {
        UnionFind uf;
        for(auto& stone:stones){
            uf.Uniod(stone[0] + 10001,stone[1]);
        }
        return stones.size() - uf.count;
    }
};

7.最大整除子集

题目来源:368. 最大整除子集
题解:

//法一:动态规划【没懂】
class Solution {
public:
    vector<int> largestDivisibleSubset(vector<int>& nums) {
        int len = nums.size();
        sort(nums.begin(), nums.end());

        // 第 1 步:动态规划找出最大子集的个数、最大子集中的最大整数
        vector<int> dp(len, 1);
        int maxSize = 1;
        int maxVal = dp[0];
        for (int i = 1; i < len; i++) {
            for (int j = 0; j < i; j++) {
                // 题目中说「没有重复元素」很重要
                if (nums[i] % nums[j] == 0) {
                    dp[i] = max(dp[i], dp[j] + 1);
                }
            }

            if (dp[i] > maxSize) {
                maxSize = dp[i];
                maxVal = nums[i];
            }
        }

        // 第 2 步:倒推获得最大子集
        vector<int> res;
        if (maxSize == 1) {
            res.push_back(nums[0]);
            return res;
        }

        for (int i = len - 1; i >= 0 && maxSize > 0; i--) {
            if (dp[i] == maxSize && maxVal % nums[i] == 0) {
                res.push_back(nums[i]);
                maxVal = nums[i];
                maxSize--;
            }
        }
        return res;
    }
};
//法二:并查集
class Solution {
public:
    vector<int> largestDivisibleSubset(vector<int>& nums) {
        int n = nums.size();
        if(n == 0) return {};
        vector<int> father(n), level(n);
        sort(nums.begin(), nums.end());
        for(int i = 0; i < n; ++i) father[i] = i; // 初始化父亲节点
        for(int i = 0; i < n; ++i){
            for(int j = i - 1; j >= 0; --j){
                if(nums[i] % nums[j] == 0 && level[i] < =level[j]){
                    father[i] = j;
                    level[i] = level[j] + 1;
                }
            }
        }
        // 取得最深元素的角标
        int x = max_element(level.begin(), level.end()) - level.begin();
        vector<int> ans = {nums[x]};
        while(x != father[x]){
            x = father[x];
            ans.push_back(nums[x]);
        }
        return ans; // 此处输出的数组元素是从大到小的顺序,不再排序也能过此题
        // return {ans.rbegin(), ans.rend()}; // 按从小到大的顺序返回稍微慢一点
    }
};

并查集难题:765.情侣牵手

//贪心算法题解
class Solution {
public:
    int minSwapsCouples(vector<int>& row) {
        int n=row.size();
        vector<int> index(n);
        for(int i=0;i<n;i++)
            index[row[i]]=i;
        int count=0;
        for(int i=0;i<n-1;i+=2){
            int p1=row[i];
            int p2=(p1&1)==0?p1+1:p1-1;
            if(row[i+1]==p2) continue;
            int p2index=index[p2];
            swap(row,index,i+1,p2index);
            count++;
        }
        return count;
    }
    void swap(vector<int>& row,vector<int>& index,int i,int j){
        int temp=row[i];
        row[i]=row[j];
        row[j]=temp;
        index[row[i]]=i;
        index[row[j]]=j;
    }
};

8. 连接所有点的最小费用

题目来源:1584. 连接所有点的最小费用
题解:

//方法一:Kruskal 算法
class DisjointSetUnion {
private:
    vector<int> f, rank;
    int n;

public:
    DisjointSetUnion(int _n) {
        n = _n;
        rank.resize(n, 1);
        f.resize(n);
        for (int i = 0; i < n; i++) {
            f[i] = i;
        }
    }

    int find(int x) {
        return f[x] == x ? x : f[x] = find(f[x]);
    }

    int unionSet(int x, int y) {
        int fx = find(x), fy = find(y);
        if (fx == fy) {
            return false;
        }
        if (rank[fx] < rank[fy]) {
            swap(fx, fy);
        }
        rank[fx] += rank[fy];
        f[fy] = fx;
        return true;
    }
};

struct Edge {
    int len, x, y;
    Edge(int len, int x, int y) : len(len), x(x), y(y) {
    }
};

class Solution {
public:
    int minCostConnectPoints(vector<vector<int>>& points) {
        auto dist = [&](int x, int y) -> int {
            return abs(points[x][0] - points[y][0]) + abs(points[x][1] - points[y][1]);
        };
        int n = points.size();
        DisjointSetUnion dsu(n);
        vector<Edge> edges;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                edges.emplace_back(dist(i, j), i, j);
            }
        }
        sort(edges.begin(), edges.end(), [](Edge a, Edge b) -> int { return a.len < b.len; });
        int ret = 0, num = 1;
        for (auto& [len, x, y] : edges) {
            if (dsu.unionSet(x, y)) {
                ret += len;
                num++;
                if (num == n) {
                    break;
                }
            }
        }
        return ret;
    }
};

//方法二:Prim
class Solution {
public:

    int dist(vector<int> a, vector<int> b)
    {
        return abs(a[0]-b[0])+abs(a[1]-b[1]);
    }

    int minCostConnectPoints(vector<vector<int>>& points) {
        int n = points.size();
        vector<bool> visited(n, false);
        visited[0] = true;
        vector<int> mindist(n, 0);
        for(int i = 1; i < n; i++)
        {
            mindist[i] = dist(points[0], points[i]);
        }
        mindist[0] = 0;
        int cost = 0;
        for(int k = 0; k < n-1; k++)
        {
            int pos = 0;
            int minn = INT_MAX;
            for(int i = 0; i < n; i++)
            {
                if(!visited[i] && minn > mindist[i])
                {
                    minn = mindist[i];
                    pos = i;
                }
            }
            cost += minn;
            visited[pos] = true;
            mindist[pos] = 0;
            for(int i = 0; i < n; i++)
            {
                if(!visited[i] && dist(points[pos], points[i]) < mindist[i])
                    mindist[i] = dist(points[pos], points[i]);
            }
        }
        return cost;
    }
};

9. 网络延迟时间【Dijkstra】

题目来源:743. 网络延迟时间
题解:

class Solution {
public:
    int networkDelayTime(vector<vector<int>> &times, int n, int k) {
        int inf = INT_MAX/2;
        vector<vector<int>> g(n, vector<int>(n, inf));
        for (auto &t : times) {
            int x = t[0] - 1, y = t[1] - 1;
            g[x][y] = t[2];
        }

        vector<int> dist(n, inf);
        dist[k - 1] = 0;
        vector<int> used(n);
        for (int i = 0; i < n; ++i) {
            int mindist = inf;
            int index=-1;
            for (int y = 0; y < n; ++y) {
                if (!used[y] &&  dist[y] < mindist) {
                    mindist=dist[y];
                    index=y;
                }
            }
            if(index!=-1){
            used[index] = true;
            for (int y = 0; y < n; ++y) {
                dist[y] = min(dist[y], dist[index] + g[index][y]);
                }
            }
        }

        int ans = *max_element(dist.begin(), dist.end());
        return ans == inf ? -1 : ans;
    }
};

  • //升序队列
    priority_queue q;
    //降序队列
    priority_queue q;
//小根堆
class Solution {
public:
    int networkDelayTime(vector<vector<int>> &times, int n, int k) {
        const int inf = INT_MAX / 2;
        vector<vector<pair<int, int>>> g(n);
        for (auto &t : times) {
            int x = t[0] - 1, y = t[1] - 1;
            g[x].emplace_back(y, t[2]);
        }

        vector<int> dist(n, inf);
        dist[k - 1] = 0;
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> q;
        q.emplace(0, k - 1);
        while (!q.empty()) {
            auto p = q.top();
            q.pop();
            int time = p.first, x = p.second;
            if (dist[x] < time) {
                continue;
            }
            for (auto &e : g[x]) {
                int y = e.first, d = dist[x] + e.second;
                if (d < dist[y]) {
                    dist[y] = d;
                    q.emplace(d, y);
                }
            }
        }

        int ans = *max_element(dist.begin(), dist.end());
        return ans == inf ? -1 : ans;
    }
};

10.概率最大的路径

题目来源:1514. 概率最大的路径
题解:

class Solution {
public:
    double maxProbability(int n, vector<vector<int>>& edges, vector<double>& succProb, int start, int end) {
        vector<vector<pair<double, int>>> graph(n);
        for (int i = 0; i < edges.size(); i++) {
            auto& e = edges[i];
            graph[e[0]].emplace_back(succProb[i], e[1]);
            graph[e[1]].emplace_back(succProb[i], e[0]);
        }

        priority_queue<pair<double, int>> que;
        vector<double> prob(n, 0);

        que.emplace(1, start);
        prob[start] = 1;
        while (!que.empty()) {
            auto [pr, node] = que.top();
            que.pop();
            if (pr < prob[node]) {
                continue;
            }
            for (auto& [prNext, nodeNext] : graph[node]) {
                if (prob[nodeNext] < prob[node] * prNext) {
                    prob[nodeNext] = prob[node] * prNext;
                    que.emplace(prob[nodeNext], nodeNext);
                }
            }
        }
        return prob[end];
    }
};

11.最小体力消耗路径

题目来源:1631. 最小体力消耗路径
题解:

class Solution {
public:
    struct State {
    int x, y, effortFromStart;
    bool operator>(const State &other) const {
        return effortFromStart > other.effortFromStart;
    }
};

// 计算从 (curX, curY) 出发可以到达哪些位置,返回“邻居”坐标列表
vector<vector<int>> adj(const vector<vector<int>>& heights, int curX, int curY) {
    int m = heights.size(), n = heights[0].size();
    vector<vector<int>> ret;
    if (curX > 0) {
        ret.push_back({curX - 1, curY});
    }
    if (curY > 0) {
        ret.push_back({curX, curY - 1});
    }
    if (curX < m - 1) {
        ret.push_back({curX + 1, curY});
    }
    if (curY < n - 1) {
        ret.push_back({curX, curY + 1});
    }
    return ret;
}
    int minimumEffortPath(vector<vector<int>>& heights) {
        int m = heights.size(), n = heights[0].size();
    // 定义:从 (0, 0) 到 (i, j) 的最小体力消耗是 effortTo[i][j]
    vector<vector<int>> effortTo(m, vector<int>(n));
    // dp table 初始化为正无穷
    for (int i = 0; i < m; i++) {
        fill(effortTo[i].begin(), effortTo[i].end(), INT_MAX);
    }
    // base case,起点到起点的最小消耗就是 0
    effortTo[0][0] = 0;

    // 优先级队列,effortFromStart 较小的排在前面
    priority_queue<State, vector<State>, greater<State>> pq;
    
    // 从起点 (0, 0) 开始进行 BFS
    pq.emplace(State{0, 0, 0});

    while (!pq.empty()) {
        State curState = pq.top();
        pq.pop();
        int curX = curState.x;
        int curY = curState.y;
        int curEffortFromStart = curState.effortFromStart;
        
        // 到达终点提前结束
        if (curX == m - 1 && curY == n - 1) {
            return curEffortFromStart;
        }
        
        if (curEffortFromStart > effortTo[curX][curY]) {
            continue;
        }
        // 将 (curX, curY) 的相邻坐标装入队列
        for (vector<int> &neighbor : adj(heights, curX, curY)) {
            int nextX = neighbor[0];
            int nextY = neighbor[1];
            // 计算从 (curX, curY) 达到 (nextX, nextY) 的消耗
            int effortToNextNode = max(
                effortTo[curX][curY], 
                abs(heights[curX][curY] - heights[nextX][nextY])
            );
            // 更新 dp table
            if (effortTo[nextX][nextY] > effortToNextNode) {
                effortTo[nextX][nextY] = effortToNextNode;
                pq.emplace(State{nextX, nextY, effortToNextNode});
            }
        }
    }
    // 正常情况不会达到这个 return
    return -1;
    }
};

你可能感兴趣的:(leetcode,leetcode,算法,深度优先,c++,力扣)