《代码随想录》Ⅷ 贪心算法 135. 分发糖果
努力学习!
题目:力扣链接
-
n
个孩子站成一排。给你一个整数数组ratings
表示每个孩子的评分。你需要按照以下要求,给这些孩子分发糖果:
- 每个孩子至少分配到
1
个糖果。 - 相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
- 每个孩子至少分配到
一、思想
这道题的核心思想是使用贪心算法,通过两次遍历数组,分别从左到右和从右到左,确保每个孩子都能满足以下条件:
-
每个孩子至少分配到1个糖果:初始时,每个孩子都分配1个糖果。
-
相邻两个孩子评分更高的孩子会获得更多的糖果:
- 从左到右遍历时,如果当前孩子的评分比前一个孩子高,则当前孩子的糖果数比前一个孩子多1。
- 从右到左遍历时,如果当前孩子的评分比后一个孩子高,则当前孩子的糖果数比后一个孩子多1。
通过这种方式,可以确保每个孩子都能满足条件,并且总糖果数是最小的。
二、代码
/**
* 该函数用于计算给定评分数组的糖果数量。
* 评分数组中的每个元素代表一个孩子的评分,糖果数量表示为每个孩子得到的糖果数。
* 规则是:如果一个孩子的评分比邻座的孩子高,则他必须得到比邻座孩子更多的糖果。
* 这个函数使用了贪婪算法,从左右两边同时遍历数组,最终得到每个孩子应得的糖果数,并求和。
*
* @param ratings 孩子的评分数组
* @return 总的糖果数量
*/
class Solution
{
public:
/**
* 计算孩子们的糖果数量
* @param ratings 孩子的评分数组
* @return 总的糖果数量
*/
int candy(vector<int> &ratings)
{
// 初始化总的糖果数量为0
int res = 0;
// 初始化每个孩子应得的糖果数为1
vector<int> candyVec(ratings.size(), 1);
// 从左到右遍历数组,如果当前孩子的评分比前一个孩子高,则当前孩子的糖果数为前一个孩子的糖果数+1
for (int i = 1; i < ratings.size(); ++i) {
if (ratings[i - 1] < ratings[i]) {
candyVec[i] = candyVec[i - 1] + 1;
}
}
// 从右到左遍历数组,如果当前孩子的评分比后一个孩子高,则当前孩子的糖果数为后一个孩子的糖果数+1的最大值
for (int i = ratings.size() - 2; i >= 0; --i) {
if (ratings[i] > ratings[i + 1]) {
candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1);
}
}
// 计算总的糖果数量
for (int candy : candyVec) {
res += candy;
}
return res;
}
};
三、代码解析
1. 算法工作原理分解
1.1 初始化糖果数组
- 目的:确保每个孩子至少分配到1个糖果。
- 实现:创建一个与
ratings
数组大小相同的candyVec
数组,初始值均为1。
1.2 从左到右遍历
-
目的:确保如果当前孩子的评分比前一个孩子高,则当前孩子的糖果数比前一个孩子多1。
-
实现:
- 从第二个孩子开始遍历,如果
ratings[i] > ratings[i - 1]
,则candyVec[i] = candyVec[i - 1] + 1
。
- 从第二个孩子开始遍历,如果
1.3 从右到左遍历
-
目的:确保如果当前孩子的评分比后一个孩子高,则当前孩子的糖果数比后一个孩子多1。
-
实现:
- 从倒数第二个孩子开始遍历,如果
ratings[i] > ratings[i + 1]
,则candyVec[i] = max(candyVec[i], candyVec[i + 1] + 1)
。
- 从倒数第二个孩子开始遍历,如果
1.4 计算总糖果数
- 目的:计算所有孩子糖果数的总和。
- 实现:遍历
candyVec
数组,累加每个孩子的糖果数。
2. 关键点说明
2.1 从左到右遍历的意义
- 从左到右遍历时,确保每个孩子比前一个评分更高的孩子获得更多的糖果。这满足了从左到右的评分规则。
2.2 从右到左遍历的意义
- 从右到左遍历时,确保每个孩子比后一个评分更高的孩子获得更多的糖果。这满足了从右到左的评分规则。
2.3 为什么需要两次遍历
- 单独从左到右或从右到左遍历都无法完全满足条件。例如,如果只从左到右遍历,可能会忽略从右到左的评分规则。因此,需要两次遍历来确保所有条件都被满足。
2.4 贪心算法的应用
- 局部最优:每次遍历时,确保当前孩子满足与相邻孩子的糖果数关系。
- 全局最优:通过两次遍历,确保所有孩子都满足条件,并且总糖果数是最小的。
四、复杂度分析
-
时间复杂度:
O(n)
- 其中
n
是孩子的数量。算法需要遍历两次数组,因此总的时间复杂度为O(n)
。
- 其中
-
空间复杂度:
O(n)
- 需要额外的
candyVec
数组来存储每个孩子的糖果数,因此空间复杂度为O(n)
。
- 需要额外的
白展堂:人生就是这样,苦和累你总得选一样吧?哪有什么好事都让你一个人占了呢。 ——《武林外传》