图解题库 / 贪心 · 双向扫描

135. 分发糖果

困难 含交互动画

学完这道你应该能: 说清它为什么用「双向扫描」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

题目描述

每人至少 1 颗糖;评分更高的要比相邻的拿更多。求最少糖果总数。

ratings = [1, 0, 2]
输出 = 5 (2,1,2)

思路解析

左扫只保证「比左边高的多 1」,右扫只保证「比右边高的多 1」,两次取最大值,两条规则就都满足了。

初始:每人 1 颗:先给每人 1 颗:candy = [1, 1, 1]。下面改的是糖果数,对照评分 [1, 0, 2]。

左扫 i=1:从左到右。i=1 评分 0 不比左边(1)高,糖果保持 1。

左扫 i=2:i=2 评分 2 比左边(0)高,糖果 = 左邻居 + 1 = 2。左扫完:[1, 1, 2]。

现在 candy=[1,1,2] 只保证了左规则。右边的约束还没管——所以得再从右往左扫一遍,用 max 合并。

右扫 i=1:现在从右到左。i=1 评分 0 不比右边(2)高,不变。

右扫 i=0:i=0 评分 1 比右边(0)高,糖果 = max(原值 1, 右邻居+1=2) = 2。取 max 是为了不破坏左扫的结果。

求和:最终 candy = [2, 1, 2],加起来 5 颗,就是最少糖果数。

一次只满足一边约束,最后合并——双向扫描是处理「左右都有约束」的好办法。

参考代码(Python)

Python
1candy = [1] * n
2for i in range(1, n):                 # 从左到右
3    if ratings[i] > ratings[i-1]:
4        candy[i] = candy[i-1] + 1
5for i in range(n-2, -1, -1):          # 从右到左
6    if ratings[i] > ratings[i+1]:
7        candy[i] = max(candy[i], candy[i+1] + 1)
8return sum(candy)

复杂度分析

  • 时间复杂度:O(n) —— 扫两遍
  • 空间复杂度:O(n) —— 一个糖果数组

套路模板

记住骨架:两遍方向相反、每遍只满足一边、第二遍用 max 合并。接雨水的「左右最高」预处理也是同一招。

Python
1# 「左右两边都有约束」的题都套
2a = [初值] * n
3for i in range(1, n):              # 左→右:只管左约束
4    if 左条件: a[i] = a[i-1] + 1
5for i in range(n-2, -1, -1):       # 右→左:只管右约束
6    if 右条件: a[i] = max(a[i], a[i+1] + 1)   # 取max不破坏左
7return sum(a)

易错点

  • 右扫直接覆盖 candy[i] → ✅ candy[i] = max(candy[i], candy[i+1]+1)(直接赋值会破坏左扫已满足的约束)
  • 右扫方向写错 → ✅ range(n-2, -1, -1)(从倒数第二个往前到 0)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →62. 不同路径 ← 返回题库