day30 代码随想录 | 射气球 无重叠区间 划分字母区间 合并区间

射气球

有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points ,其中points[i] = [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。

一支弓箭可以沿着 x 轴从不同点 完全垂直 地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstartxend, 且满足  xstart ≤ x ≤ xend,则该气球会被 引爆 。可以射出的弓箭的数量 没有限制 。 弓箭一旦被射出之后,可以无限地前进。

给你一个数组 points ,返回引爆所有气球所必须射出的 最小 弓箭数 

示例 1:

输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
解释:气球可以用2支箭来爆破:
-在x = 6处射出箭,击破气球[2,8]和[1,6]。
-在x = 11处发射箭,击破气球[10,16]和[7,12]。

这个题你看者描述这么多,起始就是一个区间的问题。气球的直径大小就是区间的跨度。只要你在区间内,气球就能爆,示意图如下。核心就是你要找到非重叠的区间范围,遇到非重叠,你就得加箭来射。相信思路就很简答。我们先排序,左边界和右边界都可以。假设我们以左边界排序。

即当前的气球区间为interval0, interval1, 上一个气球区间为left,right. 因为排过序。如果interval0 < right。说明已经重叠了,我们不需要加箭,但是,此时,我们要更新右边界,我们是找最小的两者右边界,如果不找最小,那么就超过了,你就一个箭射一个求。如果interval0 > right。说明已经不重叠了,我们得加箭

def findMinArrowShots(self, points: List[List[int]]) -> int:
    if len(points) == 1:
        return 1
    count = 1
    points.sort(key=lambda x: x[0])
    for i in (1, len(points)):
        if points[i-1][1] > points[i][0]: # 说明重叠
            # 我需要更新右边界
            points[i][1] = min(points[i-1][1], points[i][1])

        else: # 不重叠 需要加箭
            count += 1
    return count

    

    

435 无重叠区间

给定一个区间的集合 intervals ,其中 intervals[i] = [starti, endi] 。返回 需要移除区间的最小数量,使剩余区间互不重叠 

示例 1:

输入: intervals = [[1,2],[2,3],[3,4],[1,3]]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。

这个题其实也很好思考。他其实是要我们找重叠区间的数量。你有两中思路。一种是直接去找,另一种是用总区间前去非重叠区间。都可以实现。

直接计算重叠区间,我们可以通过排序左区间。看下一个右区间是否重叠,如果重叠,我们取最小的右边界。

def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
    if len(intervals) == 1:
        return 0

    intervals.sort(key=lambda: x[0])
    count = 0

    for i in range(1, len(intervals)):
        if intervals[i-1][1] > intervals[i][0]:
            intervals[i][1] = min(intervals[i-1][1], intervals[i][1])
            count += 1
    
    return count

第二种思路,计算非重叠区间,用总区间减去重叠区间

这就需要右排序,如果右边的区间小于下一个左边的区间,说明没有重叠

def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
    if len(intervals) == 1:
        return 0

    intervals.sort(key=lambda: x[1])
    count = 1 # 初始化为1
    end = intervals[0][1]
    for i in range(1, len(intervals)):
        if end < intervals[i][0]: # 没有重叠
            count += 1
            end = intervals[i][1]
            
            
    return len(intervals) - count

763.划分字母区间

给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。

返回一个表示每个字符串片段的长度的列表。

示例 1:

输入:s = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca"、"defegde"、"hijhklij" 。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 这样的划分是错误的,因为划分的片段数较少。

经过前面两个题,这个题其实也是很好想思路的。我们需要得到每一个字母最长的index在哪里。

然后我们遍历整个字符,并且比较当前字符的最长max_index和 max_index的长度。max_index初始化为0.如果当前遍历的索引i 等于最大的max_index,那么说明就可以切割了。因为你的字符串里面现在最远的字符已经到达,就可以切割了。

def partitionLabels(self, s: str) -> List[int]:
    last_index = {}

    for i in range(len(s)):
        last_index[s[i]] = i

    start = 0
    max_index = 0

    res = []

    for i in range(len(s)):
        max_index = max(max_index, last_index[s[i]])
        if i == max_index:
            length = i - start + 1
            res.append(length)
            start = i + 1
    return res

56 合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。

示例 1:

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].

这个题其实你做过你前面的化,就很简单。我们就直接排序呗。按左区间排序。然后比较右边与下一个左区间。如果大,合并。如果小,不合并。

def merge(self, intervals: List[List[int]]) -> List[List[int]]:
    if len(intervals) == 1:
        return intervals   

    intervals.sort(key=lambda: x[0])

    left = intervals[0][0]
    right = intervals[0][1]
    res = []

    for i in range(1, len(intervals)):
        if intervals[i][0] <= right:
            right = max(right, intervals[i][1])

        else:
            res.append([left, right])
            left = intervals[i][0]
            right = intervals[i][1]

    res.append([left, right])

    return res

    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值