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

文章介绍了图论中的基本算法,包括深度优先搜索(DFS)和广度优先搜索(BFS)的图遍历、环检测、拓扑排序以及克隆图问题。此外,还讨论了最小生成树的Kruskal和Prim算法,以及如何解决收集树上所有苹果的最少时间和验证二叉树的问题。
摘要由CSDN通过智能技术生成

学习链接:

图的遍历框架

// 注意: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<Node*> neighbors;
    Node() {
        val = 0;
        neighbors = vector<Node*>();
    }
    Node(int _val) {
        val = _val;
        neighbors = vector<Node*>();
    }
    Node(int _val, vector<Node*> _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<vector>
#include<queue>

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 {
   };
        }
        // 逆后序遍历结果即为拓扑排序结果
        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值