一、算法解释
贪心算法或贪心思想采用贪心的策略,保证每次操作都是局部最优的,从而使最后得到的结果是全局最优的。
二、题型
2.1 分配问题
455. 分发饼干
饥饿度最小的最容易吃饱,将饥饿度从小到大排序,然后饼干也从小到大排序。
456. 分发糖果
糖果问题,一群孩子站成一排,每一个孩子有自己的评分。现在需要给这些孩子发糖果,规则是如果一个孩子的评分比自己身旁的一个孩子要高,那么这个孩子就必须得到比身旁孩子更多的糖果;所有孩子至少要有一个糖果。求解最少需要多少个糖果。
思路:从左到右遍历一遍,再从右到左遍历一遍,每次比较分数和糖果数目(如果分数一样,怎么决定呢~~~~)
2.2 区间问题
435. 无重叠区间问题
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
可以认为区间的终点总是大于它的起点。
区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
思路:
我们主要关注区间的结尾,区间的结尾越小,留给其他区间的空间就越大,将区间按结尾排序。每次比较下个区间的开始和前一个区间的结尾即可。
三、练习
605. 种花问题
假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成,其中 0 表示没种植花,1 表示种植了花。另有一个数 n ,能否在不打破种植规则的情况下种入 n 朵花?能则返回 true ,不能则返回 false。
输入:flowerbed = [1,0,0,0,1], n = 1
输出:true
解题思路:
- 设一个记录能种的花的数目count。对flowered列表先前后补0,解决边界问题,然后每次判断前一个和后一个是否都为0,是的话就赋值为1,并且count加1。
class Solution:
def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
flowerbed = [0] + flowerbed + [0]
count = 0
for i in range(1, len(flowerbed) - 1):
if flowerbed[i] == 0:
if flowerbed[i - 1] == 0 and flowerbed[i + 1] == 0:
flowerbed[i] = 1
count += 1
return count >= n
思路二,设置变量记录0的数目和能种的花的数目。如果有连续的三个0,这说明中间能种一棵花,这时候0的数目从1开始。
class Solution:
def canPlaceFlowers(self, flowerbed: List[int], n: int) -> bool:
zero_count, count = 1, 0
for i in range(len(flowerbed)):
if flowerbed[i] == 0:
zero_count += 1
if zero_count == 3:
flowerbed[i-1] = 1
zero_count = 1
count += 1
else:
zero_count = 0
if zero_count == 2:
count += 1
return count >= n
452. 用最少数量的箭引爆气球
思路:这道题有点像无重叠区间。将区间按右端点从低往高排序,每次比较后一个区间的左端点和前一个区间的右端点,如果右端点小于等于左端点,那么可以用同一根箭引爆,这个时候删除列表里的后一个区间,继续比较。
思路二,也可以比较左端点,从低到高排序,这个时候端点的判断要用min函数,不能跟上面一样直接赋值,因为可能出现[[1,10],[3,9],…],这种情况。这个时候重合区间是[3,9]。而如果按右端点排序,那就是[[3,9],[1,10],…],就可以直接删除[1,10],继续后面的比较。
class Solution:
def findMinArrowShots(self, points: List[List[int]]) -> int:
points = sorted(points, key = lambda x: x[0])
points.sort()
i, st = 0, 0
times = len(points) - 1
while i < times:
if points[st + 1][0] <= points[st][1]:
# points[st + 1][1] = points[st][1]
points[st + 1][1] = min(points[st][1], points[st+1][1])
del points[st]
else:
st += 1
i += 1
return len(points)
763 划分字母区间
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
思路:先记录下每个字母出现的最后位置(可以用字典存储)。然后两个指针,一个指向区间开始,一个指向结束。后面遍历字母,对于每个出现的字母,结束指针跑到该字母出现的最后位置,如果遍历到的位置刚好是结束指针的位置,说明前面可以划分为一个区间。
class Solution:
def partitionLabels(self, s: str) -> List[int]:
saveDict = {}
for i, char in enumerate(s):
saveDict[char] = i
# print(saveDict)
st, end = 0, 0
splitList = []
for i, char in enumerate(s):
end = max(end, saveDict[char])
if i == end:
splitList.append(end - st + 1)
st = end + 1
# print(splitList)
return splitList
#### 406 根据身高重建队列
假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。
输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
先将身高从高往低排序,再将前面出现的人从低到高排序。
接下来执行插入操作,因为
k
i
k_i
ki表示前面升高大于等于
h
i
h_i
hi的人数,身高从高往低排序,后面插入的人不会对前面产生影响。
class Solution:
def reconstructQueue(self, people: List[List[int]]) -> List[List[int]]:
people.sort(key = lambda x: (-x[0], x[1]))
res = []
for p in people:
res.insert(p[1], p)
return res