代码随想录算法训练营DAY36|C++贪心算法Part.5|435.无重叠区间、763.划分字母区间、56. 合并区间

文章目录

  • 435.无重叠区间
    • 按右边界排序
      • CPP代码
    • 按左边界排序
      • 如何判断相邻区间是否重叠
      • 如何判断一下一个区间与当前相邻区间是否重叠
      • 总结
      • CPP代码
  • 763.划分字母区间
    • 思路
    • 伪代码实现
    • CPP代码
  • 56. 合并区间
    • 思路
    • CPP代码

435.无重叠区间

力扣题目链接

文章链接:435.无重叠区间

视频链接:贪心算法,依然是判断重叠区间 | LeetCode:435.无重叠区间

状态:排序顺序很重要!决定了我们如何处理后续逻辑。对于按右边界排序,我们只要抓住分割线即可,每次更新分割线,说明就有非交叉区间;

想都不用想,本题首先要求的肯定就是进行排序,让为了让我们后续更好进行操作。

并且可以很直观得推导出我们的贪心策略:

局部最优——当前区间与相邻两个区间是否重叠,这里是非常有技巧的,具体可以看下面的思路

全局最优——找出所有的重叠区间

按右边界排序

我们先按右边界进行排序,然后从左向右记录非交叉区间的个数。最后用区间总数减去非交叉区间的个数就是需要移除的区间个数了。

记录非交叉区间的个数也是很需要技巧的:

总之一句话,最重要的点就在于找到区间的分割线,每次遇到分割线,我们就记录一次非交叉区间个数。比如上文中,更新了两次分割线,所以非交叉区间是3。所以在代码表现上,也是比较直观的。
基于以上代码的一个重要前提就是:区间是按照右边界来排序的

CPP代码

class Solution {
public:
    // 按照区间右边界排序
    static bool cmp (const vector<int>& a, const vector<int>& b) {
        return a[1] < b[1];
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if (intervals.size() == 0) return 0;
        sort(intervals.begin(), intervals.end(), cmp);
        int count = 1; // 记录非交叉区间的个数
        int end = intervals[0][1]; // 记录区间分割点
        for (int i = 1; i < intervals.size(); i++) {
            if (end <= intervals[i][0]) {	//与每个区间的左边界比较
                end = intervals[i][1];		//更新分割线
                count++;
            }
        }
        return intervals.size() - count;
    }
};

按左边界排序

对于左边界排序,这里拿 intervals = [[1,100],[11,22],[1,11],[2,12]]举例,

排序后:intervals = [[1,100],[1,11],[2,12],[11,22]]。如果我们按照右边界排序的处理还能行吗。简单推导一下,这样会导致我们的最终结果是3!因为end永远都无法更新,程序认为只有一条分割线,也就是count = 1

那么如果按照左边界来排序应该怎么写呢?

如何判断相邻区间是否重叠

如果当前区间的左边界[1, 11]大于等于上一个区间的右边界[1, 100]。说明相邻区间不重叠,如果不满足该情况,那肯定说明区间重叠

if (nums[i][0] >= nums[i-1][1]) //当前区间的左边界大于等于上一个区间的右边界
else {
   count++; //记录我们重叠了多少个区间
}

如何判断一下一个区间与当前相邻区间是否重叠

要首先计算出之前我们判断的相邻区间的最小边界(左边界的最小值),和我们下一个区间的左边界是否重叠。

else {
  count++;
  nums[i][1] = min(nums[i-1][1], nums[i][1])
}

这里num[i][1]=min(nums[i-1][1], nums[i][1]),等到i遍历到下一个区间,应该和之前两个相邻区间的最小右边界比较,如果当前i区间的左边界要大的话,那么说明不是重叠区间。

总结

左边界的思想一句话就是:如果发现了重叠区间,我们就进行更新新的分割点,并且count++

CPP代码

class Solution {
public:
    static bool cmp (const vector<int>& a, const vector<int>& b) {
        return a[0] < b[0]; // 改为左边界排序
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if (intervals.size() == 0) return 0;
        sort(intervals.begin(), intervals.end(), cmp);
        int count = 0; // 注意这里从0开始,因为是记录重叠区间
        int end = intervals[0][1]; // 记录区间分割点
        for (int i = 1; i < intervals.size(); i++) {   
            if (intervals[i][0] >= end)  end = intervals[i][1]; // 无重叠的情况
            else { // 重叠情况 
                end = min(end, intervals[i][1]);
                count++;
            }
        }
        return count;
    }
};

# 精简版
class Solution {
public:
    static bool cmp (const vector<int>& a, const vector<int>& b) {
        return a[0] < b[0]; // 改为左边界排序
    }
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if (intervals.size() == 0) return 0;
        sort(intervals.begin(), intervals.end(), cmp);
        int count = 0; // 注意这里从0开始,因为是记录重叠区间
        for (int i = 1; i < intervals.size(); i++) {
            if (intervals[i][0] < intervals[i - 1][1]) { //重叠情况
                intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]);
                count++;
            }
        }
        return count;
    }
};

763.划分字母区间

力扣题目链接

文章链接:763.划分字母区间

视频链接:贪心算法,寻找最远的出现位置! LeetCode:763.划分字母区间

状态:

本题其实就是一句话“面多了加水,水多了加面,直到刚刚好”。

这里完全不是贪心的思路,就是全局的一个模拟,主要它也属于重叠区间的问题。

思路

思路上还是很难想到的。

我们在遍历过程中,相当于找到每一个字母出现的边界,如果找到之前遍历过的所有字母的最远边界,说明这个边界就是分割点了

所以分为如下两步:

  • 统计每个字符最后出现的位置
  • 从头遍历字符,并更新字符的最远出现下标,如果找到字符最远出现位置下标和当前下标相等了,则找到了分割点

我们需要记录每个字符出现的最后位置,如图:

伪代码实现

  • 统计每一个字符最后出现的位置
int hash[27] = {0}; //i为字符,hash[i]为字符出现的最后位置
for (int i = 0; i < S.size(); ++i) {
  hash[S[i] - 'a'] = i;
}
  • 定义变量
vector<int> result;
int left = 0;
int right = 0;
  • 字符出现的最远边界的更新和结果存储
for (int i = 0; i < S.size(); i++) {
    right = max(right, hash[S[i] - 'a']); // 找到字符出现的最远边界
    if (i == right) {
        result.push_back(right - left + 1);
        left = i + 1;
    }
}

CPP代码

class Solution {
public:
    vector<int> partitionLabels(string S) {
        int hash[27] = {0}; // i为字符,hash[i]为字符出现的最后位置
        for (int i = 0; i < S.size(); i++) { // 统计每一个字符最后出现的位置
            hash[S[i] - 'a'] = i;
        }
        vector<int> result;
        int left = 0;
        int right = 0;
        for (int i = 0; i < S.size(); i++) {
            right = max(right, hash[S[i] - 'a']); // 找到字符出现的最远边界
            if (i == right) {
                result.push_back(right - left + 1);
                left = i + 1;
            }
        }
        return result;
    }
};

56. 合并区间

力扣题目链接

文章链接:56. 合并区间

视频链接:贪心算法,合并区间有细节!LeetCode:56.合并区间

状态:

思路

本题同样也是重叠区间的问题。

区别在于判断区间重叠后的逻辑,本题是将重叠区间进行合并。

先排序,如果intervals[i][0] <= intervals[i - 1][1]就有重叠,所以进行合并

合并的逻辑也比较简单,

用合并区间后左边界和右边界,作为一个新的区间,加入到result数组里就可以了。如果没有合并就把原区间加入到result数组

CPP代码

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        vector<vector<int>> result;
        if (intervals.size() == 0) return result; // 区间集合为空直接返回
        // 排序的参数使用了lambda表达式
        sort(intervals.begin(), intervals.end(), [](const vector<int>& a, const vector<int>& b){return a[0] < b[0];});

        // 第一个区间就可以放进结果集里,后面如果重叠,在result上直接合并
        result.push_back(intervals[0]); 

        for (int i = 1; i < intervals.size(); i++) {
            if (result.back()[1] >= intervals[i][0]) { // 发现重叠区间
                // 合并区间,只更新右边界就好,因为result.back()的左边界一定是最小值,因为我们按照左边界排序的
                result.back()[1] = max(result.back()[1], intervals[i][1]); 
            } else {
                result.push_back(intervals[i]); // 区间不重叠 
            }
        }
        return result;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/569399.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

开发简易复用 SDK(项目加分项)

文章目录 开发 SDK新建项目修改pom文件删除启动类创建配置类复制之前的客户端新建spring.factories打包 开发 SDK 为什么要开发SDK。 减少代码的冗余提高代码的复用 如果实际项目中需要使用到该SDK&#xff0c;在pom.xml中注入就可以了。 类似于maven一样&#xff0c;把需要…

索引的最左匹配原则

索引的最左匹配原则 我们先创建一张测试表&#xff0c;表的两个字段用来创建联合索引 CREATE TABLE test(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,col1 INT,col2 INT,col3 INT );CREATE INDEX idx_c1c2 ON test(col1, col2);现在我们就可以分析查询sql脚本了 1.使用联合索…

jasypt组件死锁bug案例分享

事故描述 1、上午9.55发布了一个Apollo动态配置参数&#xff1b; 2、片刻后&#xff0c;服务器接口开始出现大量的超时告警&#xff0c;似乎是某资源被耗尽不足分配&#xff1b; 3、正值业务请求高峰的上午十点&#xff08;平台上午10点会有一些活动会拉一波用户流量&#x…

Redis事务全解析:从MULTI到EXEC的操作指南!

【更多精彩内容,欢迎关注小米的微信公众号“软件求生”】 亲爱的粉丝朋友们,大家好!今天我们要讨论的主题是Redis的事务。Redis作为一款优秀的NoSQL数据库,凭借其高性能和灵活性广受欢迎。事务是Redis的一项关键功能,它为我们提供了一种在数据操作中确保一致性的机制。接…

【目标检测】FPN特征金字塔完整流程详解

学习视频&#xff1a;1.1.2 FPN结构详解 对比 可以看到FPN是自上而下、自下而上并且可以进行多尺度特征融合的的层级结构。 具体结构 1x1 conv: 对通道数进行调整&#xff0c;不同大小的特征图通道数不同&#xff0c;越高层次的特征图通道数越大&#xff0c;论文中使用256个1…

Linux-线程互斥和死锁

目录 一.线程互斥 1.1 进程线程间的互斥相关背景概念 1.2 互斥量mutex 二.互斥量的接口 2.1 初始化互斥量 2.2 销毁互斥量 2.3 互斥量加锁和解锁 2.4 改进后售票代码 三.死锁 3.1.什么是死锁&#xff1f; 3.2.死锁四个必要条件 3.3 避免死锁 一.线程互斥 1.1 进程…

【01-机器学习入门:理解Scikit-learn与Python的关系】

文章目录 前言Python与机器学习Scikit-learn简介Scikit-learn与Python的关系使用Scikit-learn进行机器学习结语前言 在当今的数据科学和人工智能领域,机器学习已经成为了一个不可或缺的组成部分。而对于那些刚刚踏入这一领域的新手来说,理解机器学习的基本概念和找到合适的工…

实施阶段(2024年4月)

【活动二】编程解决问题&#xff0c;二分查找法统计查字典次数。 任务要求&#xff1a;假设字典为1000页&#xff0c;若用二分法来翻到用户输入的具体指定的页数&#xff0c;则需要的最大查找次数为&#xff1f; 设计算法&#xff1a; 取总页码数据中间值&#xff0c;将待查数…

深入理解Linux文件系统和日志分析

目录 一.inode与block 1.inode与block概述 1.1.文件数据包括元信息与实际数据 1.2.文件存储在硬盘上&#xff0c;硬盘最小存储单位是“扇区”&#xff0c;每个扇区存储512字节 1.3.block&#xff08;块&#xff09; 1.4.inode&#xff08;索引节点&#xff09; 2.inode内…

统一SQL 支持Oracle CHAR和VARCHAR2 (size BYTE|CHAR)转换

统一SQL介绍 https://www.light-pg.com/docs/LTSQL/current/index.html 源和目标 源数据库&#xff1a;Oracle 目标数据库&#xff1a;Postgresql&#xff0c;TDSQL-MySQL&#xff0c;达梦8&#xff0c;LightDB-Oracle 操作目标 在Oracle中的CHAR和VARCHAR2数据类型&…

6.2 整合MongoDB

6.2 整合MongoDB 1. MongoDB简介2. MongoDB安装2.1 下载2.2 配置MongoDB2.3 MongoDB的启动和关闭1. 启动MongoDB2. 关闭MogoDB 2.4 安全管理 3. 整合SpringBoot3.1 依赖3.2 MongoTemplate使用3.3 测试1. 新增2. 查询3. 删除 *************************************************…

sudo的设置

sudo指令就是提高你的用户权限&#xff0c;用来完成root可以完成的工作&#xff0c;但是有一个前提&#xff0c;就是被root添加到信任名单中&#xff0c;接下来我们要讲解如何在root中添加用户到信任名单中。 在root中输入指令&#xff1a; 即可到达添加信用列表的位置&#x…

读天才与算法:人脑与AI的数学思维笔记08_生物的创造力

1. 生物的创造力 1.1. 在进化树中是否有其他的物种已经具有与我们人类相当的创造力水平 1.2. 20世纪50年代中期&#xff0c;动物学家德斯蒙德莫里斯&#xff08;Desmond Morris&#xff09;在伦敦动物园做了这样一个试验 1.2.1. 动物学家给…

基于卷积神经网络的垃圾图像分类系统研究与实现

1.摘要 垃圾分类作为资源回收利用的重要环节之一, 可以有效地提高资源回收利用效率, 进一步减轻环境污染带来的危害. 随着现代工业逐步智能化, 传统的图像分类算法已经不能满足垃圾分拣设备的要求. 本文提出一种基于卷积神经网络的垃圾图像分类模型 (Garbage Classification Ne…

RF高频腔设计(7)

3.11 高阶模 之前我们说过&#xff0c;由于束流负载或者其它的原因会在RF高频腔中激发出很多模式&#xff0c;这些模式可能会对束流的稳定性造成不利的影响&#xff0c;尤其是腔的 R / Q ∗ Q L R/Q*Q_L R/Q∗QL​很高时。 为了解决这个问题&#xff0c;需要使用HOM阻尼器来抑…

数字化化转型中,人的重要性仍然不容忽视!

今日&#xff0c;蚓链多个数字化营销项目进行了复盘&#xff0c;最后数据显示&#xff1a;营销数据表现向上且居于高端的企业不仅仅是营销机制设置的科学、贴合市场&#xff0c;数据好的更主要原因还是体现在“人”&#xff01;是团队的构成科学合理&#xff0c;拿到好结果的大…

AWVS的使用

AWVS的使用 1、使用docker拉取AWVS的镜像 docker pull secfa/docker-awvs 2.使用AWVS docker run -it -d -p 13443:3443 --cap-add LINUX_IMMUTABLE secfa/docker-awvs 3.访问 4.输入账号密码 https://hub.docker.com/r/secfa/docker-awvs 找到账号密码 username:adminadmin.…

1W 1.5KVDC 3KVDC 隔离宽范围输入,单、双输出 DC/DC 电源模块——TP2L-1W 系列

TP2L-1W系列是一款高性能、超小型的电源模块&#xff0c;宽范围2&#xff1a;1,4:1输入&#xff0c;输出有稳压和连续短路保护功能&#xff0c;隔离电压为1.5KVDC&#xff0c;3KVDC工作温度范围为–40℃到85℃。特别适合对输出电压的精度有严格要求的地方&#xff0c;外部遥控功…

【Python】爬虫-基础入门

目录 一、什么是爬虫 二、爬虫的主要用途 三、学会爬虫需要掌握的技能 四、爬虫使用的语言 五、编写爬虫需要的库&#xff0c;以python为例 六、爬虫示例-python 示例一 示例二 示例三 一、什么是爬虫 爬虫&#xff0c;又称网络爬虫或网页爬虫&#xff0c;是一种用来自…

《智能前沿:应对ChatGPT算力挑战》

在全球人工智能热潮中&#xff0c;以 ChatGPT 为代表的 AIGC 技术引发了广泛关注。人工智能和机器学习等技术对数据规模及处理速度等提出了更高要求。在数据成为主要生产要素的当下和未来&#xff0c;如何跟上时代的发展步伐&#xff0c;构建适应 AI 需求的数据中心&#xff0c…