0135. 分发糖果

题目链接

135. 分发糖果 - 力扣(LeetCode)

题目描述

n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。

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

  • 每个孩子至少分配到 1 个糖果。
  • 相邻两个孩子评分更高的孩子会获得更多的糖果。

请你给每个孩子分发糖果,计算并返回需要准备的 最少糖果数目 。

题目示例

示例 1 :

plain 复制代码
输入:ratings = [1,0,2]
输出:5
解释:你可以分别给第一个、第二个、第三个孩子分发 2、1、2 颗糖果。

示例 2 :

plain 复制代码
输入:ratings = [1,2,2]
输出:4
解释:你可以分别给第一个、第二个、第三个孩子分发 1、2、1 颗糖果。
     第三个孩子只得到 1 颗糖果,这满足题面中的两个条件。

解题思路

本题要求在满足两个条件的前提下,分配最少数量的糖果:

  1. 每个孩子至少一颗糖果。
  2. 评分高的孩子比相邻评分低的孩子获得更多糖果。

这意味着对于任何一个孩子 i,他获得的糖果数量 candy[i] 必须满足:

  • 如果 ratings[i] > ratings[i-1],则 candy[i] > candy[i-1]
  • 如果 ratings[i] > ratings[i+1],则 candy[i] > candy[i+1]

为了满足这些条件,同时使总糖果数最少,我们可以采用两次遍历的策略:

  1. 第一次遍历(从左向右):
    • 我们首先满足每个孩子相对于其左侧邻居的条件。
    • 创建一个 left 数组,left[i] 表示在只考虑左侧邻居的情况下,孩子 i 应得的糖果数。
    • 初始化 left 数组所有元素为 1 (每个孩子至少一颗糖果)。
    • 从索引 1 开始遍历到 n-1
      • 如果 ratings[i] > ratings[i-1],则 left[i] = left[i-1] + 1。这意味着当前孩子比左侧邻居多一颗糖果。
      • 否则 (ratings[i] <= ratings[i-1]),left[i] 保持为 1 (因为他不需要比左边多,所以最少是1颗)。
  2. 第二次遍历(从右向左):
    • 接下来,我们满足每个孩子相对于其右侧邻居的条件。
    • 创建一个 right 数组 (或者直接在 count 中累加并处理 right 逻辑),right[i] 表示在只考虑右侧邻居的情况下,孩子 i 应得的糖果数。
    • 初始化 right 数组所有元素为 1
    • 从索引 n-2 开始遍历到 0
      • 如果 ratings[i] > ratings[i+1],则 right[i] = right[i+1] + 1。这意味着当前孩子比右侧邻居多一颗糖果。
      • 否则 (ratings[i] <= ratings[i+1]),right[i] 保持为 1
    • 合并结果: 对于每个孩子 i,他最终应得的糖果数是 max(left[i], right[i])。这是因为 left[i] 确保了对左侧邻居的条件满足,right[i] 确保了对右侧邻居的条件满足。取两者最大值可以同时满足这两个独立的条件,并且由于我们是基于"最少一颗"和"比邻居多一颗"递增的,所以取最大值是满足所有条件所需的最小数量。
    • 在第二次遍历中,我们将 max(left[i], right[i]) 累加到总数 count 中。需要注意的是,count 初始值是 left[n-1],这是因为最后一个孩子 n-1 没有右邻居,所以它的糖果数就是 left[n-1](或者说 max(left[n-1], right[n-1]) 也是 left[n-1],因为 right[n-1] 始终为 1)。

题解代码

java 复制代码
import java.util.Arrays; // 导入 Arrays 工具类,用于数组填充

class Solution {
    /**
     * 计算分发糖果的总数,满足以下条件:
     * 1. 每个孩子至少分得一颗糖果。
     * 2. 评分较高的孩子比相邻评分较低的孩子获得更多的糖果。
     *
     * @param ratings 一个整数数组,表示每个孩子的评分。
     * @return 分发糖果的总数。
     */
    public int candy(int[] ratings) {
        int n = ratings.length; // 获取孩子的总人数
        // 如果没有孩子,则不需要分发糖果
        if (n == 0) {
            return 0;
        }
        
        // left 数组:从左向右遍历,记录每个孩子相对于其左侧邻居应得的糖果数。
        // 例如,如果 ratings[i] > ratings[i-1],则 left[i] = left[i-1] + 1。
        int[] left = new int[n];
        // right 数组:从右向左遍历,记录每个孩子相对于其右侧邻居应得的糖果数。
        // 例如,如果 ratings[i] > ratings[i+1],则 right[i] = right[i+1] + 1。
        int[] right = new int[n];
        
        // 初始化:每个孩子至少分得一颗糖果,所以初始都设为1。
        Arrays.fill(left, 1);
        Arrays.fill(right, 1);
        
        // 第一次遍历:从左向右,处理"左邻居"关系
        // 遍历从第二个孩子开始 (索引1),因为第一个孩子 (索引0) 没有左邻居。
        for(int i = 1; i < n; i++) {
            // 如果当前孩子的评分高于其左侧邻居的评分
            if(ratings[i] > ratings[i - 1]) {
                // 则当前孩子比左侧邻居多一颗糖果
                left[i] = left[i - 1] + 1;
            }
        }
        
        // 初始化总糖果数。
        // 最后一个孩子的糖果数,只受左侧规则(left[n-1])影响,因为其没有右邻居需要比较。
        // 或者说,right[n-1] 肯定是1,所以 Math.max(left[n-1], right[n-1]) 就是 left[n-1]。
        int count = left[n - 1]; 
        
        // 第二次遍历:从右向左,处理"右邻居"关系,并累加最终结果
        // 遍历从倒数第二个孩子开始 (索引 n-2),因为倒数第一个孩子 (索引 n-1) 已在上面处理。
        for(int i = n - 2; i >= 0; i--) {
            // 如果当前孩子的评分高于其右侧邻居的评分
            if(ratings[i] > ratings[i + 1]) {
                // 则当前孩子比右侧邻居多一颗糖果
                right[i] = right[i + 1] + 1;
            }
            // 最终每个孩子应得的糖果数,是满足左右两边条件的最大值。
            // 例如,如果孩子A评分比左边B高,left[A]=left[B]+1。
            // 如果孩子A评分比右边C高,right[A]=right[C]+1。
            // 那么A最终应得的糖果数是 max(left[A], right[A]),以同时满足两边要求。
            count += Math.max(left[i], right[i]);
        }
        
        return count; // 返回总的糖果数
    }
}

复杂度分析

假设 n 是孩子的数量(即 ratings.length)。

  1. 时间复杂度:
    • 初始化 leftright 数组:O(n) (使用 Arrays.fill)。
    • 第一次遍历(从左向右):O(n),遍历 n-1 次。
    • 第二次遍历(从右向左):O(n),遍历 n-1 次。
    • 总的时间复杂度是所有这些步骤的总和,即 O(n) + O(n) + O(n) = O(n)
  2. 空间复杂度:
    • left 数组:O(n)
    • right 数组:O(n)
    • 其他变量 (n, i, count):O(1)
    • 总的空间复杂度是 O(n) + O(n) = O(n)
相关推荐
IronMurphy6 小时前
【算法五十二】5. 最长回文子串
算法
Lewiis6 小时前
白话选择排序
数据结构·算法·排序算法
计算机安禾6 小时前
【算法分析与设计】第19篇:二分图匹配与指派问题
大数据·人工智能·算法
学习中.........6 小时前
从傅里叶级数到傅里叶变换:详细的数学推导
算法
宇宙realman_9996 小时前
420B污染度等级查询代码
java·开发语言·算法
Dfreedom.6 小时前
深度学习量化技术全景解析:从校准算法到量化算子的完整指南
人工智能·深度学习·算法·量化·模型加速
如竟没有火炬6 小时前
乘法表中第K小的数——二分
开发语言·数据结构·python·算法·leetcode·职场和发展·动态规划
2401_876964136 小时前
27考研李艳芳网课|王谱2027数学讲义
人工智能·经验分享·深度学习·考研·算法·计算机视觉·概率论
努力的小张.7 小时前
最大子段和(前缀和解决)
算法