代码随想录算法训练营第35天 |(贪心-区间篇) 452. 用最少数量的箭引爆气球 435. 无重叠区间 763.划分字母区间 56.合并区间

代码随想录系列文章目录

贪心篇 - 区间问题


452. 用最少数量的箭引爆气球

题目链接
如何使用最少的弓箭呢?

直觉上来看,貌似只射重叠最多的气球,用的弓箭一定最少,那么有没有当前重叠了三个气球,我射两个,留下一个和后面的一起射这样弓箭用的更少的情况呢?
尝试一下举反例,发现没有这种情况。

局部最优:当气球出现重叠,一起射,所用弓箭最少。全局最优:把所有气球射爆所用弓箭最少。

思路确定下来,怎么去模拟实现这个过程呢?

1.为了让气球尽可能的重叠,需要对数组进行排序。
按照气球起始位置排序,还是按照气球终止位置排序呢?
其实都可以!只不过对应的遍历顺序不同,我就按照气球的起始位置排序了。
既然按照起始位置排序,那么就从前向后遍历气球数组,靠左尽可能让气球重复。
2.遍历每个小区间
从前向后遍历遇到重叠的气球了怎么办?
如果气球重叠了,重叠气球中右边边界的最小值 之前的区间一定需要一个弓箭。
在这里插入图片描述
可以看出首先第一组重叠气球,一定是需要一个箭,气球3,的左边界大于了 第一组重叠气球的最小右边界,所以再需要一支箭来射气球3了。

总结:我们可以将cnt 一开始初始化为1,表示肯定射第一组重叠气球,如果说,遍历第i个区间,区间左边界大于上一组重复区间的最小右边界的话,计数+1,然后维护当前组的最小右边界就行

class Solution:
    def findMinArrowShots(self, points: List[List[int]]) -> int:
        cnt = 1
        points.sort(key = lambda x: x[0])
        for i in range(1, len(points)):
            if points[i][0] > points[i-1][1]: cnt += 1
            else: points[i][1] = min(points[i][1], points[i-1][1])
        return cnt

435. 无重叠区间

题目链接

代码1(把这道题和452.用最少的箭引爆放在一起比较)

如果说这道题是求无重叠的区间,上一道题452.可以说是尽可能让重叠的区间更多,只有迫不得已了才射下一只箭,也就是cnt+1。那其实,迫不得已射另一只箭,也说明区间无重复了,我们必须要拿另一只箭去射另一堆重叠区间了。

所以把上一题的代码改一改这道题就能过,只不过上一题为了尽可能重复,排序按照左端点排,这道题为了使右边有更多的区间,并且维护一个最小右边界,所以用右端点排序,最后求的是移除的个数,n-cnt
在这里插入图片描述

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        n = len(intervals)
        intervals.sort(key = lambda x: x[1])
        cnt = 1
        for i in range(1, len(intervals)):
            if intervals[i][0] >= intervals[i-1][1]:#这题的定义就算端点挨着也算不重叠
                cnt += 1
            else: intervals[i][1] = min(intervals[i-1][1], intervals[i][1])
        return n-cnt

代码2

这道题应该说是比较难想的:
1.一看题就有感觉需要排序,但究竟怎么排序,按左边界排还是右边界排? 为什么按照右边界排序?排完序之后如何遍历?

右边界越小越好,只要右边界越小,留给下一个区间的空间就越大,所以从左向右遍历,优先选右边界小的。

这个也是这道题的思想精髓

接着分析,直接求重复的区间是复杂的,所以转而求最大非重复区间个数。然后这道题其实和452.有点像了,然后可以用452的思路求一下

也可以直接用下图思路:
在这里插入图片描述
区间,1,2,3,4,5,6都按照右边界排好序。

每次取非交叉区间的时候,都是可右边界最小的来做分割点(这样留给下一个区间的空间就越大),所以第一条分割线就是区间1结束的位置。

接下来就是找大于区间1结束位置的区间,是从区间4开始。意味着找到了第二堆区间,(和第一堆无重叠),然后这一堆的右边界更新为区间4的右边界,cnt+1

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        n = len(intervals)
        intervals.sort(key = lambda x: x[1])
        cnt = 1
        right = intervals[0][1]
        for i in range(1, len(intervals)):
            if intervals[i][0] >= right:
                right = intervals[i][1]
                cnt += 1
        return n-cnt

因为我们直接按照右边界排序的,我们最小右边界肯定是第一个数组,我们找到第二片不重叠的数组直接把它的右边界变成right。所以代码直接这样写。

763.划分字母区间

题目链接
这个划分区间,也是找一段类似字母无重复的区间

题目要求同一字母最多出现在一个片段中,那么如何把同一个字母的都圈在同一个区间里呢?

如果没有接触过这种题目的话,还挺有难度的。

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

可以分为如下两步:

1.统计每一个字符最后出现的位置
在这里插入图片描述
那怎么维护一个这样的,字符最后出现的位置呢?有26个字母,我们可以维护一个长度为26的数组,'a’的下标为0,值为最后一个a出现的位置,那么怎么维护呢?

ord(s[i])-ord(‘a’)就是把 a映射到下标0,z映射到下标25,遍历s,不断更新每一个字符出现的最远下标

dict = [0]*26
for i in range(len(s)):
	dict[ord(s[i])-ord('a')] = i

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

class Solution:
    def partitionLabels(self, s: str) -> List[int]:
        dict = [0]*26
        for i in range(len(s)):
            dict[ord(s[i])-ord('a')] = i #把key固定到0-25, 然后value变成最远的位置下标, 即每个字母的最远下标维护成一个list
        left, right = 0, 0
        res = []
        for i in range(len(s)):
            right = max(right, dict[ord(s[i])-ord('a')])
            if i == right:
                res.append(right-left+1)
                left = i+1
        return res
        

56.合并区间

题目链接
其实这道题就是一个模拟题,我在八个月前刚开始做力扣的时候做过一次

这道题的思路比较好想,在做了上面几题之后,立马就模拟出了这道题的方法。
1.按照区间左端点排序
2.模拟的去合并区间,实际操作中,改变res内的上一个的右端点就行,取当前遍历到的区间和上一个的右端点长的(本身合并就该如此,有时候常识就是贪心,夺吓人)

写代码,善用切片,草,小米面试手撕给我挂了

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        intervals.sort(key = lambda x: x[0])
        res = []
        res.append(intervals[0])
        for i in intervals[1:]:
            if i[0] <= res[-1][1]:
                res[-1][1] = max(res[-1][1],i[1])
            else: res.append(i)
        return res
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值