135.Candy
题目
有N 个孩子站成一排,每个孩子被分配一个等级值。
你给这些孩子按照以下要求分糖果:
每个孩子必须至少有一个糖果。
高等级的孩子得到的糖果比相邻的孩子多。
最少给多少个糖果?
代码块
代码1:时间复杂度为O(n^2) ,不能AC。
import java.util.Arrays;
class Solution {
public static int candy(int[] ratings) {
int[] candies = new int[ratings.length];
Arrays.fill(candies, 1);
boolean flag = true;
int sum = 0;
while(flag){
flag = false;
for(int i = 0; i < candies.length; i++){
if(i > 0 && ratings[i] < ratings[i-1] && candies[i-1] <= candies[i]){//i>0 与i<len-1弄混了,不清楚判断条件是为了保证数组不越界
candies[i-1] = candies[i] + 1;
//flag = false;
flag = true;
}
if(i < candies.length - 1 && ratings[i] < ratings[i+1] && candies[i+1] <= candies[i]){
candies[i+1] = candies[i] + 1;
//flag = false;
flag = true;
}
}
}
//for(int i = 0; i < candies.length; i++){
for(int candy : candies){ //这个格式没见过
//sum += candies[i];
sum += candy;
}
return sum ;
}
public static void main(String[] args) {
int[] ratings = {3,2,1,4,5,6};
System.out.println(candy(ratings));
}
}
代码2:时间复杂度为O(n),能够AC
class Solution {
public int candy(int[] ratings) {
int[] left2right = new int[ratings.length];
int[] right2left = new int[ratings.length];
Arrays.fill(left2right, 1);
Arrays.fill(right2left, 1);
int sum = 0;
for(int i = 1; i < ratings.length ; i++){ //i的范围
if(ratings[i] > ratings[i - 1]){
left2right[i] = left2right[i - 1] + 1;
}
}
for(int i = ratings.length - 2; i >= 0; i--){
if(ratings[i] > ratings[i + 1]){
right2left[i] = right2left[i + 1] + 1;
}
}
for(int i = 0; i < ratings.length; i++){
sum += Math.max(left2right[i], right2left[i]);
}
return sum;
}
}
代码分析
代码#1暴力破解【时间超时】
- 最简单的方法是使用一维数组,candies来记录给学生的糖果。首先,给每个学生一个糖果,然后,我们开始开始从左到右扫描数组。遍历到任一元素,首先,如果当前的元素等级,ratings[i]比它前一个元素(ratings[i-1])大,并且candies[i]<=candies[i−1],则我们将candies[i]更新为 candies[i]=candies[i-1] + 1。因此,现在对于candies[i−1] 和candies[i]这两个元素糖果的分发是暂时(局部)正确的。
- 同样的步骤,我们来检查当前的元素级别ratings[i],假如比下一个元素等级高,ratings[i]>ratings[i+1],我们就再次更新 candies[i]=candies[i+1] + 1。我们对整个rantings数组继续这一步骤。如果在遍历过程中,candies数组中的元素不再更新了,就意味着我们达到了最后的要求。为了记录这一过程,我们使用了一个flag,如果在遍历中发生了任何更新,它将被设置为 {True}。
- 最后,我们将candies数组元素加起来就可以的到要求的最小糖果数了。
时间复杂度:O(n^2) 我们需要遍历数组最多n次。
空间复杂度: O(n) 只需要一个n长的candies数组就可以了。
代码#2 使用两个数组【AC】
此法使用两个一维数组left2right 和 right2left。
1. 用 left2right来存储当前学生所需要的分发的糖果的数量,只考虑左边的邻居。比如:假定分发规则是:一个比左边等级高的学生总比左边得到更多的糖果。
相似的,right2left用来存储只考虑右边邻居的满足要求的糖果数。假定分发规则是:一个比右边等级高的学生总比右边得到更多的糖果。
2. 为了满足要求,我们先将两个数组元素都置为一(给每个学生分发一个糖果)。然后
3. 然后根据上述规则遍历数组,先从左到右,如果当前元素的等级比左边高,我们就更新left2right数组,left2right[i] = left2right[i-1] + 1,因为在更新之前当前元素的糖果总是小于等于左边的。
4. 在前向遍历后,再来更新right2left数组, right2left[i] = right2left[i + 1] +1。
5. 在第i个位置的学生,再取两个数组中的最大值,就可以满足左右关系了。最后把他们加起来即可。
小知识
for(int candy : candies)
遍历candies数组,每次遍历的对象用candy 这个对象去接收。
相当于:
int candy=0; //用于接收index数组中的某一个对象
for(int j = 0;j<candies.length;j++){
candy = candies[j];
}