贪心算法
贪心选择:
通过一系列的局部最优解达到整体最优解。前提:
必须证明贪心选择可以达到最优解:先证明整体最优解是从贪心选择开始的,然后做了贪心选择后问题可以简化成子问题,最后用数学归纳法证明通过每一步的贪心选择最终可以得到一个最优解。做法:
从顶向下,迭代把问题简化成小规模的子问题。
从问题的某一个初始解出发一步一步地进行,根据某个优化测度,每一步都要确保能获得局部最优解。每一步只考虑一个数据,他的选取应该满足局部优化的条件。若下一个数据和部分最优解连在一起不再是可行解时,就不把该数据添加到部分解中,直到把所有数据枚举完,或者不能再添加算法停止-
过程
- 建立数学模型来描述问题;
- 把求解的问题分成若干个子问题;
- 对每一子问题求解,得到子问题的局部最优解;
- 把子问题的解局部最优解合成原来解问题的一个解。
最优子结构:
当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。-
贪心/动态规划
-
贪心算法的每一次操作都对结果产生直接影响,
动态规划则不是。 -
贪心算法对每个子问题的解决方案都做出选择,不能回退;
动态规划则会根据以前的选择结果对当前进行选择,有回退功能。 -
贪心一般是一维问题,
动态规划主要运用于二维或三维问题。
-
贪心算法的每一次操作都对结果产生直接影响,
1221. 分割平衡字符串
在一个「平衡字符串」中,'L' 和 'R' 字符的数量是相同的。
给出一个平衡字符串 s,请你将它分割成尽可能多的平衡字符串。
返回可以通过分割得到的平衡字符串的最大数量。
1 <= s.length <= 1000
s[i] = 'L' 或 'R'
输入:s = "RLRRLLRLRL"
输出:4
解释:s 可以分割为 "RL", "RRLL", "RL", "RL", 每个子字符串中都包含相同数量的 'L' 和 'R'。
- 匹配
class Solution:
def balancedStringSplit(self, s: str) -> int:
balance = 0
res = 0
for i in s:
if i == 'R':
balance += 1
elif i == 'L':
balance -= 1
if balance == 0:
res += 1
return res
- 栈
初始入栈一个数据,后续如果发现与栈顶数据一致,则入栈,不一致则出栈
直接判断栈中数据是否为0,为0则说明成功匹配一个数据,此时平衡字符串数量+1
class Solution:
def balancedStringSplit(self, s: str) -> int:
# 通过队列操作,当栈中数量减到0时 + 1
result = 0
stack = []
for x in s:
if len(stack) > 0:
stack.append(x) if stack[0] == x else stack.pop()
result += 1 if len(stack) == 0 else 0
else:
stack.append(x)
return result
455. 分发饼干
假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
注意:
你可以假设胃口值为正。
一个小朋友最多只能拥有一块饼干。
输入: [1,2,3], [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
排序然后逐个比较,先让胃口小的满足
class Solution:
def findContentChildren(self, g: List[int], s: List[int]) -> int:
# 胃口g list g[i] > 0
# 尺寸s list
# 一人一块
# 输出最多能满足多少
g.sort()
s.sort()
res = 0
for gg in g:
for ss in s:
if gg <= ss:
res += 1
s.remove(ss)
break
return res
用指针优化, 降低复杂度
class Solution:
def findContentChildren(self, g: List[int], s: List[int]) -> int:
# 胃口g list g[i] > 0
# 尺寸s list
# 一人一块
# 输出最多能满足多少
g.sort()
s.sort()
gi = 0
si = 0
while gi < len(g) and si < len(s):
if g[gi] <= s[si]:
gi += 1
si += 1
else:
si += 1
return gi
# gi实际和被满足的人数一样
435. 无重叠区间
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
可以认为区间的终点总是大于它的起点。
区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
输入: [ [1,2], [2,3], [3,4], [1,3] ]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。
转换思路,从给出区间集合中最多能选几个不重叠的,剩下的就是需要去掉的
- 按结束时间升序排序
- 遍历判断当前区间是否满足开始的时间>=上次结束时间
- 每次选择结束时间最早的
- 更新结束时间
class Solution:
def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int:
if not intervals:
return 0
intervals = sorted(intervals, key=lambda x:x[1])
# intervals.sort(key=self.takeSecond)
picked = 0 # 选出的不重叠区间
end = - float('inf') # 结束初始值负无穷
for i in intervals:
if i[0] >= end:
picked += 1
end = i[1]
return len(interval) - picked
先升序排序是关键点!
321. 拼接最大数
Reference
[1] 牛客贪心算法
[3] leetcode 贪心算法