135. 分发糖果

题目:

思路:

【1】多次遍历

主要是将规则进行拆分,分为
1)左规则:当 ratings[i−1]<ratings[i] 时,i 号学生的糖果数量将比 i−1 号孩子的糖果数量多。
2)右规则:当 ratings[i]>ratings[i+1] 时,i 号学生的糖果数量将比 i+1 号孩子的糖果数量多。
根据不同的规则生成对应的分配数组
而位置所处的值的最大值即是理应分配的最小值(因为多个规则都要满足)

 

【2】常数空间遍历:

这种更是利用了升降序的理念

 

代码展示:

【1】多次遍历

//时间2 ms 击败 48.56%
//内存42.5 MB 击败 45.6%
//时间复杂度:O(n),其中 n 是孩子的数量。
//我们需要遍历两次数组以分别计算满足左规则或右规则的最少糖果数量。
//空间复杂度:O(n),其中 n 是孩子的数量。
//我们需要保存所有的左规则对应的糖果数量。
class Solution {
    public int candy(int[] ratings) {
        int n = ratings.length;
        int[] left = new int[n];
        // 生成左升序的峰值
        for (int i = 0; i < n; i++) {
            if (i > 0 && ratings[i] > ratings[i - 1]) {
                left[i] = left[i - 1] + 1;
            } else {
                left[i] = 1;
            }
        }
        int right = 0, ret = 0;
        // 生成右升序的峰值
        for (int i = n - 1; i >= 0; i--) {
            if (i < n - 1 && ratings[i] > ratings[i + 1]) {
                right++;
            } else {
                right = 1;
            }
            // 两个峰值取最大值,即时正确的分配
            ret += Math.max(left[i], right);
        }
        return ret;
    }
}


//时间1 ms 击败 100%
//内存42.6 MB 击败 44.35%
class Solution {
    public int candy(int[] ratings) {
        int[] candies = new int[ratings.length];
        for(int i = 0; i < candies.length; i++) candies[i] = 1;

        // update right
        for(int i = 1; i < ratings.length; i++){
            if(ratings[i] > ratings[i-1]){
                candies[i] = candies[i-1] + 1;
            }
        }

        // update left
        for(int i = ratings.length - 2; i >= 0; i--){
            if(ratings[i] > ratings[i + 1]){
                candies[i] = (candies[i+1] + 1) > candies[i] ? candies[i+1] + 1 : candies[i];
            }
        }

        int sum = 0;
        for(int i = 0; i < candies.length; i++){
            sum += candies[i];
        }

        return sum;
    }
}

 

【2】常数空间遍历:

//时间2 ms 击败 48.56%
//内存43.1 MB 击败 5.6%
//时间复杂度:O(n),其中 n 是孩子的数量。
//空间复杂度:O(1)。我们只需要常数的空间保存若干变量。
class Solution {
    public int candy(int[] ratings) {
        int n = ratings.length;
        // 作为糖果的个数和,因为默认是从第二位开始查找,而第一位默认是先分配1颗
        // 故初始化是1
        int ret = 1;
        // pre算是初始化第一位获得的就是1颗糖果
        int inc = 1, dec = 0, pre = 1;
        for (int i = 1; i < n; i++) {
            // 升序的情况
            if (ratings[i] >= ratings[i - 1]) {
                // 这里是重置降序时候的糖果数
                dec = 0;
                // 如果是存在两值相等如【2,2】,这种不满足相邻两个孩子评分更高的孩子会获得更多的糖果
                // 故后面的可以分配1,如果不相等则必是大于,所以获得更多的最低条件便是+1
                // 即当前位置获得的糖果比之前的位置+1
                pre = ratings[i] == ratings[i - 1] ? 1 : pre + 1;
                // 累加当前位置的糖果数
                ret += pre;
                // 记录峰值的最大值
                inc = pre;
            } else {
                // 降序的情况
                // 降序分配的糖果数是跟升序差不多的,如果是【4,3,2,1】,理论上是4拿的更多
                // 但在这里,4 给予 1,3 给予 2 ,2 给予 3, 1 给予 4
                // 相当于将给的个数进行了反转,但是和是不会变的
                dec++;
                // 这里是为了处理特殊情况,如【1,0,2】和【1,2,4,3,2,1】
                // 本质是为了处理最高的峰值不足以满足降序的问题
                // 从【1,2,4,3,2,1】的升序部分看数值4应该分3颗,但从降序部分看应该分4颗(不分4颗就不能满足降序了)
                // 但由于升序部分已经分了【1,2,3,?,?,?】
                // 所以降序部分先分1,2 ,等分到3的时候为了满足降序而+1,就会变为4
                // 所以变为【1,2,3,4,2,1】,但这样不合理,故将位置变换为【1,2,4,3,2,1】就满足条件了
                // 所以这个+1是为了弥补峰值不够的部分进行替换用的,但总和是正确的
                if (dec == inc) {
                    dec++;
                }
                ret += dec;
                // 如果降序结束重新变为升序的话,又相当于重新开始
                // 故将前置位的糖果数又变为1
                pre = 1;
            }
        }
        return ret;
    }
}

 

常数空间遍历

posted @ 2023-06-25 13:59  忧愁的chafry  阅读(6)  评论(0编辑  收藏  举报