1. 问题背景与核心逻辑
分糖果问题是一个经典的数组操作题目,通常描述为:老师要给孩子们分发糖果,每个孩子有一个评分值存储在数组中。分配规则要求:
- 每个孩子至少分到1颗糖
- 相邻孩子中评分更高的必须获得更多糖果
- 糖果总数要尽可能少
这个问题考察的核心能力是对数组的双向遍历处理,以及贪心算法的实际应用。我在实际面试辅导中发现,90%的初级开发者会陷入单向遍历的思维陷阱。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础解法实现
2.1 双向遍历法
最可靠的解法需要左右各遍历一次数组:
python复制def candy(ratings):
n = len(ratings)
left = [1] * n
# 从左向右遍历
for i in range(1, n):
if ratings[i] > ratings[i-1]:
left[i] = left[i-1] + 1
right = 1
total = left[-1]
# 从右向左遍历
for i in range(n-2, -1, -1):
if ratings[i] > ratings[i+1]:
right += 1
else:
right = 1
total += max(left[i], right)
return total
关键点:第二次遍历时需要用max比较左右遍历结果,这个细节决定了算法的正确性
2.2 时间复杂度分析
- 时间复杂度:O(n) 两次线性遍历
- 空间复杂度:O(n) 存储左遍历结果
- 最优解空间复杂度可优化到O(1),但会牺牲代码可读性
3. 常见错误模式
3.1 单向遍历陷阱
很多开发者会尝试用单次遍历解决:
python复制# 错误示范
def wrong_candy(ratings):
candy = [1] * len(ratings)
for i in range(1, len(ratings)):
if ratings[i] > ratings[i-1]:
candy[i] = candy[i-1] + 1
return sum(candy)
这种解法会失败在类似[1,3,2,1]的测试用例上,因为无法处理右邻比左邻小的情况。
3.2 谷底处理不当
对于连续递减序列如[5,4,3,2,1],需要确保每次递减都增加糖果数。常见错误是只在变化时调整:
python复制# 另一个错误示范
for i in range(1, n):
if ratings[i] > ratings[i-1]:
candy[i] = candy[i-1] + 1
elif ratings[i] < ratings[i-1]: # 这样处理不够
candy[i] = candy[i-1] - 1
4. 优化技巧
4.1 空间优化版
将空间复杂度降到O(1)的写法:
python复制def candy_optimized(ratings):
if not ratings:
return 0
n = len(ratings)
total = 1
up = down = peak = 1
for i in range(1, n):
if ratings[i] > ratings[i-1]:
up += 1
down = 1
peak = up
total += up
elif ratings[i] == ratings[i-1]:
up = down = peak = 1
total += 1
else:
up = 1
down += 1
total += down
if down > peak: # 关键修正点
total += 1
return total
4.2 实际应用变种
在实际开发中可能会遇到这些变种:
- 允许评分相同的孩子获得不同糖果数(解除严格单调限制)
- 每个孩子有最小和最大糖果限制
- 糖果总数不超过某个阈值时的最优分配
5. 测试用例设计
完整的测试应该包含这些边界情况:
python复制test_cases = [
([1,2,2], 4), # 基础案例
([1,3,2,1], 7), # 波峰波谷
([5,4,3,2,1], 15), # 单调递减
([1,2,3,4,5], 15), # 单调递增
([1], 1), # 单元素
([1,1,1,1], 4), # 全等值
([1,3,4,5,2], 9) # 复杂波形
]
6. 算法选择思考
为什么贪心算法适合这个问题?
- 全局最优解可以通过局部最优解组合得到
- 无后效性:当前分配决策不影响之前的结果
- 子问题相互独立:每个孩子的糖果数只与直接相邻孩子有关
对比动态规划解法:
虽然可以用DP表格存储中间状态,但会带来不必要的空间开销。贪心算法在这里更高效。
7. 工程实践建议
-
在LeetCode等平台提交时,注意输入为空数组的情况
-
实际业务中如果孩子数量极大(>1e6),可以考虑并行化处理:
- 将数组分段
- 每段单独计算
- 合并时处理分段边界
-
内存优化技巧:
python复制# 用生成器替代列表存储
def candy_stream(ratings):
n = len(ratings)
if n == 0: yield 0
last = 1
yield last
for i in range(1, n):
if ratings[i] > ratings[i-1]:
last += 1
else:
last = 1
yield last
8. 扩展思考
这个问题可以延伸到:
- 环形数组版本(首尾孩子也相邻)
- 多维版本(教室座位排列)
- 带权版本(不同孩子糖果成本不同)
我在实际项目中遇到过类似的资源分配问题,比如:
- 服务器负载均衡
- 广告预算分配
- 任务调度优先级
关键思路都是:在满足约束条件下,通过局部最优达到全局最优。
