分发糖果
今天的题目是力扣面试经典150题中的数组的困难难度题:分发糖果。
题目链接:https://leetcode.cn/problems/candy/description/?envType=study-plan-v2&envId=top-interview-150
这也是数组困难难度的第一题,没几题就要结束数组方面的练习了,正好周末,到时候总结一下数组这块的一些个人心得和菜鸡理解。
题目描述
n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。
你需要按照以下要求,给这些孩子分发糖果:
- 每个孩子至少分配到 1 个糖果。
- 相邻两个孩子评分更高的孩子会获得更多的糖果。
请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。
-
示例 1:
-
输入:
ratings = [1,0,2] -
输出:
5 -
解释:
你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。
-
-
示例 2:
-
输入:
ratings = [1,2,2] -
输出:
4 -
解释:
你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。
-
题目分析
题目要求给每个孩子分最少一个糖果,但是相邻孩子评分高的得到的糖果会多一点。这里面蕴涵的信息除了表面的每个孩子最少一个外,还告诉我们,需要对每个孩子的评分两两互相比较。
最后题目要求算出最少的糖果数,这里意味着在我们未进行比较前,每个小孩都必须先分一个糖果。
解题思路
- 定义一个数组,数组的长度与ratings一致为n。这个数组用于保存每位孩子的糖果数,并且每个下标位置的初始值都是1。
- 双向遍历,也就是说我们需要先从左到右遍历一次,还要从右到左再遍历一次。因为题目要求的是相邻的两个都要比较分值,单向遍历无法实现目标要求。在单向遍历的时候,我们只是在后面下标元素的值大于前面下边元素的值的时候,将后面下标对应的孩子的糖果数加1,当小于等于时,不会进行处理,所以得再反向遍历一遍
- 将定义孩子糖果数的数组元素值相加,获得结果。
实际算法代码
以下是双向遍历的代码实现:
public class Solution {
public static void main(String[] args) {
int[] ratings = {1, 0, 2};
Solution solution = new Solution();
int candy = solution.candy(ratings);
System.out.println("candy = " + candy);
}
public int candy(int[] ratings) {
if (ratings == null || ratings.length == 0) return 0;
int n = ratings.length;
int[] candies = new int[n];
Arrays.fill(candies, 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 totalCandies = 0;
for (int candy : candies) {
totalCandies += candy;
}
return totalCandies;
}
}
从代码也可以看出,第一个for循环,我们从左到右进行循环,当遇到当前元素的值大于前一位元素的值的时候,给当前位置对应的孩子加一个糖果。因为我们要最少的糖果数,所以只能加一个。
当第一次循环结束后,我们已经将从左到右相邻孩子中的糖果数赋值好了,但是从右到左比较还没有处理,因此反向来了个遍历,当然这个反向遍历处理方式稍微不同的是。一个是当前元素还是与下一位元素比较,但是我们是往前(也就是往右)遍历的。其实就是糖果数这里不能单纯的加1,而是要两个位置的糖果数比较取大。简单点说,就是要保证每个评分高的孩子在两边都能得到足够的糖果,从而满足题目要求。具体分析的话就要考虑第一次遍历的时候,孩子分值是一直上升的趋势,而在当前位置突然下降的场景,这个时候candies[i + 1]的大概率可能是1,但是candies[i]的值可能大于2,这种场景还强制的取candies[i + 1] + 1很明显就无法实现题目要求。
当然非上升趋势的场景也有,这个需要自己去分析,我都讲完了你还学啥。自己发现总结归纳的,才容易变成自己的知识能力。
结果
好了,我们运行程序,符合示例要求:
提交到力扣去,也正常通过:
效果看着不是那么理想,后面我再看看大神们有哪些神奇的思路!
总结
双向遍历,其实还是暴力破解了。题目的关键在于,如何实现两两比较,从而保证每个评分高的孩子在两边都能得到足够的糖果,这一点想明白了,题目就比较简单了。
可以稍微记一下,类似这种题目可以考虑直接套双向遍历的方法。
第一题困难题目成功拿下,加油!!!