每日算法刷题Day78:10.23:leetcode 一般树7道题,用时1h30min

1.建立二叉搜索树:

(1)父子关系表

输入格式:

复制代码
5
0 2    (节点0是根,子节点是2)
2 1    (节点2子节点是1)
2 3    (节点2子节点是3)
1 4    (节点1子节点是4)
3 5    (节点3子节点是5)

建立一般树:

复制代码
vector<vector<int>> g; // 邻接表
// 带权值
// typedef pair<int,int> PII;
// vector<vector<PII>> g;
void bulidTree(){
	int n;
	cin>>n;
	for(int i=0;i<n;++i){
		int a,b;
		cin>>a>>b;
		g[a].push_back(b);
		// g[b].push_back(a); // 无向树
	}
}

DFS遍历二叉树(需传递父节点,防止回头,树无回路,所以无需vis数组):

复制代码
void dfs(int cur,int fa){
	for(auto& nxt:g[cur]){
		if(nxt==fa) continue;
		dfs(nxt,cur);
	}
}

一、遍历

1.套路
2.题目描述
3.学习经验
1. 2368.受限条件下可到达节点的数目(中等,学习优化思想)

2368. 受限条件下可到达节点的数目 - 力扣(LeetCode)

思想

1.现有一棵由 n 个节点组成的无向树,节点编号从 0n - 1 ,共有 n - 1 条边。

给你一个二维整数数组 edges ,长度为 n - 1 ,其中 edges[i] = [ai, bi] 表示树中节点 aibi 之间存在一条边。另给你一个整数数组 restricted 表示 受限 节点。

在不访问受限节点的前提下,返回你可以从节点 0 到达的 最多 节点数目_。_

注意,节点 0 会标记为受限节点。

2.我的思路是在dfs遍历时判断是否受限,但这样会增加遍历次数

3.优化思想是在建树时就判断是否受限,若两端都不受限才建立边

4.然后节点数目可以不用vis数组,而是递归返回

代码
复制代码
class Solution {
public:
    vector<vector<int>> g;
    int res = 0;
    set<int> st;
    vector<bool> vis;
    void dfs(int cur, int fa) {
        if (!vis[cur]) {
            ++res;
            vis[cur] = true;
        }
        for (auto& nxt : g[cur]) {
            if (nxt == fa || st.count(nxt))
                continue;
            dfs(nxt, cur);
        }
    }
    int reachableNodes(int n, vector<vector<int>>& edges,
                       vector<int>& restricted) {
        g.resize(n);
        for (auto& e : edges) {
            int a = e[0], b = e[1];
            g[a].push_back(b);
            g[b].push_back(a);
        }
        for (int& x : restricted)
            st.insert(x);
        vis.assign(n, false);
        dfs(0, -1);
        return res;
    }
};

优化代码:

复制代码
class Solution {
public:
    vector<vector<int>> g;
    int res = 0;
    set<int> st;
    vector<bool> vis;
    int dfs(int cur, int fa) {
        int cnt=1;
        for (auto& nxt : g[cur]) {
            if (nxt == fa)
                continue;
            cnt+=dfs(nxt, cur);
        }
        return cnt;
    }
    int reachableNodes(int n, vector<vector<int>>& edges,
                       vector<int>& restricted) {
        g.resize(n);
        for (int& x : restricted)
            st.insert(x);
        for (auto& e : edges) {
            int a = e[0], b = e[1];
            if(!st.count(a) && !st.count(b)){ // 建树时判断
                g[a].push_back(b);
                g[b].push_back(a);
            }
        }
        return dfs(0, -1);
    }
};
2. 1466. 重新规划路线(中等)

1466. 重新规划路线 - 力扣(LeetCode)

思想

1.n 座城市,从 0n-1 编号,其间共有 n-1 条路线。因此,要想在两座不同城市之间旅行只有唯一一条路线可供选择(路线网形成一颗树)。去年,交通运输部决定重新规划路线,以改变交通拥堵的状况。

路线用 connections 表示,其中 connections[i] = [a, b] 表示从城市 ab 的一条有向路线。

今年,城市 0 将会举办一场大型比赛,很多游客都想前往城市 0 。

请你帮助重新规划路线方向,使每个城市都可以访问城市 0 。返回需要变更方向的最小路线数。

题目数据 保证 每个城市在重新规划路线方向后都能到达城市 0 。

代码
复制代码
class Solution {
public:
    vector<vector<int>> gIn, gOut;
    int res = 0;
    void dfs(int cur, int fa) {
        for (auto& nxt : gOut[cur]) {
            if (nxt == fa)
                continue;
            ++res;
            dfs(nxt, cur);
        }
        for (auto& nxt : gIn[cur]) {
            if (nxt == fa)
                continue;
            dfs(nxt, cur);
        }
    }
    int minReorder(int n, vector<vector<int>>& connections) {
        gIn.resize(n);
        gOut.resize(n);
        for (auto& con : connections) {
            int a = con[0], b = con[1];
            gOut[a].push_back(b);
            gIn[b].push_back(a);
        }
        dfs(0, -1);
        return res;
    }
};

二、自顶向下DFS

1.套路
2.题目描述
3.学习经验
1. 1376. 通知所有员工所需的时间(中等)

1376. 通知所有员工所需的时间 - 力扣(LeetCode)

思想

1.公司里有 n 名员工,每个员工的 ID 都是独一无二的,编号从 0n - 1。公司的总负责人通过 headID 进行标识。

manager 数组中,每个员工都有一个直属负责人,其中 manager[i] 是第 i 名员工的直属负责人。对于总负责人,manager[headID] = -1。题目保证从属关系可以用树结构显示。

公司总负责人想要向公司所有员工通告一条紧急消息。他将会首先通知他的直属下属们,然后由这些下属通知他们的下属,直到所有的员工都得知这条紧急消息。

i 名员工需要 informTime[i] 分钟来通知它的所有直属下属(也就是说在 informTime[i] 分钟后,他的所有直属下属都可以开始传播这一消息)。

返回通知所有员工这一紧急消息所需要的 分钟数

代码
复制代码
class Solution {
public:
    vector<vector<int>> g;
    int res = 0;
    void dfs(int cur, int fa, int sumTime, vector<int>& informTime) {
        res = max(res, sumTime);
        sumTime += informTime[cur];
        for (auto& nxt : g[cur]) {
            if (nxt == fa)
                continue;
            dfs(nxt, cur, sumTime, informTime);
        }
    }
    int numOfMinutes(int n, int headID, vector<int>& manager,
                     vector<int>& informTime) {
        g.resize(n);
        for (int i = 0; i < n; ++i) {
            // ma[i]->i
            if (manager[i] != -1)
                g[manager[i]].push_back(i);
        }
        dfs(headID, -1, 0, informTime);
        return res;
    }
};
2. 3528. 单位转换I(中等)

3528. 单位转换 I - 力扣(LeetCode)

思想

1.有 n 种单位,编号从 0n - 1。给你一个二维整数数组 conversions,长度为 n - 1,其中 conversions[i] = [sourceUniti, targetUniti, conversionFactori] ,表示一个 sourceUniti 类型的单位等于 conversionFactoritargetUniti 类型的单位。

请你返回一个长度为 n 的数组 baseUnitConversion,其中 baseUnitConversion[i] 表示 一个 0 类型单位等于多少个 i 类型单位。由于结果可能很大,请返回每个 baseUnitConversion[i]109 + 7 取模后的值。

代码
复制代码
class Solution {
public:
    typedef pair<int, int> PII;
    vector<vector<PII>> g;
    vector<int> res;
    int n;
    const int mod = 1e9 + 7;
    typedef long long ll;
    void dfs(int cur, int fa, ll ji) {
        res[cur] = ji % mod;
        for (auto& nxt : g[cur]) {
            int nxtTar = nxt.first, nxtFac = nxt.second;
            if (nxtTar == fa)
                continue;
            dfs(nxtTar, cur, ji * nxtFac % mod);
        }
    }
    vector<int> baseUnitConversions(vector<vector<int>>& conversions) {
        n = conversions.size() + 1;
        g.resize(n);
        res.resize(n);
        for (auto& con : conversions) {
            int sou = con[0], tar = con[1], fac = con[2];
            g[sou].push_back({tar, fac});
        }
        dfs(0, -1, 1);
        return res;
    }
};
3. 1443. 收集树上所有苹果的最少时间(中等)

1443. 收集树上所有苹果的最少时间 - 力扣(LeetCode)

思想

1.给你一棵有 n 个节点的无向树,节点编号为 0n-1 ,它们中有一些节点有苹果。通过树上的一条边,需要花费 1 秒钟。你从 节点 0 出发,请你返回最少需要多少秒,可以收集到所有苹果,并回到节点 0 。

无向树的边由 edges 给出,其中 edges[i] = [fromi, toi] ,表示有一条边连接 fromtoi 。除此以外,还有一个布尔数组 hasApple ,其中 hasApple[i] = true 代表节点 i 有一个苹果,否则,节点 i 没有苹果。

代码
复制代码
class Solution {
public:
    vector<vector<int>> g;
    typedef pair<int, bool> PIO;
    PIO dfs(int cur,int fa, vector<bool>& hasApple) {
        int cnt = 0;
        bool isA = false;
        for (auto& nxt : g[cur]) {
            if(nxt==fa) continue;
            auto tmp = dfs(nxt,cur, hasApple);
            if (tmp.second) {
                cnt += tmp.first + 2;
                isA = true;
            }
        }
        if (hasApple[cur])
            isA = true;
        return {cnt, isA};
    }
    int minTime(int n, vector<vector<int>>& edges, vector<bool>& hasApple) {
        g.resize(n);
        for (auto& e : edges) {
            int a = e[0], b = e[1];
            g[a].push_back(b);
            g[b].push_back(a);
        }
        auto tmp = dfs(0,-1, hasApple);
        return tmp.first;
    }
};

三、自底向上DFS

1.套路
2.题目描述
3.学习经验
1. 690. 员工的重要性(中等)

690. 员工的重要性 - 力扣(LeetCode)

思想

1.你有一个保存员工信息的数据结构,它包含了员工唯一的 id ,重要度和直系下属的 id 。

给定一个员工数组 employees,其中:

  • employees[i].id 是第 i 个员工的 ID。

  • employees[i].importance 是第 i 个员工的重要度。

  • employees[i].subordinates 是第 i 名员工的直接下属的 ID 列表。
    给定一个整数 id 表示一个员工的 ID,返回这个员工和他所有下属的重要度的 总和
    2.注意:

    auto emp = mp[id];

这里如果id不存在,就会尝试插入一个默认构造的值,所以结构体得有默认构造,不能只有显式构造

代码
复制代码
/*
// Definition for Employee.
class Employee {
public:
    int id;
    int importance;
    vector<int> subordinates;
};
*/

class Solution {
public:
    struct newEmployee {
        int importance;
        vector<int> subordinates;
        // 得有默认构造
        newEmployee() : importance(0), subordinates() {}
        newEmployee(int _imp, vector<int> _sub)
            : importance(_imp), subordinates(_sub) {}
    };
    map<int, newEmployee> mp;
    int dfs(int id) {
        int sum = 0;
        auto emp = mp[id];
        for (auto& nxt : emp.subordinates) {
            sum += dfs(nxt);
        }
        sum += emp.importance;
        return sum;
    }
    int getImportance(vector<Employee*> employees, int id) {
        for (auto& emp : employees) {
            mp[emp->id] = newEmployee(emp->importance, emp->subordinates);
        }
        return dfs(id);
    }
};
2. 3249. 统计好节点的数目(中等)

3249. 统计好节点的数目 - 力扣(LeetCode)

思想

1.现有一棵 无向 树,树中包含 n 个节点,按从 0n - 1 标记。树的根节点是节点 0 。给你一个长度为 n - 1 的二维整数数组 edges,其中 edges[i] = [ai, bi] 表示树中节点 ai 与节点 bi 之间存在一条边。

如果一个节点的所有子节点为根的 子树 包含的节点数相同,则认为该节点是一个 好节点

返回给定树中 好节点 的数量。
子树 指的是一个节点以及它所有后代节点构成的一棵树。

代码
复制代码
class Solution {
public:
    vector<vector<int>> g;
    int n;
    int res = 0;
    int dfs(int cur, int fa) {
        int pre = INT_MIN;
        bool isG = true;
        int sum = 1;
        for (auto& nxt : g[cur]) {
            if (nxt == fa)
                continue;
            int cnt = dfs(nxt, cur);
            sum += cnt;
            if (pre == INT_MIN)
                pre = cnt;
            else if (pre != cnt)
                isG = false;
        }
        if (isG)
            ++res;
        return sum;
    }
    int countGoodNodes(vector<vector<int>>& edges) {
        n = edges.size() + 1;
        g.resize(n);
        for (auto& e : edges) {
            int a = e[0], b = e[1];
            g[a].push_back(b);
            g[b].push_back(a);
        }
        dfs(0, -1);
        return res;
    }
};
相关推荐
Swift社区4 小时前
LeetCode 403 - 青蛙过河
算法·leetcode·职场和发展
地平线开发者4 小时前
三种 Badcase 精度验证方案详解与 hbm_infer 部署实录
算法·自动驾驶
papership4 小时前
【入门级-算法-5、数值处理算法:高精度的减法】
算法·1024程序员节
lingran__4 小时前
算法沉淀第十天(牛客2025秋季算法编程训练联赛2-基础组 和 奇怪的电梯)
c++·算法
DuHz4 小时前
基于MIMO FMCW雷达的二维角度分析多径抑制技术——论文阅读
论文阅读·物联网·算法·信息与通信·毫米波雷达
Dragon_D.5 小时前
排序算法大全——插入排序
算法·排序算法·c·学习方法
大数据张老师5 小时前
数据结构——红黑树
数据结构·算法·红黑树
Dream it possible!6 小时前
LeetCode 面试经典 150_链表_两数相加 (57_2_C++_中等)
leetcode·链表·面试
自在极意功。6 小时前
动态规划核心原理与高级实战:从入门到精通(Java全解)
java·算法·动态规划·最优子结构·重叠子问题