题目链接
题目描述
n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
- 每个孩子至少分配到
1个糖果。 - 相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
题目示例
示例 1 :
plain
输入:ratings = [1,0,2]
输出:5
解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。
示例 2 :
plain
输入:ratings = [1,2,2]
输出:4
解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。
解题思路
本题要求在满足两个条件的前提下,分配最少数量的糖果:
- 每个孩子至少一颗糖果。
- 评分高的孩子比相邻评分低的孩子获得更多糖果。
这意味着对于任何一个孩子 i,他获得的糖果数量 candy[i] 必须满足:
- 如果
ratings[i] > ratings[i-1],则candy[i] > candy[i-1]。 - 如果
ratings[i] > ratings[i+1],则candy[i] > candy[i+1]。
为了满足这些条件,同时使总糖果数最少,我们可以采用两次遍历的策略:
- 第一次遍历(从左向右):
- 我们首先满足每个孩子相对于其左侧邻居的条件。
- 创建一个
left数组,left[i]表示在只考虑左侧邻居的情况下,孩子i应得的糖果数。 - 初始化
left数组所有元素为1(每个孩子至少一颗糖果)。 - 从索引
1开始遍历到n-1:- 如果
ratings[i] > ratings[i-1],则left[i] = left[i-1] + 1。这意味着当前孩子比左侧邻居多一颗糖果。 - 否则 (
ratings[i] <= ratings[i-1]),left[i]保持为1(因为他不需要比左边多,所以最少是1颗)。
- 如果
- 第二次遍历(从右向左):
- 接下来,我们满足每个孩子相对于其右侧邻居的条件。
- 创建一个
right数组 (或者直接在count中累加并处理right逻辑),right[i]表示在只考虑右侧邻居的情况下,孩子i应得的糖果数。 - 初始化
right数组所有元素为1。 - 从索引
n-2开始遍历到0:- 如果
ratings[i] > ratings[i+1],则right[i] = right[i+1] + 1。这意味着当前孩子比右侧邻居多一颗糖果。 - 否则 (
ratings[i] <= ratings[i+1]),right[i]保持为1。
- 如果
- 合并结果: 对于每个孩子
i,他最终应得的糖果数是max(left[i], right[i])。这是因为left[i]确保了对左侧邻居的条件满足,right[i]确保了对右侧邻居的条件满足。取两者最大值可以同时满足这两个独立的条件,并且由于我们是基于"最少一颗"和"比邻居多一颗"递增的,所以取最大值是满足所有条件所需的最小数量。 - 在第二次遍历中,我们将
max(left[i], right[i])累加到总数count中。需要注意的是,count初始值是left[n-1],这是因为最后一个孩子n-1没有右邻居,所以它的糖果数就是left[n-1](或者说max(left[n-1], right[n-1])也是left[n-1],因为right[n-1]始终为1)。
题解代码
java
import java.util.Arrays; // 导入 Arrays 工具类,用于数组填充
class Solution {
/**
* 计算分发糖果的总数,满足以下条件:
* 1. 每个孩子至少分得一颗糖果。
* 2. 评分较高的孩子比相邻评分较低的孩子获得更多的糖果。
*
* @param ratings 一个整数数组,表示每个孩子的评分。
* @return 分发糖果的总数。
*/
public int candy(int[] ratings) {
int n = ratings.length; // 获取孩子的总人数
// 如果没有孩子,则不需要分发糖果
if (n == 0) {
return 0;
}
// left 数组:从左向右遍历,记录每个孩子相对于其左侧邻居应得的糖果数。
// 例如,如果 ratings[i] > ratings[i-1],则 left[i] = left[i-1] + 1。
int[] left = new int[n];
// right 数组:从右向左遍历,记录每个孩子相对于其右侧邻居应得的糖果数。
// 例如,如果 ratings[i] > ratings[i+1],则 right[i] = right[i+1] + 1。
int[] right = new int[n];
// 初始化:每个孩子至少分得一颗糖果,所以初始都设为1。
Arrays.fill(left, 1);
Arrays.fill(right, 1);
// 第一次遍历:从左向右,处理"左邻居"关系
// 遍历从第二个孩子开始 (索引1),因为第一个孩子 (索引0) 没有左邻居。
for(int i = 1; i < n; i++) {
// 如果当前孩子的评分高于其左侧邻居的评分
if(ratings[i] > ratings[i - 1]) {
// 则当前孩子比左侧邻居多一颗糖果
left[i] = left[i - 1] + 1;
}
}
// 初始化总糖果数。
// 最后一个孩子的糖果数,只受左侧规则(left[n-1])影响,因为其没有右邻居需要比较。
// 或者说,right[n-1] 肯定是1,所以 Math.max(left[n-1], right[n-1]) 就是 left[n-1]。
int count = left[n - 1];
// 第二次遍历:从右向左,处理"右邻居"关系,并累加最终结果
// 遍历从倒数第二个孩子开始 (索引 n-2),因为倒数第一个孩子 (索引 n-1) 已在上面处理。
for(int i = n - 2; i >= 0; i--) {
// 如果当前孩子的评分高于其右侧邻居的评分
if(ratings[i] > ratings[i + 1]) {
// 则当前孩子比右侧邻居多一颗糖果
right[i] = right[i + 1] + 1;
}
// 最终每个孩子应得的糖果数,是满足左右两边条件的最大值。
// 例如,如果孩子A评分比左边B高,left[A]=left[B]+1。
// 如果孩子A评分比右边C高,right[A]=right[C]+1。
// 那么A最终应得的糖果数是 max(left[A], right[A]),以同时满足两边要求。
count += Math.max(left[i], right[i]);
}
return count; // 返回总的糖果数
}
}
复杂度分析
假设 n 是孩子的数量(即 ratings.length)。
- 时间复杂度:
- 初始化
left和right数组:O(n)(使用Arrays.fill)。 - 第一次遍历(从左向右):
O(n),遍历n-1次。 - 第二次遍历(从右向左):
O(n),遍历n-1次。 - 总的时间复杂度是所有这些步骤的总和,即
O(n) + O(n) + O(n) = O(n)。
- 初始化
- 空间复杂度:
left数组:O(n)right数组:O(n)- 其他变量 (
n,i,count):O(1) - 总的空间复杂度是
O(n) + O(n) = O(n)。