力扣面试经典算法150题:分发糖果

43 篇文章 0 订阅
22 篇文章 0 订阅

分发糖果

今天的题目是力扣面试经典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 颗糖果,这满足题面中的两个条件。

题目分析

题目要求给每个孩子分最少一个糖果,但是相邻孩子评分高的得到的糖果会多一点。这里面蕴涵的信息除了表面的每个孩子最少一个外,还告诉我们,需要对每个孩子的评分两两互相比较。

最后题目要求算出最少的糖果数,这里意味着在我们未进行比较前,每个小孩都必须先分一个糖果。

解题思路

  1. 定义一个数组,数组的长度与ratings一致为n。这个数组用于保存每位孩子的糖果数,并且每个下标位置的初始值都是1。
  2. 双向遍历,也就是说我们需要先从左到右遍历一次,还要从右到左再遍历一次。因为题目要求的是相邻的两个都要比较分值,单向遍历无法实现目标要求。在单向遍历的时候,我们只是在后面下标元素的值大于前面下边元素的值的时候,将后面下标对应的孩子的糖果数加1,当小于等于时,不会进行处理,所以得再反向遍历一遍
  3. 将定义孩子糖果数的数组元素值相加,获得结果。

实际算法代码

以下是双向遍历的代码实现:

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很明显就无法实现题目要求。

当然非上升趋势的场景也有,这个需要自己去分析,我都讲完了你还学啥。自己发现总结归纳的,才容易变成自己的知识能力。

结果

好了,我们运行程序,符合示例要求:

在这里插入图片描述

提交到力扣去,也正常通过:

在这里插入图片描述

效果看着不是那么理想,后面我再看看大神们有哪些神奇的思路!

总结

双向遍历,其实还是暴力破解了。题目的关键在于,如何实现两两比较,从而保证每个评分高的孩子在两边都能得到足够的糖果,这一点想明白了,题目就比较简单了。

可以稍微记一下,类似这种题目可以考虑直接套双向遍历的方法。

第一题困难题目成功拿下,加油!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值