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

每日算法-250421

每日算法学习 2025-04-21

记录今天学习的几道 LeetCode 算法题,主要涉及贪心策略。


2126. 摧毁小行星

题目描述:

LeetCode Problem 2126 Description

思路

贪心

解题过程

这道题的目标是尽可能多地摧毁小行星。我们可以观察到,摧毁小行星可以增加我们的质量,而更大的质量能让我们摧毁更大的小行星。这是一个正向循环。

因此,一个自然的贪心策略是:优先摧毁质量最小的小行星。这样可以以最小的代价来获得增长,从而更有可能摧毁后续更大的小行星。

具体步骤如下:

  1. 对小行星数组 asteroids 进行升序排序。
  2. 初始化当前质量 MASS (使用 long 类型防止溢出)。
  3. 遍历排序后的小行星数组:
    • 如果当前质量 MASS 小于当前遇到的小行星质量 asteroid,则无法摧毁,直接返回 false
    • 如果可以摧毁,将小行星的质量加到当前质量 MASS 上 (MASS += asteroid)。
  4. 如果成功遍历完所有小行星,说明都可以摧毁,返回 true

注意: 累加质量时,总质量可能会超过 int 的最大值,因此需要使用 long 类型来存储当前质量。

复杂度

  • 时间复杂度: O ( N log ⁡ N ) O(N \log N) O(NlogN),主要来自对小行星数组的排序。
  • 空间复杂度: O ( 1 ) O(1) O(1) O ( log ⁡ N ) O(\log N) O(logN),取决于排序算法使用的额外空间(通常认为原地排序为 O ( 1 ) O(1) O(1) 辅助空间)。

Code

class Solution {public boolean asteroidsDestroyed(int mass, int[] asteroids) {Arrays.sort(asteroids);long MASS = mass;for (int asteroid : asteroids) {if (MASS < asteroid) {return false;}MASS += asteroid;}return true;}
}

2587. 重排数组以得到最大前缀分数

题目描述:

LeetCode Problem 2587 Description

思路

贪心

解题过程

题目要求我们通过重排数组 nums 来最大化其前缀和为正数的数量(即“分数”)。

考虑前缀和的计算方式 prefix[i] = nums[0] + nums[1] + ... + nums[i]。为了让尽可能多的前缀和保持正数,我们应该:

  1. 优先累加正数:正数总会增加前缀和,有助于保持其为正。
  2. 将较大的数放在前面:这样可以使初始的前缀和尽可能大,为后面可能出现的负数提供缓冲。
  3. 将负数(尤其是绝对值大的负数)放在后面:负数会减小前缀和,应尽量推迟它们的影响。

具体步骤:

  1. 对数组 nums 进行排序(升序或降序皆可,后续处理方式不同)。
  2. 如果按升序排序,则从数组末尾(最大元素)开始向前遍历;如果按降序排序,则从头开始遍历。
  3. 维护一个当前前缀和 sum(使用 long 类型防止溢出)。
  4. 遍历过程中,累加当前元素到 sum
  5. 如果 sum > 0,则计数器 ret 加 1。
  6. 如果 sum <= 0,说明从此点开始,后续的前缀和(如果有的话,因为添加了更小的或负数)也不会大于 0 了(因为数组已排序),可以直接停止遍历并返回当前的 ret
  7. 遍历完成后,返回 ret

特殊情况:如果排序后最大的元素(即 nums[nums.length - 1] 如果是升序排序)都小于等于 0,那么不可能有任何正的前缀和,直接返回 0。

注意: 前缀和 sum 可能会溢出 int,需要使用 long 类型。

复杂度

  • 时间复杂度: O ( N log ⁡ N ) O(N \log N) O(NlogN),主要来自排序。
  • 空间复杂度: O ( 1 ) O(1) O(1) O ( log ⁡ N ) O(\log N) O(logN),取决于排序算法使用的额外空间。

Code

class Solution {public int maxScore(int[] nums) {// 1. 升序排序Arrays.sort(nums);// 特殊情况:最大值都 <= 0,不可能有正前缀和if (nums[nums.length - 1] <= 0) {return 0;}// 2. 使用 long 防止溢出long sum = 0;int score = 0;// 3. 从大到小遍历(因为是升序排序,所以从后往前)for (int i = nums.length - 1; i >= 0; i--) {// 4. 累加当前元素sum += nums[i];// 5. 如果当前前缀和 <= 0,后续不可能再 > 0if (sum <= 0) {break;}// 6. 否则,分数 +1score++;}return score;}
}

976. 三角形的最大周长

题目描述:

LeetCode Problem 976 Description

思路

贪心

解题过程

我们要找能组成三角形的三条边的最大周长。

根据三角形的性质,任意两边之和必须大于第三边。即对于边长 a, b, c,需要满足 a + b > c, a + c > b, b + c > a

为了使周长 a + b + c 最大,我们应该尽可能选择较长的边。

一个贪心策略是:

  1. 对数组 nums 进行升序排序
  2. 从最长的边开始尝试。考虑数组中最大的三个数 nums[i], nums[i-1], nums[i-2](其中 i 是数组最后一个元素的索引)。
  3. 检查这三条边是否能构成三角形。由于我们已经排序 (nums[i-2] <= nums[i-1] <= nums[i]),我们只需要检查最短的两边之和是否大于最长边即可,即 nums[i-2] + nums[i-1] > nums[i]。因为其他两个条件 (nums[i-2] + nums[i] > nums[i-1]nums[i-1] + nums[i] > nums[i-2]) 必然成立。
  4. 如果 nums[i-2] + nums[i-1] > nums[i] 成立,那么这三条边构成了当前能找到的最大周长的三角形(因为我们是从最长的边开始尝试的),直接返回它们的和 nums[i] + nums[i-1] + nums[i-2]
  5. 如果不满足条件,说明以 nums[i] 作为最长边无法构成三角形,那么需要放弃 nums[i],尝试下一组可能的最大边组合,即考虑 nums[i-1], nums[i-2], nums[i-3],继续这个过程。也就是将 i 减 1,重复步骤 3。
  6. 从数组末尾向前遍历,直到找到第一个满足条件的三个连续元素,或者遍历完所有可能的三个连续组合(即 i 减小到 2)。
  7. 如果遍历结束都没有找到能组成三角形的三条边,说明无法构成任何三角形,返回 0。

复杂度

  • 时间复杂度: O ( N log ⁡ N ) O(N \log N) O(NlogN),主要来自排序。
  • 空间复杂度: O ( 1 ) O(1) O(1) O ( log ⁡ N ) O(\log N) O(logN),取决于排序算法使用的额外空间。

Code

class Solution {public int largestPerimeter(int[] nums) {// 1. 升序排序Arrays.sort(nums);// 2. 从数组末尾向前遍历,寻找满足条件的三个连续元素// i 指向潜在的最长边for (int i = nums.length - 1; i >= 2; i--) {// 3. 检查最短两边之和是否大于最长边if (nums[i - 2] + nums[i - 1] > nums[i]) {// 4. 如果满足,这就是能组成的最大周长三角形return nums[i] + nums[i - 1] + nums[i - 2];}// 5. 如果不满足,继续向前尝试更小的边组合}// 6. 遍历完成仍未找到,返回 0return 0;}
}

1170. 比较字符串最小字母出现频次 (复习)

题目描述:

LeetCode Problem 1170 Description

这是第二次做这道题了,这次完全理清了思路,非常顺畅地写出来了。详细题解可以参考之前的笔记 每日算法-250408。

Code

class Solution {public int[] numSmallerByFrequency(String[] queries, String[] words) {int qLen = queries.length, wLen = words.length;// 1. 转换数组int[] Queries = new int[qLen];for (int i = 0; i < qLen; i++) {Queries[i] = f(queries[i]);}int[] Words = new int[wLen];for (int i = 0; i < wLen; i++) {Words[i] = f(words[i]);}Arrays.sort(Words);// 2. 二分查找for (int i = 0; i < qLen; i++) {Queries[i] = wLen - searchIndex(Words, Queries[i]);}return Queries;}private int searchIndex(int[] arr, int t) {int left = 0, right = arr.length - 1;while (left <= right) {int mid = left + (right - left) / 2;if (arr[mid] <= t) {left = mid + 1;} else {right = mid - 1;}}return left;}private int f(String s) {char minChar = 'z';int count = 0;for (char ch : s.toCharArray()) {if (ch < minChar) {minChar = ch;count = 1;} else if (ch == minChar) {count++;}}return count;}
}

相关文章:

  • Java 并发包核心机制深度解析:锁的公平性、异步调度、AQS 原理全解
  • 【MySQL】:数据库事务管理
  • JavaEE--2.多线程
  • 把dll模块注入到游戏进程的方法_基于文件修改的注入方式
  • MCP:AI时代的“万能插座”,开启大模型无限可能
  • SvelteKit 最新中文文档教程(22)—— 最佳实践之无障碍与 SEO
  • 进程与线程:02 多进程图像
  • 在统信UOS 1060上实现自动关机
  • 高防IP能抵御哪些类型的网络攻击?
  • Buildroot、BusyBox与Yocto:嵌入式系统构建工具对比与实战指南
  • 辛格迪客户案例 | 苏州富士莱医药GMP培训管理(TMS)项目
  • 深度学习3.3 线性回归的简洁实现
  • XXL-JOB 深入理解教程
  • 【MySQL】表的约束(主键、唯一键、外键等约束类型详解)、表的设计
  • javaSE.二叉查找树和平衡二叉树
  • EMQX安装使用和客户端认证
  • PCIE Spec ---Base Address Registers
  • 13 数据存储单位与 C 语言整数类型:从位到艾字节、常见整数类型及其范围、字面量后缀、精确宽度类型详解
  • 【嵌入式系统设计师(软考中级)】第二章:嵌入式系统硬件基础知识(上)
  • 玩转Docker | 使用Docker部署nullboard任务管理工具
  • 新片|真人版《星际宝贝史迪奇》5月23日与北美同步上映
  • 根据学习教育安排,上海市委中心组专题学习总书记力戒形式主义官僚主义重要论述
  • 世界史圆桌|16-18世纪的跨太平洋贸易
  • 群内“分享”侵权书籍电子版,培训公司被判赔偿出版社2万元
  • 纪念|巴尔加斯·略萨:写作之为命运
  • 深一度|上海半马,展示“体育+”无限可能的路跑狂欢