当前位置: 首页 > news >正文

力扣每日一刷Day 15

Practice Day fifteen:Leetcode T435

今天要讲的故事是,正义的骑士,大战邪恶的贪心算法

先来讲讲贪心算法是怎么一回事吧。

事实上贪心算法并不算一种算法,更多的代表一种思想。

核心思想
  • 局部最优 → 全局最优:在每一步,只考虑当前情况下的最佳选择,不回溯、不考虑未来步骤的影响。
  • 无后效性:过去的选择不会影响未来的决策,每个步骤的决策仅依赖于当前状态。
适用条件
  1. 贪心选择性质
    全局最优解可以通过一系列局部最优选择(贪心选择)来获得。即,不从整体出发规划,而是在每一步都做当下最好的选择,最终能得到全局最优。

  2. 最优子结构性质
    问题的最优解包含其子问题的最优解。例如,若问题的最优解在第 k 步选择了方案 A,则剩余步骤的解也必须是对应子问题的最优解。

优缺点
  • 优点

    • 效率高:通常时间复杂度较低(如 O (nlogn),因常涉及排序)。
    • 实现简单:思路直观,无需复杂的动态规划状态转移。
  • 缺点

    • 局限性大:仅适用于满足贪心选择性质的问题,否则可能得到错误解。
    • 无法回溯:一旦做出选择,无法修改,可能错过全局最优。

这里我偷了些懒,用了AI,但是我不会自检的,有的用,那当然使劲用啊,哈哈哈哈

理解了贪心算法的思想后,来看看今天的题目吧!

题目数据:以二维数组的形式,给了很多个闭区间

题目条件:

①返回重叠区间个数

②对于点重叠,不作处理

这是一个局部问题,显然可以使用贪心算法。

今天我们使用Leetcode官方解答来解决这个问题吧!这次的解法我还是比较满意的。

事实上,这道题目不适用贪心或者动态规划也能做。只需利用bitset容器以及test()函数即可轻易解决问题。但这是贪心专题,我们还是先讲讲贪心算法如何解决问题吧。

   int eraseOverlapIntervals(vector<vector<int>>& intervals)

1 好的,我们要考虑他根本没有给我们区间的情况。这种情况理应在一开始就检测,否则程序运行到最后才检测,就拖慢时间了,不合理。

不过事实上,题目数据要求,至少会有一个区间,不存在没有区间的情况。所以这一行代码,其实在这个题目里面是无效的。为了尊重官方解答,我还是把他放上来了。

     if (intervals.empty()) {return 0;}

给出的数组为空,直接返回0值就完事了

2 我们希望进行比较的永远是相邻的两个区间,并且这个相邻需要被定义为:结束区间的相邻。以保证我们进行按一定次序进行比较时,不会出现第三个元素插入两个元素之间,但是被误判为没有重叠的情况。

判断两区间是否重叠的条件为:如果当前区间的结束点小于等于下一个区间的起始点,说明区间没有重叠。这无关排序,只要满足这个条件的,都说明,没有重叠。

就像这样[1,5] [3,6]显然按照上面的条件,这两个区间是进行重叠的。但是当我们插入第三个区间,[1,2],此时[1,2]与[3,6]进行比较,是没有重叠的,按照程序的判断,此时应该输出无重叠。但事实上[1,5]和[1,2]进行重叠了,程序没有对此做出反应。

为了杜绝这种情况的发生,所以我们需要进行排序,排序的顺序为,结束点从小到大依次排列。

        sort(intervals.begin(), intervals.end(), [](const auto& u, const auto& v) {return u[1] < v[1];});

这个sort()函数的形状不太常见,我希望可以讲解他。

先来看看他的函数原型吧

template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );

各参数含义:

在 C++ 中,随机访问迭代器(Random Access Iterator) 是迭代器类型中的一种,它支持所有迭代器的操作,并且额外提供了随机访问的能力

  1. RandomIt first
    随机访问迭代器(Random Access Iterator),指向要排序的序列的起始位置(包含该位置元素)。在你的代码中,对应 intervals.begin()

  2. RandomIt last
    随机访问迭代器,指向要排序的序列的结束位置(不包含该位置元素)。在你的代码中,对应 intervals.end()

  3. Compare comp
    比较函数(或函数对象),用于定义排序规则。它需要满足以下条件:

    但是我们发现,最后一个函数好像不太对劲,怎么形状这么猎奇,怎么没有函数名,还有个[]在前面?

    这是Lambda表达式

    • 接受两个参数(类型与迭代器指向的元素类型一致),即代码中的 const auto& u 和 const auto& v(实际类型为 const vector<int>&)。
    • 返回 bool 类型:若第一个参数应排在第二个参数之前,则返回 true;否则返回 false

    在 C++ 中,lambda 表达式是一种匿名函数(没有函数名的函数),可以在需要函数的地方直接定义和使用,通常用于简化代码,尤其是作为算法的回调函数(如排序、遍历等场景)。

    [capture](parameters) -> return_type {...}

    外部变量(External Variables) 通常指的是在当前代码块(如函数、lambda 表达式、循环体等)外部定义的变量,即不在当前作用域内声明,但可能被当前代码块访问的变量。

    • [capture](捕获列表)
      用于捕获外部变量(lambda 表达式所在作用域中的变量),使其能在函数体内使用。常见用法:

      • []:不捕获任何外部变量
      • [=]:按值捕获所有外部变量(只读)
      • [&]:按引用捕获所有外部变量(可修改)
      • [x, &y]:按值捕获 x,按引用捕获 y
    • (parameters)(参数列表)
      与普通函数的参数列表类似,用于定义函数的输入参数。

    • -> return_type(返回类型)
      可选部分,指定函数的返回类型。如果函数体中只有一条 return 语句,编译器可以自动推断返回类型,此时可省略。

    • { ... }(函数体)
      函数的具体实现逻辑。

    没有例子实在干巴,我们就以这份代码为例子

[](const auto& u, const auto& v) {return u[1] < v[1];}

u和v显然是在Lambda表达式之外被定义的,可是并没有在[ ]捕获列表中捕获变量,似乎与我们在上面说的不大一样。

但Lambda表达式比较赖皮的是,由于他在参数列表进行了参数的输入,所以现在u和v被识别为Lambda表达式本身的参数,所以不必使用[ ]进行捕获。

我们发现,在返回类型的部分是没有东西的,这是因为符合了上述使用条件:函数体中只有一条return语句,那么编译器就会自动推断返回类型,可以直接省略。

在函数体部分,由于传入的u和v实际上是迭代器的地址,加上索引后就可以指向迭代器指向元素的参数,这里索引为1,表示的是区间的第二个数字,也就是结束时间。别忘了,数组索引可是0-base的。

现在,来讲解sort函数本身的排列顺序。

C++ 标准库中的 sort 函数默认是从小到大(升序) 排序的,但这取决于排序时使用的比较规则

当指定第三个参数(如 lambda 表达式、函数指针等)时,sort 会严格按照该规则排序。规则的逻辑是:

  • 若比较函数 comp(a, b) 返回 true,则 a 会被排在 b 前面;
  • 否则 b 排在 a 前面。

现在,使用了sort函数后,较小的结束区间会被排列在前面

3 判断两区间是否重叠的条件为:如果当前区间的结束点小于等于下一个区间的起始点,说明区间没有重叠。

那么,进入for循环遍历比较前,我们需要人为初始化一个对象,使之程序存在比较的对象

        int right = intervals[0][1];

接下来还有一个问题:你该如何计算需要删去的区间呢?我们的区间个数是已知的,只是不知道多少个区间重叠。那么只能使用间接的方法了:用无重叠的区间个数减去总区间个数,就是重叠的区间个数了。

所以我们需要一个元素,来为我们记录无重叠区间的个数。由于官方数据规定,必定会有一个元素,所以无重叠区间初始化为1。

      int ans = 1;

接着进入for循环遍历判断,当比较完成后,移交比较对象,即把right值赋为当前处理区间的结束点

        for (int i = 1; i < n; ++i) {if (intervals[i][0] >= right) {++ans;right = intervals[i][1];}}

当上述程序结束后,输出答案

        return n - ans;}

作者:力扣官方题解

链接:https://leetcode.cn/problems/non-overlapping-intervals/solutions/541543/wu-zhong-die-qu-jian-by-leetcode-solutio-cpsb/

来源:力扣(LeetCode)

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

http://www.dtcms.com/a/347453.html

相关文章:

  • 在github上通过dmca数字版权申诉侵权并删除侵权仓库
  • 图结构使用 Louvain 社区检测算法进行分组
  • P9246 [蓝桥杯 2023 省 B] 砍树
  • 蓝桥杯算法之基础知识(2)——Python赛道
  • ComfyUI Portrait Master肖像大师中文版
  • TopK问题(堆排序)-- go
  • 牛客面经2 京东社招-002
  • 知识蒸馏 Knowledge Distillation 序列的联合概率 分解成 基于历史的条件概率的连乘序列
  • 基于SpringBoot的招聘系统源码
  • 破解秘籍——hashcat使用宝典
  • 商超场景徘徊识别误报率↓79%!陌讯多模态时序融合算法落地优化
  • 书写本体论视域下的文字学理论重构
  • 防范瓦斯爆炸:AI摄像机如何杜绝煤矿井下违规抽烟?
  • 复杂工业场景误报率↓85%!陌讯多模态火焰识别算法实战解析
  • ⸢ 零 ⸥ ⤳ 安全体系构建内容结构
  • 卷积神经网络的基本概念
  • 【码蹄杯】2025年本科组省赛第一场
  • 解决Node.js安装与npx命令问题
  • Stm32通过ESP8266 WiFi连接阿里云平台
  • 使用Lora微调大模型介绍篇
  • 【Python】新手入门:Python标准库有哪些常用模块?
  • 使用Screenpipe+本地大模型实现私人助手Agent
  • 【小宁学习日记4 Python】Python 变量与标识符全解:编程入门的基石
  • 简单AI:搜狐公司旗下AI绘画产品
  • 最新短网址源码,防封。支持直连、跳转。 会员无广
  • 部署PG一主一从
  • 使用Proxifier+vmware碰到的一些问题
  • GEO优化服务商:AI时代数字经济的新引擎——解码行业发展与技术创新实践
  • 蔬菜溯源系统的开发与设计小程序
  • 【基础-判断】使用http模块发起网络请求时,必须要使用on(‘headersReceive’)订阅请求头,请求才会成功。