在许多编程面试中,经常会遇到各种算法题目,其中"分发糖果"问题是一个非常典型且有趣的问题。这个问题不仅考察了基本的编程能力,还涉及到了贪心算法的思想。今天,我们将深入探讨这个问题,包括其问题描述、解题思路、代码实现以及相关的学习点。
问题描述
"分发糖果"问题的描述如下:一群孩子站成一排,每个孩子有一个评分。你需要按照以下要求,给这些孩子分发糖果:
- 每个孩子至少分到1颗糖果。
- 评分更高的孩子必须比他们相邻的评分低的孩子获得更多的糖果。
你需要计算出最少需要准备多少颗糖果。
题目链接:. - 力扣(LeetCode)
解题思路
这个问题可以通过贪心算法来解决。核心思想是两次遍历评分数组ratings
:
- 从左向右遍历:确保每个孩子与其左边相邻的孩子相比,如果评分更高,则糖果数也要更多。
- 从右向左遍历:这次确保每个孩子与其右边相邻的孩子相比,如果评分更高,则糖果数也要更多。
最后,对于每个孩子,我们取其左右两边规则计算出的糖果数的较大值,这样就能满足题目的所有要求。
代码实现
复杂度分析:
时间复杂度 O(N)O(N)O(N) : 遍历两遍数组即可得到结果;
空间复杂度 O(N)O(N)O(N) : 需要借用 left,right 的线性额外空间。
class Solution:
def candy(self, ratings: List[int]) -> int:
left = [1 for _ in range(len(ratings))]
right = left[:] # 不可写为right=left, 不然相当于C++中的引用,即right会自动随left改变而改变
#初始化left和right
for i in range(1, len(ratings)):
if ratings[i]>ratings[i-1]:
left[i]=left[i-1]+1
for i in range(len(ratings)-2, -1, -1):
if ratings[i]>ratings[i+1]:
right[i]=right[i+1]+1
# 初始化ans, 即每个小盆友对应的最小糖果数,应同时满足left与right,所以取max(left[i], right[i])
ans = []
for i in range(len(right)):
ans.append(max(right[i], left[i]))
# 返回所有糖果和
return sum(ans)
学习点
通过这个问题,我们可以学习到:
- 贪心算法的基本应用:通过局部最优解达到全局最优解。
- 列表的浅拷贝:
right = left[:]
确保了right
和left
是两个独立的列表,修改一个不会影响另一个。 - 逆向遍历:通过
range(len(ratings) - 2, -1, -1)
实现从右向左的遍历。 - 综合应用:综合使用以上技巧和思路来解决实际问题。
结论
"分发糖果"问题不仅是一个有趣的编程挑战,也是一个很好的学习机会,它帮助我们理解和掌握了贪心算法的实际应用,以及Python编程中的一些重要概念。希望通过这篇博客,你能对这个问题有更深的理解和认识
详细题解:. - 力扣(LeetCode)