贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
贪心策略适用的前提是**:局部最优策略能导致产生全局最优解**。
相关笔记:贪心算法及LeetCode中相关问题
一、分配问题
有一群孩子和一堆饼干,每个孩子有一个饥饿度,每个饼干都有一个大小。每个孩子只能吃 最多一个饼干,且只有饼干的大小大于孩子的饥饿度时,这个孩子才能吃饱。求解最多有多少孩 子可以吃饱。
输入两个数组,分别代表孩子的饥饿度和饼干的大小。输出最多有多少孩子可以吃饱的数 量。
Input: [1,2], [1,2,3]
Output: 2
-
贪心策略:给剩余孩子里最小饥饿度的孩子分配最小的能饱腹的饼干
-
两个数组分别排序,然后遍历
-
while (child < children.size() && cookie < cookies.size()) { if (children[child] <= cookies[cookie]) ++child; ++cookie; } return child;
一群孩子站成一排,每一个孩子有自己的评分。现在需要给这些孩子发糖果,规则是如果一 个孩子的评分比自己身旁的一个孩子要高,那么这个孩子就必须得到比身旁孩子更多的糖果;所 有孩子至少要有一个糖果。求解最少需要多少个糖果。
输入是一个数组,表示孩子的评分。输出是最少糖果的数量。
Input: [1,0,2]
Output: 5
- 首先要准确理解题意:2,4,4的情况最少发的糖果是1,2,1
- 不需要排序
- 贪心策略:每次只检查一侧的大小情况(在每次遍历中只考虑并更新相邻一侧的大小关系。 )
- 不一定所有的贪心策略都是**”优先选择“,也可以是”只处理/先处理“**
- –> <–两次遍历
二、区间问题/间隔调度
435.不重叠区间
给定多个区间,计算让这些区间互不重叠所需要移除区间的最少个数。起止相连不算重叠。
输入是一个数组,数组由多个长度固定为 2 的数组组成,表示区间的开始和结尾。输出一个 整数,表示需要移除的区间数量。
Input: [[1,2], [2,4], [1,3]]
Output: 1
-
重叠产生的原因:end值大的区间的start值 < end值小的区间的end值
-
排序:按end值排序(降低对后面的区间的start值的“要求”)
-
多维数组自定义排序
-
class myComparator implements Comparator<int []>{ public int compare(int[] a,int[] b){ return a[1]-b[1]; } } //int[][]的元素类型为int[]
-
-
贪心策略:优先选择不重叠的,end值小的区间
- 用最少数量的箭引爆气球 TO DO
在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以纵坐标并不重要,因此只要知道开始和结束的横坐标就足够了。开始坐标总是小于结束坐标。
一支弓箭可以沿着 x 轴从不同点完全垂直地射出。在坐标 x 处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。
给你一个数组 points ,其中 points [i] = [xstart,xend] ,返回引爆所有气球所必须射出的最小弓箭数。
Input: points = [[10,16],[2,8],[1,6],[7,12]]
Output: 2
解释:对于该样例,x = 6 可以射爆 [2,8],[1,6] 两个气球,以及 x = 11 射爆另外两个气球
三、其他
字符串
S
由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。Input: S = “ababcbacadefegdehijhklij”
Output:[9,7,8]
解释:
划分结果为 “ababcbaca”, “defegde”, “hijhklij”。
每个字母最多出现在一个片段中。
像 “ababcbacadefegde”, “hijhklij” 的划分是错误的,因为划分的片段数较少
- 使用贪心策略之前,可能需要进行一些预处理
- 在处理数组前,统计一遍信息(如频率、个数、第一次出现位置、最后一次出现位置等)可以使题目难度大幅降低 -----
lastIndex[]
存储每个字母最后出现的位置
- 在处理数组前,统计一遍信息(如频率、个数、第一次出现位置、最后一次出现位置等)可以使题目难度大幅降低 -----
- 贪心策略:不断选择更大的last index
给你一个长度为 n 的整数数组,请你判断在最多改变 1 个元素的情况下,该数组能否变成一个非递减数列。
我们是这样定义一个非递减数列的: 对于数组中所有的 i (0 <= i <= n-2),总满足 nums[i] <= nums[i + 1]。
Input: nums = [4,2,3]
Output: true
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。
- 题意理解(结合例子)
- 重点在于:“改变一个元素”:改大or改小?改谁?
- 4 2 3: 2<4 不满足,要把4改小(改nums[i-1])
- 3 1 5: 1<3 不满足,要把1改大i(改nums[i])
- 贪心策略:进行更改时,选择可以使当前三个元素(i-2,i-1,i)满足非递减关系的更改方案
- 重点在于:“改变一个元素”:改大or改小?改谁?
- 根据身高重建队列 TO DO
假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个
people[i] = [hi, ki]
表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中
queue[j] = [hj, kj]
是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。Input: people = [[6,0],[5,0],[4,0],[3,2],[2,2],[1,4]]
Output:[[4,0],[5,0],[2,2],[3,2],[1,4],[6,0]]