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

每日算法-250502

每日算法 - 2025.05.02

记录一下今天刷的几道 LeetCode 算法题。


3191. 使二进制数组全部等于 1 的最少操作次数 I

题目

Problem 3191 Description

思路

贪心

解题过程

遍历数组 nums。当我们遇到 nums[i] 时:

  1. 如果 nums[i] 是 1,我们不需要进行操作,因为目标是全 1,并且之前的元素 [0, i-1] 已经被处理为 1(基于贪心策略)。
  2. 如果 nums[i] 是 0,我们 必须 在此时进行翻转操作。因为这是唯一一次可以影响 nums[i] 的操作(操作窗口是 [i, i+1, i+2])。任何后续的操作都无法再将 nums[i] 从 0 变为 1。因此,我们翻转 nums[i], nums[i+1], nums[i+2],并增加操作计数。

我们只遍历到 n-3,因为操作需要三个连续的元素。当循环结束后,我们需要检查最后两个元素 nums[n-2]nums[n-1]。由于我们无法再对它们执行翻转操作(没有足够的后续元素形成长度为 3 的窗口),它们必须已经是 1。如果其中任何一个是 0,则无法完成目标,返回 -1。否则,返回累计的操作次数。

复杂度

  • 时间复杂度: O(N),其中 N 是数组的长度,我们只需要遍历数组一次。
  • 空间复杂度: O(1),我们只使用了常数级别的额外空间。

Code

class Solution {public int minOperations(int[] nums) {int n = nums.length;int ret = 0;for (int i = 0; i <= n - 3; i++) {if (nums[i] == 0) {nums[i] = 1;nums[i + 1] = 1 - nums[i + 1];nums[i + 2] = 1 - nums[i + 2];ret++;}}if (nums[n - 2] == 0 || nums[n - 1] == 0) {return -1;}return ret;}
}

1827. 最少操作使数组递增

题目

Problem 1827 Description

思路

贪心

解题过程

我们要使数组严格递增,并且操作次数最少。我们可以从左到右遍历数组,比较相邻的两个元素 nums[i]nums[i+1]

  1. 如果 nums[i] < nums[i+1],数组在 ii+1 之间已经是严格递增的,我们不需要做任何操作。
  2. 如果 nums[i] >= nums[i+1],为了满足严格递增的要求,nums[i+1] 必须至少增加到 nums[i] + 1。为了使操作次数最少,我们选择将其恰好增加到 nums[i] + 1。增加的操作次数是 (nums[i] + 1) - nums[i+1]。同时,我们需要更新 nums[i+1] 的值为 nums[i] + 1,以便后续的比较基于更新后的值。

累加所有操作次数即可。

复杂度

  • 时间复杂度: O(N),其中 N 是数组的长度,我们只需要遍历数组一次。
  • 空间复杂度: O(1),我们是在原地修改数组(或者可以只用一个变量记录前一个元素的值),只使用了常数级别的额外空间。

Code

class Solution {public int minOperations(int[] nums) {int n = nums.length;int ret = 0;for (int i = 0; i < n - 1; i++) {if (nums[i] >= nums[i + 1]) {ret += nums[i] + 1 - nums[i + 1];nums[i + 1] = nums[i] + 1;}}return ret;}
}

2027. 转换字符串的最少操作次数

题目

Problem 2027 Description

思路

贪心

解题过程

我们需要将字符串中的所有 ‘X’ 转换成 ‘O’,每次操作可以连续转换 3 个字符。目标是使用最少的操作次数。

我们可以从左到右遍历字符串 s

  1. 如果当前字符 s[i] 是 ‘O’,它已经满足条件,我们不需要操作,直接看下一个字符 s[i+1]
  2. 如果当前字符 s[i] 是 ‘X’,我们必须执行一次操作来转换它。根据贪心策略,这次操作应该尽可能地覆盖更多的 ‘X’。由于操作固定影响 s[i], s[i+1], s[i+2] 这三个字符,我们执行一次操作,将这三个字符(无论它们原来是 ‘X’ 还是 ‘O’)都视为已处理(逻辑上变成 ‘O’),然后将索引 i 直接增加 3(即跳过 i+1i+2),因为这三个位置都已经被这次操作覆盖了。操作次数加 1。

遍历结束后,累计的操作次数就是最少次数。

复杂度

  • 时间复杂度: O(N),其中 N 是字符串的长度。虽然我们可能会跳过字符 (i += 2),但每个字符最多被访问一次。
  • 空间复杂度: O(N)(如果使用 toCharArray())或 O(1)(如果直接操作字符串索引)。Java 中字符串是不可变的,toCharArray() 会创建字符数组副本,空间复杂度为 O(N)。如果语言支持原地修改或仅通过索引访问,则可以是 O(1)

Code

class Solution {public int minimumMoves(String ss) {char[] s = ss.toCharArray();int n = s.length;int ret = 0;for (int i = 0; i < n; /* i 在循环内部更新 */) {// 如果当前字符是 'X'if (s[i] == 'X') {// 需要一次操作ret++;// 这次操作覆盖了 i, i+1, i+2,所以下次从 i+3 开始检查i += 3; } else {// 如果当前字符是 'O',直接检查下一个字符i++;}}return ret;}
}

注意:原代码中 i += 2; 配合 for 循环的 i++ 实际上是 i += 3 的效果,这里修改为在 if 块内直接 i += 3else 块内 i++,更清晰地表达了逻辑。


3397. 执行操作后不同元素的最大数量 (复习)

题目

Problem 3397 Description

这是第二次做这道题了,已经算是掌握了。核心思想是贪心地调整每个数的值,以最大化不同元素的数量。

详细题解见之前的博客:每日算法-250427,里面还有点小优化。

Code

class Solution {public int maxDistinctElements(int[] nums, int k) {Arrays.sort(nums);int ret = 1;nums[nums.length - 1] += k;for (int i = nums.length - 2; i >= 0; i--) {nums[i] = Math.max(Math.min(nums[i] + k, nums[i + 1] - 1), nums[i] - k);if (nums[i] < nums[i + 1]) {ret++;}}return ret;}
}

2476. 二叉搜索树最近节点查询 (复习)

题目

Problem 2476 Description

这道题也是复习,核心思路已经掌握。主要步骤是:

  1. 通过 中序遍历 将二叉搜索树(BST)转换为一个 有序数组
  2. 对于每个查询值 k,在有序数组中使用 二分查找 来找到 min_i (小于等于 k 的最大值) 和 max_i (大于等于 k 的最小值)。

二分查找细节:
通常使用二分查找找到第一个大于等于 k 的元素的索引 idx

  • max_i 就是 nums[idx](如果 idx 没越界)。如果 idx 越界,说明 k 比所有元素都大,max_i 为 -1。
  • min_i
    • 如果 nums[idx] == k,则 min_i = k
    • 如果 nums[idx] > k,则 min_iidx 前一个元素 nums[idx-1](如果 idx > 0)。如果 idx == 0,说明 k 比所有元素都小,min_i 为 -1。
    • 如果 idx 越界(即 k 比所有元素都大),min_i 是数组最后一个元素 nums[n-1]

详细题解见之前的博客:每日算法-250413

Code

class Solution {public List<List<Integer>> closestNodes(TreeNode root, List<Integer> queries) {List<Integer> arr = new ArrayList<>();rootToList(root, arr);int len = arr.size();int[] nums = new int[len];ListToArray(nums, arr);int n = queries.size();int min = 0, max = 0;List<List<Integer>> ret = new ArrayList<>(n);for (int i = 0; i < n; i++) {List<Integer> tmp = new ArrayList<>();int k = queries.get(i);int index = search(nums, k);if (index >= len) {max = -1;min = nums[len - 1];} else {min = index == 0 ? -1 : nums[index - 1];if (nums[index] == k) {min = nums[index];}max = nums[index];}tmp.add(min);tmp.add(max);ret.add(tmp);}return ret;}private void rootToList(TreeNode root, List<Integer> arr) {if (root == null) {return;}rootToList(root.left, arr);arr.add(root.val);rootToList(root.right, arr);}private void ListToArray(int[] nums, List<Integer> arr) {for (int i = 0; i < nums.length; i++) {nums[i] = arr.get(i);}}private int search(int[] nums, int k) {int left = 0, right = nums.length - 1;while (left <= right) {int mid = left + (right - left) / 2;if (nums[mid] < k) {left = mid + 1;} else {right = mid - 1;}}return left;}
}

相关文章:

  • Python爬虫实战:获取好大夫在线各专业全国医院排行榜数据并分析,为患者就医做参考
  • 传统银行服务和 区块链支付无缝融合的一种解决方案
  • 【AI面试准备】数据治理与GDPR脱敏机制构建
  • 4.Java中的注释
  • VBA宏即根据第一列的内容和第二列的数字,按照数字数量生成对应内容并依次放在第三列、第四列等
  • c++环境和vscode常用的一些有用插件
  • Qt C++简单图形界面与绘图实验
  • 开闭原则与依赖倒置原则区别:原类不变,新增类(功能)vs 接口类不变,原实现类可变
  • 算法篇(九)【滑动窗口】
  • 《筑牢防线:全方位守护移动应用免受逆向侵扰》
  • linux下如何在一个录目中将一个文件复制到另一个录目,删除目录
  • A2A 协议与 MCP 协议:智能代理生态系统的双轮驱动
  • 组件通信-自定义事件
  • Dify 安装 使用
  • STM32 ZIBEE DL-20 无线串口模块
  • Linux Netlink机制:现代网络通信的核心
  • C++ set和map
  • 5月2日日记
  • 普通IT的股票交易成长史--20250502 突破(1)
  • 动态规划算法精解(Java实现):从入门到精通
  • 申活观察|人潮涌动成常态,豫园为何常来常新?
  • 长三角铁路今日预计发送390万人次,昨日客发量同比增长10.5%
  • 美国将于6月14日举行阅兵式,美媒报当天是特朗普生日
  • 湖北鄂城:相继4所小学有学生腹泻呕吐,供餐企业负责人已被采取强制措施
  • 赵乐际主持十四届全国人大常委会第十五次会议闭幕会并作讲话
  • 金砖国家外长会晤发表主席声明,强调南方国家合作