跟随carl代码随想录刷题
语言:python
选择每一阶段的局部最优,从而达到全局最优。
如:每次都拿走面额最大的钱,最终就可以拿到最多的钱。
举反例!如果想不到反例,那就试一试贪心。
贪心有时是常识性的推导,会认为本就应该这么做。
将问题分为若干子问题
找出适合的贪心策略
求解每一个子问题的最优解
将局部最优解堆叠成全局最优解
简单
分发饼干题目:假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,
每个孩子最多只能给一块饼干
。
对每个孩子 i,都有一个胃口值 g[i]
,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j,都有一个尺寸 s[j]
。如果s[j] >= g[i]
,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。
👉示例 1:
输入: g = [1,2,3], s = [1,1]
输出: 1
解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。
👉示例 2:
输入: g = [1,2], s = [1,2,3]
输出: 2
解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.
思路1:
思路2:
index
控制饼干数组的遍历。class Solution:
def findContentChildren(self, g: List[int], s: List[int]) -> int:
g.sort()
s.sort()
res = 0
for i in range(len(s)):
if res<len(g) and s[i] >= g[res]: # 小饼干先喂饱小胃口
res += 1
return res
class Solution:
def findContentChildren(self, g: List[int], s: List[int]) -> int:
g.sort()
s.sort()
res = 0
start = len(s) - 1
for i in range(len(g)-1, -1, -1):
if start >= 0 and s[start] >= g[i]: # 大饼干先喂饱大胃口
res += 1
start -= 1
return res
简单
柠檬水找零题目:在柠檬水摊上,
每一杯柠檬水的售价为 5 美元
。顾客排队购买你的产品,(按账单 bills 支付的顺序)一次购买一杯。
每位顾客只买一杯柠檬水,然后向你付 5 美元、10 美元或 20 美元。你必须给每个顾客正确找零,也就是说净交易是每位顾客向你支付 5 美元。
注意,一开始你手头没有任何零钱。
给你一个整数数组 bills ,其中 bills[i] 是第 i 位顾客付的账
。如果你能给每位顾客正确找零,返回 true
,否则返回 false
。
👉示例 1:
输入:bills = [5,5,5,10,20]
输出:true
解释:
前 3 位顾客那里,我们按顺序收取 3 张 5 美元的钞票。
第 4 位顾客那里,我们收取一张 10 美元的钞票,并返还 5 美元。
第 5 位顾客那里,我们找还一张 10 美元的钞票和一张 5 美元的钞票。
由于所有客户都得到了正确的找零,所以我们输出 true。
👉示例 2:
输入:bills = [5,5,10,10,20]
输出:false
解释:
前 2 位顾客那里,我们按顺序收取 2 张 5 美元的钞票。
对于接下来的 2 位顾客,我们收取一张 10 美元的钞票,然后返还 5 美元。
对于最后一位顾客,我们无法退回 15 美元,因为我们现在只有两张 10 美元的钞票。
由于不是每位顾客都得到了正确的找零,所以答案是 false。
把能遇到的情况分析一下:
10
美元可以给20
找零
5
美元可以给10
和20
找零。所以,5
美元更万能。
class Solution:
def lemonadeChange(self, bills: List[int]) -> bool:
five_ = 0
ten_ = 0
twenty_ = 0
for i in range(len(bills)):
if bills[i] == 5: # 如果顾客付的是5块
five_ += 1 # 直接收
elif bills[i] == 10: # 如果顾客付的是10块
if five_ > 0: # 把10块收起来,并找一个5块
ten_ += 1
five_ -= 1
else:
return False
elif bills[i] == 20: # 如果顾客付的是20块
if ten_ > 0 and five_ > 0: # 如果有5块也有10块,优先找1个10块、1个五块,并把20块收起来
ten_ -= 1
five_ -= 1
twenty_ += 1
elif five_ > 2: # 否则,如果有五块,那就找3个五块
five_ -= 3
twenty_ += 1
else:
return False
return True
简单
K 次取反后最大化的数组和题目:给你一个整数数组 nums 和一个整数 k ,按以下方法修改该数组:
选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。
重复这个过程恰好 k 次。可以多次选择同一个下标 i 。
以这种方式修改数组后,返回数组 可能的最大和 。
👉示例 1:
输入:nums = [4,2,3], k = 1
输出:5
解释:选择下标 1 ,nums 变为 [4,-2,3] 。
👉示例 2:
输入:nums = [3,-1,0,2], k = 3
输出:6
解释:选择下标 (1, 2, 2) ,nums 变为 [3,1,0,2] 。
👉示例 3:
输入:nums = [2,-3,-1,5,-4], k = 2
输出:13
解释:选择下标 (1, 4) ,nums 变为 [2,3,-1,5,4] 。
局部最优:每次都只改变最小值的符号
全局最优:这样就可以得到最大值。
nums
按绝对值从大到小排列A = sorted(nums, key=abs, reverse=True)
我自己写出来的呢😄
class Solution:
def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:
while k>0:
nums.sort()
nums[0] = -nums[0]
k -= 1
sum_ = 0
for i in range(len(nums)):
sum_ += nums[i]
return sum_
数组元素求和
可以直接sum(数组名)
class Solution:
def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:
while k>0:
nums.sort()
nums[0] = -nums[0]
k -= 1
return sum(nums)
class Solution:
def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:
# 将nums按绝对值从大到小排列
A = sorted(nums, key=abs, reverse=True)
for i in range(len(A)):
if k > 0 and A[i] < 0:
A[i] = -A[i]
k -= 1
if k > 0:
A[-1] *= (-1)**k
return sum(A)
最后K的判断,只需用判断K的奇偶即可。如果是奇数,那A[-1] = -A[1]
,否则不操作。
class Solution:
def largestSumAfterKNegations(self, nums: List[int], k: int) -> int:
# 将nums按绝对值从大到小排列
A = sorted(nums, key=abs, reverse=True)
for i in range(len(A)):
if k > 0 and A[i] < 0:
A[i] = -A[i]
k -= 1
if k > 0:
if k % 2 == 1:
A[-1] = - A[-1]
return sum(A)
中等
摆动序列题目:如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。
例如, [1, 7, 4, 9, 2, 5] 是一个 摆动序列 ,因为差值 (6, -3, 5, -7, 3) 是正负交替出现的。
相反,[1, 4, 7, 2, 5] 和 [1, 7, 4, 5, 5] 不是摆动序列,第一个序列是因为它的前两个差值都是正数,第二个序列是因为它的最后一个差值为零。
子序列 可以通过从原始序列中删除一些(也可以不删除)元素来获得,剩下的元素保持其原始顺序。
给你一个整数数组 nums
,返回nums
中作为摆动序列
的最长子序列的长度
。
题目要求:返回nums中作为 摆动序列
的 最长子序列的长度
思路:转化为求数列中的拐点数目
拐点
,即当前差值curC = nums[i] - nums[i-1]
与前一个差值preC = nums[i-1] - nums[i-2]
是否异号,即:乘积是否<0
。
乘积是否 <= 0 and 当前差值不为0
class Solution:
def wiggleMaxLength(self, nums: List[int]) -> int:
preC, curC, res = 0, 0, 1
for i in range(len(nums)-1):
curC = nums[i + 1] - nums[i]
# 当前差值 与 前一个差值 异号,并且当前差值不为0时
if curC * preC <= 0 and curC != 0: # 差值为0时不算摆动。
res += 1 # 则拐点数量+1
preC = curC # 向后遍历,把当前差值 作为 前一个差值
return res # 返回拐点数量
题目:给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
子数组 是数组中的一个连续部分。
👉示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
👉示例 2:
输入:nums = [1]
输出:1
👉示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
当连续和
为负数的时候,立刻放弃。因为负数只会拉低总和。因此
for i in range(len(nums)):
# 遍历数组
count += nums[i]
# 计数连续和
大于目前的结果result:count > result
,就更新result:result = count
nums[i]
开始计数的连续和
为负数时count < 0
,放弃此次计数:count = 0
,然后从下一个元素numd[i+1]
重新计算连续和
。全局最优:选取最大连续和。return result
思路陷阱:
连续和
为负数连续和
加上负数class Solution:
def maxSubArray(self, nums: List[int]) -> int:
result = -float('inf')
count = 0
for i in range(len(nums)):
count += nums[i]
if count > result: # 如果count比result大,就更新result
result = count
if count <= 0: # `<=`或`<`都可以。`连续和`为负数,直接放弃,开启下一个元素的计数
count = 0
return result
中等
买卖股票的最佳时机 II题目:给你一个整数数组
prices
,其中prices[i] 表示某支股票第 i 天的价格
。
在每一天,你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票
。你也可以先购买,然后在 同一天 出售。
返回 你能获得的 最大 利润
。
👉示例 1:
输入:prices = [7,1,5,3,6,4]
输出:7
解释:在第 2 天(股票价格 =1
)的时候买入,在第 3 天(股票价格 =5
)的时候卖出, 这笔交易所能获得利润 = 5 - 1 = 4
。
随后,在第 4 天(股票价格 =3
)的时候买入,在第 5 天(股票价格 =6
)的时候卖出, 这笔交易所能获得利润 =6 - 3 = 3
。
总利润为 4 + 3 = 7
。
👉示例 2:
输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 =1
)的时候买入,在第5
天 (股票价格 =5
)的时候卖出, 这笔交易所能获得利润 =5 - 1 = 4
。
总利润为 4
。
👉示例 3:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 交易无法获得正利润,所以不参与交易
可以获得最大利润,最大利润为 0
。
价格数组 -> 利润数组 -> 贪心法则:每天只收取正利润
局部最优:收集每天的正利润
全局最优:得到最大利润
class Solution:
def maxProfit(self, prices: List[int]) -> int:
count = 0
for i in range(len(prices)-1):
count += max(prices[i+1] - prices[i], 0)
return count
中等
买卖股票的最佳时机含手续费题目:给定一个
整数数组 prices
,其中prices[i]表示第 i 天的股票价格
;整数 fee 代表了交易股票的手续费用
。
你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值
。
注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
👉示例 1:
输入:prices = [1, 3, 2, 8, 4, 9], fee = 2
输出:8
解释:能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8
👉示例 2:
输入:prices = [1,3,7,5,10,3], fee = 3
输出:6
class Solution:
def maxProfit(self, prices: List[int], fee: int) -> int:
result = 0
minPrice = prices[0]
for i in range(1, len(prices)):
if prices[i] < minPrice:
minPrice = prices[i]
elif prices[i] >= minPrice and prices[i] <= minPrice + fee:
continue
else:
result += prices[i] - minPrice - fee
minPrice = prices[i] - fee
return result