代码随想录day33 贪心(4)

452. 用最少数量的箭引爆气球 - 力扣(LeetCode)

先根据气球左边界从小到大排序,然后从左往右遍历,需要维护当前区间最小右边界

写法一:用while循环,不过边界判断和i更新的位置容易出错。当气球的左边界不大于区间右边界,更新右边界end,i增加1;否则,箭的数量增加1。

class Solution:
    def findMinArrowShots(self, points) -> int:
        points.sort(key = lambda x: x[0])
        i = 0
        cnt = 0
        while i < len(points):
            end = points[i][1]
            i += 1
            while i < len(points) and points[i][0] <= end:
                end = min(end, points[i][1])
                i += 1
            cnt += 1
        return cnt

写法二:for循环遍历points,将当前元素的右边界改为区间右边界,每次判断元素左边界与上一个元素的有边界即可。

class Solution:
    def findMinArrowShots(self, points) -> int:
        points.sort(key = lambda x: x[0])
        cnt = 1
        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. 无重叠区间 - 力扣(LeetCode)

首先按左边界从小到大将区间排序,接着遍历一次数组,若上一个区间与当前区间不重合,不需要处理;若重合,删掉右边界更靠右的区间(cnt增1),修改当前区间的右边界为更靠左的右边界(因为更靠右的已经删掉了)。

class Solution:
    def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
        intervals.sort(key = lambda x: x[0])
        cnt = 0

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

763. 划分字母区间 - 力扣(LeetCode)

解法一:与上面两题思路类似,首先耗费O(1)的空间统计字符串中各字母所在区间:[首次出现位置,最后出现位置],接着按左边界从小到大排序,用left和right维护当前区间的左右边界,若当前字母区间在上一个区间之外,即right<该字母开始位置,就将上一个区间长度推入res,更新left和right为当前字母的左右边界;若当前字母与上一区间重合,需要合并两区间:将right更新为上一区间右界和当前字母右界的较大值。循环结束还有最后一个区间未入res,加入。

易错1:统计时,当第一次遇到一个字母,应该将其右边界也初始化为左边界的值,因为它可能只出现一次。

易错2:通过上一区间右界right判断是否重叠,right更新时也是取自身和当前右界的最大值,而不是考虑上个字母的右界values[i-1][1]。

class Solution:
    def partitionLabels(self, s: str) -> List[int]:
        interval = dict()
        for i in range(len(s)):
            c = s[i]
            try:
                interval[c][1] = i
            except KeyError:
                interval[c] = [i, i]

        values = list(interval.values())
        values.sort(key = lambda x: x[0])
        res = []
        left = 0
        right = values[0][1]
        for i in range(1, len(values)):
            if right < values[i][0]:
                res.append(right-left+1)
                left = values[i][0]
                right = values[i][1]
            else:
                right = max(values[i][1], right)

        res.append(right-left+1)

        return res

解法二:用字典保存字母最后出现位置,遍历s,用当前字母最后位置更新区间右界right,若当前下标等于区间右界,说明得到了一个区间,将长度推入res。

class Solution:
    def partitionLabels(self, s: str) -> List[int]:
        interval = dict()
        for i, c in enumerate(s):
            interval[c] = i

        left = 0
        right = interval[s[0]]
        res = []
        for i, c in enumerate(s):
            right = max(right, interval[c])
            if right == i:
                res.append(right-left+1)
                left = i + 1
        return res

### 关于贪心算法的题解 #### 什么是贪心算法? 贪心算法是一种在每一步选择中都采取当前状态下最优策略的方法,从而希望最终能够达到全局最优解。这种方法并不总是有效,但在某些特定情况下可以得到正确的结果。 #### 贪心算法的应用场景 贪心算法通常适用于那些可以通过局部最优来实现整体最优的问题。例如,在分配资源、路径规划等问题中,如果每次都能做出最佳的选择,则可能获得全局的最佳解决方案[^2]。 #### 示例题解:分发饼干 (LeetCode 455) 给定两个数组 `g` 和 `s`,分别表示孩子们的胃口值和饼干尺寸。目标是尽可能多地满足孩子的胃口需求。 解决方法如下: 1. 将孩子的需求按从小到大的顺序排列。 2. 同样地,将饼干也按照大小排序。 3. 使用双指针逐一匹配最小的孩子需求与最小的可用饼干。 ```python def findContentChildren(g, s): g.sort() s.sort() child_index = cookie_index = 0 while child_index < len(g) and cookie_index < len(s): if s[cookie_index] >= g[child_index]: child_index += 1 cookie_index += 1 return child_index ``` 这段代码实现了上述逻辑,并返回能被满足的孩子数量。 #### 另一个例子:摆动序列 (LeetCode 376) 此问题的目标是从输入数组中找到最长的子序列,使得该子序列中的相邻元素交替上升下降。以下是基于贪心思想的一个 C++ 实现: ```cpp class Solution { public: int wiggleMaxLength(vector<int>& nums) { if (nums.size() <= 1) return nums.size(); int curdif = 0; int predif = 0; int result = 1; for (int i = 0; i < nums.size() - 1; ++i) { curdif = nums[i + 1] - nums[i]; if ((curdif > 0 && predif <= 0) || (curdif < 0 && predif >= 0)) { result++; predif = curdif; } } return result; } }; ``` 这里的关键在于利用差值的变化趋势判断是否构成有效的波动点[^3]。 #### 总结 虽然贪心算法不像动态规划那样有固定的模板,但它仍然遵循一定的设计原则——始终追求局部最优解并验证其能否导向全局最优解。对于初学者来说,掌握几个经典案例是非常重要的[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值