题目概览
n个孩子站成一排。给你一个整数数组ratings表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
- 每个孩子至少分配到
1个糖果。 - 相邻两个孩子中,评分更高的那个会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的最少糖果数目。
示例 1:
输入:ratings = [1,0,2] 输出:5 解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。
示例 2:
输入:ratings = [1,2,2] 输出:4 解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。 第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。
提示:
n == ratings.length1 <= n <= 2 * 10^40 <= ratings[i] <= 2 * 10^4
来源:135. 分发糖果 - 力扣(LeetCode)
解题分析
方法:两次遍历
本题的核心要求是:每个孩子至少 1 颗糖,且相邻孩子中评分更高的必须获得更多糖果。为了用最少的糖果满足这两个条件,我们可以采用「两次遍历」的策略,分别处理从左到右和从右到左的递增关系。
思路解析
如果只考虑「左邻居」,规则很简单:
- 若当前孩子评分比左边高,则他的糖果数应比左边多 1。
- 若当前孩子评分不高于左边,则他最少可以只拿 1 颗糖(因为右边还没考虑)。
同理,如果只考虑「右邻居」:
- 若当前孩子评分比右边高,则他的糖果数应比右边多 1。
- 否则,他可以只拿 1 颗糖。
但题目要求同时满足左右两边的约束,因此每个孩子最终的糖果数应取上述两个方向计算结果的最大值,这样才能同时保证比左边高时足够多、比右边高时也足够多。
算法步骤
- 第一次遍历(从左到右):初始化数组
left,令left[0] = 1。遍历i = 1 → n-1:- 若
ratings[i] > ratings[i-1],则left[i] = left[i-1] + 1(保证比左边多)。 - 否则,
left[i] = 1(先给最少,右边遍历时会再调整)。
- 若
- 第二次遍历(从右到左):初始化变量
right = 1(最后一个孩子的右向糖果数),总糖果数sum = left[n-1]。遍历i = n-2 → 0:- 若
ratings[i] > ratings[i+1],则right = right + 1(保证比右边多)。 - 否则,
right = 1(重置为最少)。 - 此时当前孩子应得的糖果数为
max(left[i], right),将其累加到sum。
- 若
- 返回
sum。
代码实现(Java)
class Solution { public int candy(int[] ratings) { int n = ratings.length; int[] left = new int[n]; // 从左到右遍历 left[0] = 1; for (int i = 1; i < n; i++) { if (ratings[i] > ratings[i - 1]) { left[i] = left[i - 1] + 1; } else { left[i] = 1; } } // 从右到左遍历并累加 int right = 1; int sum = left[n - 1]; // 最后一个孩子的糖果数 for (int i = n - 2; i >= 0; i--) { if (ratings[i] > ratings[i + 1]) { right++; } else { right = 1; } sum += Math.max(left[i], right); } return sum; } }复杂度分析
- 时间复杂度:O(n),仅需两次线性遍历。
- 空间复杂度:O(n),用于存储左向糖果数组
left。若优化为 O(1) 空间,可将第二次遍历直接与第一次结合,但代码会稍复杂。
示例推演
以ratings = [1,0,2]为例:
- 左向遍历得
left = [1,1,2]。 - 右向遍历时:
- i=2(评分 2):
right=1,sum=left[2]=2。 - i=1(评分 0):因为 0 > 2?否,
right=1,max(left[1]=1, right=1)=1,sum=2+1=3。 - i=0(评分 1):因为 1 > 0?是,
right=2,max(left[0]=1, right=2)=2,sum=3+2=5。
- i=2(评分 2):
- 最终结果 5,与题目输出一致。
该方法保证了每个孩子既满足左邻约束(通过left数组),又满足右邻约束(通过动态的right变量),且取最大值后即为满足双边条件的最小糖果数。