这段代码的算法思想是 贪心算法,通过两次遍历,分别从左到右、从右到左调整糖果分配,以满足题目中相邻评分较高的孩子必须获得更多糖果的要求,并最终计算出最少需要分配的糖果总数。
以下是代码的详细思想与执行过程:
算法思想:
-
每个孩子至少分配一个糖果:
- 每个孩子至少应该有一个糖果,初始化糖果数组
candies
,所有孩子的糖果数都为 1。
- 每个孩子至少应该有一个糖果,初始化糖果数组
-
从左到右遍历:
- 如果当前孩子的评分 高于左边的孩子,那么当前孩子的糖果数应该比左边的孩子多 1。
- 这样可以确保当前孩子的糖果分配满足 “相邻评分更高的孩子分得更多糖果” 的规则。
-
从右到左遍历:
- 如果当前孩子的评分 高于右边的孩子,那么当前孩子的糖果数也应该比右边的孩子多 1。
- 但这里需要特别注意:因为已经有一个从左到右的遍历调整结果,所以需要取当前糖果数和从右到左计算结果的最大值,保证不破坏之前的分配规则。
-
累加糖果总数:
- 最后,将
candies
数组中的糖果数累加,得到分配糖果的最小总数。
- 最后,将
代码逻辑解析:
1. 初始化糖果数组:
int[] candies = new int[n];
for (int i = 0; i < n; i++) {
candies[i] = 1;
}
每个孩子最少分一个糖果,因此初始化 candies
数组为全 1。
2. 从左到右遍历:
for (int i = 1; i < n; i++) {
if (ratings[i] > ratings[i - 1]) {
candies[i] = candies[i - 1] + 1;
}
}
- 当发现当前孩子评分比左边孩子高时,将当前孩子的糖果数设置为左边孩子的糖果数加 1。
- 这一步确保了左->右方向相邻评分规则满足。
3. 从右到左遍历:
for (int i = n - 2; i >= 0; i--) {
if (ratings[i] > ratings[i + 1]) {
candies[i] = Math.max(candies[i], candies[i + 1] + 1);
}
}
- 当发现当前孩子评分比右边孩子高时,将当前孩子的糖果数设置为右边孩子的糖果数加 1。
- 同时,用
Math.max
确保糖果分配满足左右两次遍历的规则。
4. 计算糖果总数:
int totalCandies = 0;
for (int candy : candies) {
totalCandies += candy;
}
将 candies
数组中所有的糖果数加起来,得到最终需要的最少糖果总数。
关键点:
-
双向遍历:
- 从左到右遍历保证了当前孩子比左边评分高时的糖果分配;
- 从右到左遍历保证了当前孩子比右边评分高时的糖果分配;
- 两次遍历结合,确保所有规则都满足。
-
动态调整:
- 在从右到左遍历时,利用
Math.max
动态调整糖果分配,避免破坏从左到右遍历的结果。
- 在从右到左遍历时,利用
-
贪心策略:
- 每次只关心局部相邻的两个孩子是否满足规则,通过两次遍历全局保证规则满足。
复杂度分析:
- 时间复杂度:
- 两次线性遍历,时间复杂度为 (O(n))。
- 空间复杂度:
- 使用了额外的
candies
数组,空间复杂度为 (O(n))。
- 使用了额外的
总结:
这段代码巧妙地利用贪心算法,分两次遍历调整糖果分配,既满足了规则,也保证了糖果分配数量的最小化,是一道经典的动态调整问题。
class Solution {
public int candy(int[] ratings) {
int n = ratings.length;
int[] candies = new int[n];
for(int i = 0; i < n; i++) {
candies[i] = 1;
}
for(int i = 1; i < n; i++) {
if(ratings[i] > ratings[i - 1]) {
candies[i] = candies[i - 1] + 1;
}
}
for(int i = n - 2; i >= 0; i--) {
if(ratings[i] > ratings[i + 1]) {
candies[i] = Math.max(candies[i], candies[i + 1] + 1);
}
}
int result = 0;
for(int i = 0; i < n; i++) {
result += candies[i];
}
return result;
}
}