当前位置: 首页 > news >正文

力扣(分发糖果)

解析 LeetCode 135. 分发糖果:两次遍历的贪心策略

一、题目分析

在这里插入图片描述

(一)问题定义

给定表示孩子评分的数组 ratings,需给每个孩子分发糖果,满足:

  • 每个孩子至少 1 颗糖果。
  • 相邻孩子中,评分高的孩子糖果数更多。
    目标是求出最少需要准备的糖果总数。

(二)核心挑战

如何在满足规则的前提下,让糖果总数最少。这意味着不能简单地给每个评分高的孩子随意多分配糖果,而要通过合理的策略,在保证规则的同时,尽可能复用已有的糖果分配,避免冗余。

二、算法思想:两次遍历的贪心策略

(一)贪心策略的分步实现

贪心算法的核心是每一步都做出局部最优选择,最终期望得到全局最优。本题中,由于需要同时满足“左相邻”和“右相邻”的规则(即对于一个孩子,要同时比左边评分低的多,比右边评分低的多 ),一次遍历无法兼顾,因此采用两次遍历

  1. 第一次遍历(从左到右 ):处理每个孩子与左边相邻孩子的关系。若当前孩子评分高于左边,那么其糖果数为左边孩子糖果数 + 1;否则保持至少 1 颗(初始化已保证 )。这样能确保满足“左边相邻评分低的孩子糖果少”的规则。
  2. 第二次遍历(从右到左 ):处理每个孩子与右边相邻孩子的关系。若当前孩子评分高于右边,此时需要比较当前已分配的糖果数和右边孩子糖果数 + 1,取较大值(因为要同时满足左边和右边的规则,必须保证比两边评分低的都多 )。这一步确保满足“右边相邻评分低的孩子糖果少”的规则。

通过两次遍历,分别处理左右相邻的约束,最终得到满足所有条件的最少糖果分配。

(二)局部最优到全局最优的推导

  • 第一次遍历的局部最优:保证每个孩子在“左相邻”关系中,评分高的得到更多糖果,此时得到的是仅满足左相邻规则的局部最优分配。
  • 第二次遍历的局部最优:在第一次的基础上,保证每个孩子在“右相邻”关系中,评分高的得到更多糖果,此时修正后的分配同时满足左右相邻规则,最终实现全局最优(糖果总数最少 )。

两次局部最优的选择,共同达成了全局满足规则且糖果总数最少的目标。

三、代码实现与详细解析

class Solution {public int candy(int[] ratings) {// 初始化每个孩子的糖果数为 1,满足“至少 1 颗”的基本要求int[] childSuger = new int[ratings.length];Arrays.fill(childSuger, 1); // 第一次遍历:从左到右,处理与左边相邻孩子的关系for (int len = 1; len < ratings.length; len++) { // 若当前孩子评分高于左边相邻孩子if (ratings[len - 1] < ratings[len]) { // 当前孩子糖果数 = 左边孩子糖果数 + 1childSuger[len] = childSuger[len - 1] + 1; }}// 第二次遍历:从右到左,处理与右边相邻孩子的关系for (int len = ratings.length - 2; len >= 0; len--) { // 若当前孩子评分高于右边相邻孩子if (ratings[len + 1] < ratings[len]) { // 取“当前已分配糖果数”和“右边孩子糖果数 + 1”的较大值,保证同时满足左右规则childSuger[len] = Math.max(childSuger[len], childSuger[len + 1] + 1); }}// 计算糖果总数:遍历累加每个孩子的糖果数int sumSuger = 0; for (int sugerNum : childSuger) { sumSuger += sugerNum; }return sumSuger; }
}

(一)代码流程拆解

  1. 初始化糖果数组:创建 childSuger 数组,用 Arrays.fill 将每个元素初始化为 1,确保每个孩子至少 1 颗糖果。
  2. 第一次遍历(左到右 ):从索引 1 开始遍历(因为要和左边孩子比较 ),若当前孩子评分高于左边,就将其糖果数设为左边孩子糖果数 + 1,这样保证了左边相邻的规则。
  3. 第二次遍历(右到左 ):从索引 ratings.length - 2 开始遍历(和右边孩子比较 ),若当前孩子评分高于右边,就取当前糖果数和右边孩子糖果数 + 1 中的较大值。这一步是为了在第一次遍历的基础上,修正右边相邻的规则,确保同时满足左右两边。
  4. 计算总数:遍历 childSuger 数组,累加得到糖果总数并返回。

(二)关键逻辑解析

  • 两次遍历的必要性:由于需要同时满足左右相邻的规则,一次遍历无法同时处理两个方向的约束。两次遍历分别处理左和右,通过分步优化,最终满足所有条件。
  • Math.max 的作用:在第二次遍历中,当当前孩子评分高于右边时,不能直接赋值为 childSuger[len + 1] + 1,因为第一次遍历可能已经给当前孩子分配了更多的糖果(比如同时满足左边更高的情况 )。所以需要取较大值,保证既满足右边规则,又不破坏左边已经满足的规则,从而实现全局最优。

四、复杂度分析

(一)时间复杂度

  • 两次遍历数组:第一次遍历 for (int len = 1; len < ratings.length; len++) ,第二次遍历 for (int len = ratings.length - 2; len >= 0; len--) ,每次遍历的时间复杂度都是 O(n)O(n)O(n)nratings 数组长度 )。
  • 最后累加糖果总数的遍历,时间复杂度也是 O(n)O(n)O(n)
    总体时间复杂度为 O(n)O(n)O(n) ,线性时间复杂度保证了算法的高效性。

(二)空间复杂度

  • 额外使用了 childSuger 数组,其长度为 n ,所以空间复杂度为 O(n)O(n)O(n) 。如果想优化空间复杂度,理论上可以不用数组,但若要严格遵循题目要求记录每个孩子的糖果数,这种空间开销是必要的。
http://www.dtcms.com/a/338246.html

相关文章:

  • 【完整源码+数据集+部署教程】海洋垃圾与生物识别系统源码和数据集:改进yolo11-RVB
  • 深度优先遍历dfs(模板)
  • VS Code Copilot 完整使用教程(含图解)
  • 【笔记ing】考试脑科学 脑科学中的高效记忆法
  • 图论:Floyd算法
  • 从数学原理推导的角度介绍大语言MOE架构的本质
  • Linux系统WireShark抓取本地网卡报文
  • uv 现代化的虚拟环境管理工具
  • 量化线性层,将原始的fp16/bf16权重加载到Linear8bitLt模块中,调用int8_module.to(“cuda”)量化 (44)
  • 视频讲解:CatBoost、梯度提升 (XGBoost、LightGBM)对心理健康数据、交通流量及股票价格预测研究
  • Dubbo 的SPI
  • 深入解析RabbitMQ与AMQP-CPP:从原理到实战应用
  • IDEA 配置终端提示符样式,通过脚本方式
  • IntelliJ IDEA 开发配置教程
  • WPF---数据模版
  • 监督学习(Supervised Learning)和 无监督学习(Unsupervised Learning)详解
  • PCIe ASPM详解
  • 14.Linux线程(2)线程同步、线程安全、线程与fork
  • 【秋招笔试】2025.08.17大疆秋招机考第一套
  • plantsimulation知识点25.8.18-从一个RGV到另一台RGV,工件长度和宽度方向互换
  • pytest测试框架之基本用法
  • GPT-5之后:当大模型更新不再是唯一焦点
  • 本地搭建dify+deepseek智能体
  • 【unitrix数间混合计算】3.1 零标记trait(zero.rs)
  • 【最后203篇系列】033 Mongo副本集修复过程
  • Maven resources资源配置详解
  • 小程序被爬虫攻击,使用waf能防护吗?
  • Vision Master的C#脚本与opencv联合编程
  • 【opencv-Python学习笔记(7):图像平滑处理】
  • 【图像算法 - 17】慧眼识“果”:基于深度学习与OpenCV的苹果智能检测系统(附完整代码)