leetcode/notes/src/greedy.md
Sainnhe Park ff53de285f
All checks were successful
ci/woodpecker/push/test Pipeline was successful
Update notes
2023-02-07 17:45:52 +08:00

153 lines
5.9 KiB
Markdown

# 总结
使用场景:手动模拟一下感觉可以局部最优推出整体最优,而且想不到反例,那么就试一试贪心。
思路:
假设题干是这样的:给出一系列元素(通常放在一个数组中),请你从中挑选出满足条件的 N 个数,使得总和最大。
来分析一下题干:
- 一系列元素,这是 base
- 满足条件的 N 个数,这是限制条件
- 总和最大,这是全局最优的优化目标
贪心算法是怎样的呢?就是直接找全局最优一般不好找,我们可以在 base 上找局部最优,比如我们可以这么设计:
- 总和大于 0 的一段连续子数组,这是限制条件
- 长度最长,这是局部最优的优化目标
限制条件和优化目标都不一定要和全局最优一样,但是这里有个关键:
你在找局部最优的过程中可以推导出全局最优。
注意,不是从所有局部最优的结果中推导出全局最优的结果,而是你在寻找局部最优的过程中可以找到全局最优。
什么意思?
假设你找到了 K 个局部最优结果,但我们并不是要从这 K 个结果中推出全局最优,而是在你找这 K 个结果的过程中,我们可以用一个变量 `record` 来记录某些东西,然后由这个变量 `record` 和这 K 个局部最优结果共同推导出全局最优解。
---
## [53. 最大子序和](https://leetcode.cn/problems/maximum-subarray/)
> Given an integer array `nums`, find the subarray with the largest sum, and return its _sum_.
>
> ```text
> Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
> Output: 6
> Explanation: The subarray [4,-1,2,1] has the largest sum 6.
> ```
全局:
- 限制:连续子数组
- 目标:总和最大
局部:
- 限制:连续子数组,总和大于零
- 目标:该子数组长度最长
过程:
在找最长子数组时用一个变量 `count` 记录当前总和,用一个变量 `max` 记录最大总和。
- `if (count > max) max = count`
- `if (count < 0) count = 0`
## [122. 买卖股票的最佳时机 II](https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/)
> You are given an integer array `prices` where `prices[i]` is the price of a given stock on the `ith` day.
>
> On each day, you may decide to buy and/or sell the stock. You can only hold **at most one share** of the stock at any time. However, you can buy it then immediately sell it on the **same day**.
>
> Find and return the **_maximum_** profit you can achieve.
>
> ```text
> Input: prices = [7,1,5,3,6,4]
> Output: 7
> Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4.
> Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3.
> Total profit is 4 + 3 = 7.
> ```
全局目标:总利润最大
局部目标:最长上升区间
过程:找到每个最长上升区间,将每个上升区间的利润加起来就是总利润了。
## [55. 跳跃游戏](https://leetcode.cn/problems/jump-game/)
> You are given an integer array `nums`. You are initially positioned at the array's **first index**, and each element in the array represents your maximum jump length at that position.
>
> Return `true` _if you can reach the last index_, or `false` _otherwise_.
>
> ```text
> Input: nums = [2,3,1,1,4]
> Output: true
> Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
> ```
全局最优:找到最长覆盖距离
局部最优:找到当前区间的最长覆盖距离
过程:一开始第一个元素的值就是初始覆盖区间的长度,遍历这个区间的所有元素,看看能不能找到能延长当前区间覆盖距离的元素,并延长区间覆盖距离,这样一直迭代,看它能不能到达数组末尾。
## [45. 跳跃游戏 II](https://leetcode.cn/problems/jump-game-ii/)
> You are given a **0-indexed** array of integers `nums` of length `n`. You are initially positioned at `nums[0]`.
>
> Each element `nums[i]` represents the maximum length of a forward jump from index `i`. In other words, if you are at `nums[i]`, you can jump to any `nums[i + j]` where:
>
> - `0 <= j <= nums[i]` and
> - `i + j < n`
>
> Return _the minimum number of jumps to reach_ `nums[n - 1]`. The test cases are generated such that you can reach `nums[n - 1]`.
全局最优:找到最短步数达到最长覆盖距离
局部最优:找到当前覆盖区间的元素,使得能最大程度地延长当前覆盖范围。
过程:一开始第一个元素的值就是初始覆盖区间的长度,遍历这个区间的所有元素,找到这样一个元素,它能够最大程度地延长当前覆盖区间,这个元素就是下一跳。在下一个覆盖区间中,再找到同样的元素,这样一直迭代。
## [452. 用最少数量的箭引爆气球](https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/)
全局:
- 限制:射完全部气球
- 目标:消耗的箭的数量最少
局部:
- 限制:射箭的范围是当前气球所在的范围
- 目标:这一箭要射穿的气球数量达到最大
算法优化:先对左边界(或右边界)进行排序,以降低时间复杂度。
## [435. 无重叠区间](https://leetcode.cn/problems/non-overlapping-intervals/)
从左向右记录非交叉区间的个数,用区间总数减去非交叉区间的个数就是需要移除的区间个数了。问题就是要求非交叉区间的最大个数。
先按右边界进行排序。
局部最优:优先选右边界小的区间,所以从左向右遍历,留给下一个区间的空间大一些,从而尽量避免交叉。
全局最优:选取最多的非交叉区间。
## [56. 合并区间](https://leetcode.cn/problems/merge-intervals/)
先按左边界排序。
局部最优:当前区间能合并出的最长区间
全局最优:合并所有重叠区间
## [968. 监控二叉树](https://leetcode.cn/problems/binary-tree-cameras/)
局部最优:让叶子节点的父节点安摄像头,所用摄像头最少
全局最优:全部摄像头数量所用最少