LeetCode面试经典150题—13 分发糖果

  • 拆分规则

    将 “相邻两个孩子评分更高的孩子会获得更多的糖果” 这条规则拆分为左右两边的两条规则进行处理:

            左规则:从左往右遍历,当右边的孩子评分(当前遍历到的 ratings[i] )比左边高( ratings[i] > ratings[i - 1] )时,右边的孩子获得的糖果数比左边多 1 个;否则令  ratings[i] = 1
     
    if (i > 0 && ratings[i] > ratings[i - 1]) left[i] = left[i - 1] + 1;
    else left[i] = 1;
            右规则:与左规则同理,从右往左遍历,当左边的孩子评分(当前遍历到的 ratings[i] )比右边高( ratings[i] > ratings[i +1] )时,左边的孩子获得的糖果数比右边多 1 个;否则令  ratings[i] = 1
     
    if (i < len - 1 && ratings[i] > ratings[i + 1]) right[i] = right[i + 1] + 1;
    else right[i] = 1;
    最后一次遍历汇总左右规则的结果,满足条件的糖果数为左右数组中的较大值
     
    //sum:存储返回的总糖果数
    sum += Math.max(left[i], right[i]);
    总代码如下:
    public static int candy(int[] ratings) {
        int len = ratings.length;
        int[] left = new int[len];
        for (int i = 0; i < len; i++) {
            if (i > 0 && ratings[i] > ratings[i - 1]) left[i] = left[i - 1] + 1;
            else left[i] = 1;
        }

        int[] right = new int[len];
        for (int i = len - 1; i >= 0; i--) {
            if (i < len - 1 && ratings[i] > ratings[i + 1]) right[i] = right[i + 1] + 1;
            else right[i] = 1;
        }

        int sum = 0;
        for (int i = 0; i < len; i++) {
            sum += Math.max(left[i], right[i]);
        }

        return sum;
    }

  • 优化

    上述方法中经过了三次遍历,并且用到了两个中间数组。

    可以对后两次遍历和第二个数组进行优化:

    在遍历执行右规则时,可以只需要一个变量记录当前位置的值,同时更新计算总糖果数

    代码如下:​​​
    public static int candy(int[] ratings) {
        int len = ratings.length;
        int[] left = new int[len];
        for (int i = 0; i < len; i++) {
            if (i > 0 && ratings[i] > ratings[i - 1]) left[i] = left[i - 1] + 1;
            else left[i] = 1;
        }

        int right = 0, sum = 0;
        for (int i = len - 1; i >= 0; i--) {
            if (i < len - 1 && ratings[i] > ratings[i + 1]) right++;
            else right = 1;
            sum += Math.max(left[i], right);
        }
        return sum;
    }

  • 升序降序(常数空间)

            把 ratings 数组看作是由一个个升序序列和降序序列组成的,用 asc 和 des 分别记录最近遍历到的一个升序序列的长度和降序序列的长度,now 记录当前孩子获得的糖果数

            从左往右依次遍历,若当前遍历到的元素的评分比上一个高或相等( ratings[i] >= ratings[i - 1] ),那么就将该元素放在一个升序序列中,同时注意维护各项变量:

            降序序列长度清0
    des = 0;
            维护当前孩子获得的糖果数,两种情况:

            1、当前评分高于上一个:当前获得的糖果数比上一个多 1 个
            
            2、当前评分与上一个相等:当前获得的糖果数为 1,相当于一个新的升序序列的开始
     
    now = ratings[i] == ratings[i - 1] ? 1 : now + 1;
            维护升序序列长度:与当前获得的糖果数量相等
    asc = now;
            维护总糖果数
    sum += now;
            以上是将元素放在升序序列中的情况,否则就是将元素放在一个降序序列中

            此时直接给当前孩子分配一个糖果
    now = 1;
            维护降序序列长度

            这里注意:如果升序序列和降序序列长度相同,需要将升序序列的最后一个元素放到降序序列的开头,降序序列是的长度还要加 1
    des++;
    if (des == asc) des++;
            同时把该孩子所在的降序序列中的所有孩子都再多分配一个糖果以满足条件上一步将升序末尾放到降序开头的原因就是这里
    sum += des;
    总代码如下:
    public static int candy(int[] ratings) {
        int len = ratings.length;
        int sum = 1;
        //asc:升序序列长度,des:降序序列长度;now:当前孩子获得的糖果数
        int asc = 1, des = 0, now = 1;

        for (int i = 1; i < len; i++) {
            if (ratings[i] >= ratings[i - 1]) {
                des = 0;
                now = ratings[i] == ratings[i - 1] ? 1 : now + 1;
                //糖果数量 == 该升序序列的元素数量
                asc = now;
                sum += now;
            } else {
                now = 1;
                des++;
                if (des == asc) des++;
                sum += des;
            }
        }
        return sum;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值