面试经典算法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是否为空或null,如果是,则返回0。然后获取孩子的数量n,并初始化两个数组leftToRightrightToLeft,这两个数组用于记录从左到右和从右到左遍历时每个孩子应得到的糖果数,初始值都设为1,因为每个孩子至少得到1颗糖果。

  2. 第一次遍历(从左到右):遍历ratings数组,如果一个孩子的评分高于他左边的孩子,则他应得到的糖果数至少是左边孩子的糖果数加1。这样保证了相邻两个孩子中评分更高的孩子会获得更多的糖果。

  3. 第二次遍历(从右到左):再次遍历ratings数组,但这次是从右到左。如果一个孩子的评分高于他右边的孩子,并且他当前得到的糖果数不大于右边孩子的糖果数,则更新他的糖果数为右边孩子的糖果数加1。这一步是为了修正第一次遍历时可能遗漏的情况,确保满足题目中的要求。

  4. 计算总糖果数:通过比较leftToRightrightToLeft两个数组中相对应的元素,取两者的较大值作为该孩子最终应得的糖果数,并将这个值累加到totalCandies上。这样做是因为一个孩子最终的糖果数应该是从左侧看和从右侧看得到的糖果数中的较大值。

  5. 返回结果:最后返回totalCandies,即所需的最少糖果总数。

这种实现方式确保了每个孩子至少得到1颗糖果,同时满足了评分更高的孩子会得到更多的糖果的条件。通过两次遍历和一次最终的糖果数比较,算法高效地计算出了最少糖果数。

实现代码:

  public int candy(int[] ratings) {
        if (ratings == null || ratings.length == 0) {
            return 0;
        }
        int n = ratings.length;
        int[] leftToRight = new int[n]; // 从左到右遍历得到的糖果数
        int[] rightToLeft = new int[n]; // 从右到左遍历得到的糖果数

        // 初始化数组,每个孩子至少得到1颗糖果
        for (int i = 0; i < n; i++) {
            leftToRight[i] = 1;
            rightToLeft[i] = 1;
        }

        // 从左到右遍历,为评分更高的孩子分配更多的糖果
        for (int i = 1; i < n; i++) {
            if (ratings[i] > ratings[i - 1]) {
                leftToRight[i] = leftToRight[i - 1] + 1;
            }
        }

        // 从右到左遍历,为评分更高的孩子分配更多的糖果
        for (int i = n - 2; i >= 0; i--) {
            if (ratings[i] > ratings[i + 1]) {
                rightToLeft[i] = rightToLeft[i + 1] + 1;
            }
        }

        // 计算并返回总糖果数
        int totalCandies = 0;
        for (int i = 0; i < n; i++) {
            // 选择左右两个方向中糖果数的最大值
            totalCandies += Math.max(leftToRight[i], rightToLeft[i]);
        }

        return totalCandies;
    }

思路模拟:

让我们使用给定的ratings1数组 {1, 6, 10, 8, 7, 3, 2} 来模拟糖果分配算法的执行过程:

  1. 初始化糖果数组

    • 创建两个数组 candiesLeftToRight 和 candiesRightToLeft,都初始化为长度等于 ratings1 的数组,每个元素值为1。
  2. 从左到右遍历

    • 遍历 ratings1,对于索引 i(从1到6):

      • 如果 ratings1[i] > ratings1[i - 1],则 candiesLeftToRight[i] 设置为 candiesLeftToRight[i - 1] + 1
    • 遍历结果:

       LTR-candy: 1 2 3 2 2 2 1

  3. 从右到左遍历

    • 反向遍历 ratings1,对于索引 i(从5到0,注意数组索引是从0开始的):

      • 如果 ratings1[i] > ratings1[i + 1] 并且 candiesRightToLeft[i] <= candiesRightToLeft[i + 1],则 candiesRightToLeft[i] 设置为 candiesRightToLeft[i + 1] + 1
    • 遍历结果:

      RTR-candy: 1 2 5 4 3 2 1

  4. 比较左右遍历结果

    • 对于每个索引 i,取 candiesLeftToRight[i]candiesRightToLeft[i] 中的较大值作为最终的糖果数。

    • 比较结果:

      Final-candy: 1 2 5 4 3 2 1

  5. 计算总糖果数

    • 累加 Final-candy 数组中的所有值来得到总糖果数。

    • 累加结果:

      Total candies: 1 + 2 + 5 + 4 + 3 + 2 + 1 = 18

  6. 输出结果

        最终,返回总糖果数 18

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值