代码随想录算法训练营Day27 | 56.合并区间、738.单调递增的数字、968.监控二叉树

LeetCode56.合并区间

56. 合并区间 - 力扣(LeetCode)

1.思路

思路和 用最少数量的弓箭引爆气球 一样,按照左边界从小到大排序后,for 从 1 开始遍历,先记录上一个区间的左边界 l 和右边界 r ,然后如果下一个的左边界小于等于上一个的右边界(即 intervalsi0 <= intervalsi - 11),说明这两个区间是重合的,然后使用 while 循环判断后面是否还有区间和当前区间重合,更新右边界,最后存入答案集(ans.push_back({l,r}))。

cpp 复制代码
class Solution {
public:
    static bool cmp(vector<int>& a,vector<int>& b){
        return a[0]<b[0];
    }
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(),intervals.end(),cmp);
        vector<vector<int>>ans;
        for(int i=1;i<=intervals.size();i++){
            int l=intervals[i-1][0];
            int r=intervals[i-1][1];
            while(i<intervals.size() && intervals[i][0]<=intervals[i-1][1]){
                intervals[i][1]=max(intervals[i][1],intervals[i-1][1]);
                r=intervals[i][1];
                i++;
            }
            ans.push_back({l,r});
        }
        return ans;
    }
};

注:

  1. 这里 for 循环要遍历到 intervals.size() 停止,因为是从 1 开始遍历的,每次存的是前一个合并的区间,要想把最后一个区间也存入数组,必须遍历到原数组末尾的下一位;

  2. 因为要遍历到 intervals.size() ,所以第一个前提就是 i<intervals.size() ,然后才能接着判断是否存在连续的重叠区间;

  3. while 里最后的 i++ :因为实际处理的是 i 前面的重叠区间,所以找到右边界后需要 i++ ,这样才不会重复放入 i 处的区间。

优化

为了避免不断寻找右边界,可以直接对 ans 的末尾修改。只需用 ans.back()1 来和 intervalsi0 来做比较,判断是否有重叠区间,有的话就修改 ans.back()1 为最大的右边界;没有重叠区间就直接放进 ans 里就行。

cpp 复制代码
class Solution {
public:
    static bool cmp(vector<int>& a,vector<int>& b){
        return a[0]<b[0];
    }
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(),intervals.end(),cmp);
        vector<vector<int>>ans;
        if(intervals.size()==0) return ans;
        ans.push_back(intervals[0]);
        for(int i=1;i<intervals.size();i++){
            if(intervals[i][0] <= ans.back()[1]){
                ans.back()[1]=max(ans.back()[1],intervals[i][1]);
            }
            else{
                ans.push_back(intervals[i]);
            }
        }
        return ans;
    }
};

2.复杂度分析

时间复杂度:O(nlog n)

空间复杂度:O(logn)

3.思考

按照 用最少数量的弓箭引爆气球 的思路,这道题还是很好想的,固定左边界,寻找不重叠的最大右边界;第二种方法属实震惊到我了,我只想到了更新右边界,没想到可以先把区间放入数组种,如果有重叠区间,再更新数组末位的值就行了。对容器的使用更深一步理解了。

4.Reference:56. 合并区间 | 代码随想录


LeetCode738.单调递增的数字

738. 单调递增的数字 - 力扣(LeetCode)

1.思路

这道题需要想清楚个例,例如 98 ,一旦出现 ai-1 > ai 的情况,首先让 ai-1--,ai='9',这样下来最接近 98 的单调递增数字就是 89 。

那么是怎样遍历呢?这里必须为从后往前遍历,举个例子 323,从前往后遍历得到的是329,从后往前则是299 (332->329->299) ;

在遍历遇到 ai-1 > ai 时,不能直接就给 ai 赋值为 '9',举个例子 100,如果直接赋值的话,最终结果就是 90 ,这与正确答案 99 不同,所以不能在遍历时直接修改,而是需要用 index 存下标,用来标记从哪个位置开始赋值 '9',最后通过 for 循环来赋值。

cpp 复制代码
class Solution {
public:
    int monotoneIncreasingDigits(int n) {
        string a = to_string(n);
        int index=a.size();
        for(int i=a.size()-1;i>0;i--){
            if(a[i-1]>a[i]){
                a[i-1]--;
                index=i;
            }
        }
        for(int i=index;i<a.size();i++){
            a[i]='9';
        }
        return stoi(a);
    }
};

2.复杂度分析

时间复杂度:O(n)

空间复杂度:O(n)

3.思考

本题难点在于要想清楚个例,一旦出现 ai-1 > ai 的情况,首先让 ai-1--,ai='9' ;

还要想好遍历的顺序,从前往后和从后往前遍历是截然不同的;

最后就是不能直接在遍历时给 ai 赋值'9',而是先通过 index 存下标,最后 for 循环来赋值。

4.Reference:738.单调递增的数字 | 代码随想录


LeetCode968.监控二叉树

968. 监控二叉树 - 力扣(LeetCode)

1.思路

难难难!

这里需要用到递归,先考虑每个节点的状态:

0:该节点无覆盖

1:本节点有摄像头

2:本节点有覆盖

依旧递归三部曲:

  1. 返回 int(代表该节点的状态),传入节点,定义一个全局变量 res 存答案;

  2. 遇到空节点返回 2(空节点的状态只能是有覆盖,这样就可以在叶子节点的父节点放摄像头了);

  3. 后序遍历,先收集左右孩子的状态,再分情况讨论:

情况1:左右节点都有覆盖

return 0 ,此时中间节点就是无覆盖的状态了。

情况2:左右节点至少有一个无覆盖的情况

有一个孩子没有覆盖,父节点就应该放摄像头。此时摄像头的数量要加一,并且return 1,代 表中间节点放摄像头。

情况3:左右节点至少有一个有摄像头

return 2 ,代表父节点是覆盖的状态。

最后判断根节点是否是情况1,如果是的话,就单独让 res++;

cpp 复制代码
class Solution {
public:
    int res=0;
    int traversal(TreeNode* node){
        if(node==NULL) return 2;
        int left = traversal(node->left);
        int right = traversal(node->right);
        // 情况1
        // 左右节点都有覆盖
        if(left==2 && right==2){
            return 0;
        }
        // 情况2
        // left == 0 && right == 0 左右节点无覆盖
        // left == 1 && right == 0 左节点有摄像头,右节点无覆盖
        // left == 0 && right == 1 左节点有无覆盖,右节点摄像头
        // left == 0 && right == 2 左节点无覆盖,右节点覆盖
        // left == 2 && right == 0 左节点覆盖,右节点无覆盖
        if(left==0 || right==0){
            res++;
            return 1;
        }
        // 情况3
        // left == 1 && right == 2 左节点有摄像头,右节点有覆盖
        // left == 2 && right == 1 左节点有覆盖,右节点有摄像头
        // left == 1 && right == 1 左右节点都有摄像头
        if(left==1 || right==1){
            return 2;
        }
        return -1;
    }
    int minCameraCover(TreeNode* root) {
        // 情况4 root 无覆盖
        if(traversal(root)==0) res++;
        return res;
    }
};

2.复杂度分析

时间复杂度:O(n)

空间复杂度:O(n)

3.思考

本题的难点首先是要想到贪心的思路,然后就是遍历和状态推导。

在二叉树上进行状态推导,难度上了一个台阶了。这道题目是名副其实的hard,难难难!

4.Reference:968.监控二叉树 | 代码随想录


贪心算法总结

一、理论

  1. 核心逻辑:无需严格数学证明,手动模拟无反例、能自圆其说,即可尝试贪心思路。
  2. 判定标准:若能找到明确的 "局部最优选择",且该选择可累积成 "全局最优结果",则适合用贪心。
  3. 学习关键:不纠结 "是否为纯贪心题",重点掌握解题思路,结合题目特点灵活应用。

二、刷题

  1. 刷题顺序:不严格按 "简单到困难",而是简单与困难交错,整体呈阶梯式上升。
  2. 与回溯法差异:回溯需严格按框架顺序刷题(前后题目有因果关系),贪心无此限制。
  3. 额外要求:需理解所用编程语言的内部机制(如 C++ 中 list 与 vector 的效率差异),才能写出高效代码。

三、题型分类与要点

1. 简单题(常识类)

  • 特点:思路贴近常识,重点是明确 "局部最优" 与 "全局最优" 的对应关系。
  • 代表题目:分发饼干、K 次取反后最大化的数组和、柠檬水找零。

2. 中等题(巧思类)

  • 特点:仅靠常识难以突破,需挖掘题目隐藏的 "最优逻辑"。
  • 代表题目:摆动序列、单调递增的数字。

3. 股票问题(贪心应用类)

  • 特点:动态规划是专长,但贪心可实现更简洁的解法。
  • 代表题目:买卖股票的最佳时机 II、买卖股票的最佳时机含手续费(后者贪心较绕,可结合动规理解)。

4. 双维度权衡问题(优先级类)

  • 特点:两个维度相互影响,需先固定一个维度排序,再处理另一个维度,避免顾此失彼。
  • 代表题目:分发糖果、根据身高重建队列。

5. 区间问题(高频重点类)

  • 特点:围绕区间的覆盖、去重、合并等操作,贪心思路集中且典型。
  • 代表题目:跳跃游戏、跳跃游戏 II、用最少数量的箭引爆气球、无重叠区间、划分字母区间、合并区间。

6. 其他难题(综合类)

  • 特点:需结合其他知识点(如二叉树、数组操作),贪心思路隐蔽,需反复练习。
  • 代表题目:最大子序和(贪心比动规更优)、加油站、监控二叉树。

四、学习建议

  1. 多刷重复题:难题(如区间问题、监控二叉树)需反复练习,强化贪心 "感觉"。
  2. 聚焦局部最优:解题时先明确 "当前步的最优选择是什么",再验证是否能推导全局最优。
  3. 结合其他算法:部分题目(如股票、最大子序和)可对比贪心与动规的解法,加深理解。
相关推荐
江畔柳前堤4 小时前
大语言模型分布式训练:从并行策略到万卡工程的系统梳理
人工智能·分布式·深度学习·算法·目标检测·机器学习·语言模型
Forever Nore5 小时前
LeetCode 4 寻找两个正序数组的中位数 - 二分
算法·leetcode
罗西的思考7 小时前
【OpenClaw具身硬件】MiniClaw 阅读笔记---(1)基础
人工智能·算法·机器学习
蛋先生DX7 小时前
大模型参数存储格式揭秘:BF不是男朋友
深度学习·算法·llm
爱跳舞的烤冷面8 小时前
自学嵌入式第22天(数据结构——哈希)
数据结构·算法·哈希算法
猎嘤一号8 小时前
博弈论(Game Theory)的理论、算法与工程
人工智能·算法·安全·博弈论
月光船幽幽8 小时前
影子模式下保护 logits 不被修改
人工智能·python·算法
马拉AI8 小时前
腾讯开源 Agent 记忆系统,AI“换对话就忘”的问题有了新解法(附安装使用教程)
人工智能·算法·开源·科研
地平线开发者10 小时前
征程6工具链模型X86推理方式说明
算法
地平线开发者10 小时前
【征程6】校准量化中HistogramObserver解析
算法