89.分发糖果

一、题目描述

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。

你需要按照以下要求,帮助老师给这些孩子分发糖果:

每个孩子至少分配到 1 个糖果。
评分更高的孩子必须比他两侧的邻位孩子获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?
在这里插入图片描述

二、解题思路

2.1 思路一:暴力求解

先根据题意简单模拟一下解题过程,看下图,输入是每个孩子的评分,每个孩子初始都能分到一个糖果。
在这里插入图片描述
第一步:对于第一个位置,孩子的评分是1,比较他的左右评分,左边没有不用比较,评分比右边孩子的小,所以不用改变糖果数量,继续依次从左往右遍历:
在这里插入图片描述

通过分析发现,第二个位置小孩的评分比右边的小孩子低,所以先不变,但是它比左边孩子的评分高,所以糖果数要加一个,变成2。

第二步:继续遍历第三个小孩子的评分87,它和右边的孩子评分一样,所以糖果数先不变,但是它的评分又比左边孩子的高,所以糖果数也要比左边孩子的高:
在这里插入图片描述

第三步:继续遍历第四个孩子,它的评分和左右孩子的一样,所以糖果数不变,那么直接看第五个孩子的评分,比它右边的高,所以糖果数加一,和左边孩子一样,糖果数保持不变。
在这里插入图片描述

第四步:遍历到第六个孩子的评分2,它比它右边的孩子评分高,所以糖果数加一,比左边的小,加一后先保持不变:
在这里插入图片描述

第五步:遍历最后一个孩子的评分,它的评分比左右的都小,所以糖果数不变。至此,所有糖果分发完毕。

这里应该会注意到一个问题,就是第五个孩子的评分比第六个孩子的评分高,但是糖果数和第六个孩子是一样的,那怎么解决呢?答案就是再从左到右再遍历一遍即可。这里就不逐一展示了,当遍历到第五个孩子时,糖果数加一即可:
在这里插入图片描述

2.2 思路二:两个数组+两次遍历

使用两个数组,一个数组(left2Right)记录从左往右遍历评分时糖果数的变化,一个数组(right2Lift)记录从右往左遍历评分的变化。

在这里插入图片描述

当从左往右遍历的时候,利用left2Right数组记录孩子糖果数的变化,遍历每一个孩子的评分时,每个孩子只与左边的孩子的评分进行比较,如果当前孩子的评分比左边的孩子大,那么糖果数就加一。

当从右往左遍历的时候,利用right2Left数组记录孩子糖果数的变化,遍历每一个孩子的评分时,每个孩子只与右边的孩子的评分进行比较,如果当前孩子的评分比右边的孩子大,那么糖果数就加一。

在这里插入图片描述

最终的糖果数统计每个孩子在两个数组left2Rightright2Left中的最大值就行,第一个孩子就是1,第二个孩子是2,第三个孩子是3,以此类推。最终得到:

在这里插入图片描述

三、代码演示

3.1 暴力求解

//定义小孩子的个数
        int n = ratings.length;

        //定义每个小孩子得到糖果的数组
        int[] candies = new int[n];
        //先给每个小孩子赋值一个糖果
        Arrays.fill(candies,1);

        //使用一个变量表示糖果有变化
        boolean hasChange = true;
        while (hasChange){
            //一开始糖果数没有变化,就是false
            hasChange = false;
            //遍历每一个孩子的评分
            for (int i=0; i<n; i++){
                /**
                 * 与右边孩子比较
                 * 三个条件:1.不是最右边的孩子。2.评分大于右边的孩子。3.糖果数小于右边的孩子
                 */
                if (i!=n-1 && ratings[i]>ratings[i+1] && candies[i]<=candies[i+1]){
                    candies[i] = candies[i+1] + 1;
                    hasChange = true; //糖果数有变化就设置为true
                }
                /**
                 * 与左边孩子比较
                 * 三个条件:1.当前孩子不是最左边的孩子。2.评分大于左边孩子。3.糖果数小于等于左边孩子
                 */
                if (i!=0 && ratings[i]>ratings[i-1] && candies[i]<=candies[i-1]){
                    candies[i] = candies[i-1] + 1;
                    hasChange = true;
                }
            }
        }

        int sum = 0;
        //累加糖果数
        for (int candy : candies){
            sum += candy;
        }
        return sum;

时间复杂度是O(n2):假如说对于一个降序排列的评分,从左往右遍历每循环一次只会算对一个孩子的糖果数(可以自己试试),所以要循环n次,时间服复杂度就是O(n2)。

3.2 两个数组+两次遍历

class Solution {
    public int candy(int[] ratings) {
        int n = ratings.length;

        //两个数组分别记录从左往右和从右往左遍历时的糖果数变化,初始都是1个糖果
        int[] left2Right = new int[n];
        Arrays.fill(left2Right,1);
        int[] right2Left = new int[n];
        Arrays.fill(right2Left,1);

        //从左往右
        for (int i=0; i<n; i++){
            if (i!=0 && ratings[i] > ratings[i-1]){
                left2Right[i] = left2Right[i-1] + 1;
            }
        }
        //从右往左
        int sum = 0;
        for (int i=n-1; i>=0; i--){
            if (i!=n-1 && ratings[i]>ratings[i+1]){
                right2Left[i] = right2Left[i+1]  + 1;
            }
            //将两个数组中的最大值累加就是最大糖果数
            sum += Math.max(left2Right[i], right2Left[i]);
        }
    return sum;
    }
}

时间复杂度是O(n),因为只经历了两次遍历

空间复杂度O(n),因为申请了两个数组(精确一点应该是2n)

3.3 思路二代码优化

只用一个数组,优化空间,使用一个变量来记录从右往左遍历时的值

class Solution {
    public int candy(int[] ratings) {
        int n = ratings.length;

        //使用一个数组记录从左往右的糖果数变化,初始都是1个糖果
        int[] left2Right = new int[n];
        Arrays.fill(left2Right,1);

        //从左往右
        for (int i=0; i<n; i++){
            if (i!=0 && ratings[i] > ratings[i-1]){
                left2Right[i] = left2Right[i-1] + 1;
            }
        }
        //从右往左
        int sum = 0;
        //使用一个变量来存储从右往左遍历时的糖果数量变化
        int right = 0;
        for (int i=n-1; i>=0; i--){
            if (i!=n-1 && ratings[i]>ratings[i+1]){
                right++;
            }else {
                right = 1; //不符合if中的条件,糖果数重新置为1
            }
            //将两个数组中的最大值累加就是最大糖果数
            sum += Math.max(left2Right[i], right);
        }
        return sum;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值