【LeetCode 每日一题】3634. 使数组平衡的最少移除数目——(解法一)排序+滑动窗口

Problem: 3634. 使数组平衡的最少移除数目

文章目录

  • [1. 整体思路](#1. 整体思路)
      • 核心问题
      • [算法逻辑:排序 + 滑动窗口](#算法逻辑:排序 + 滑动窗口)
  • [2. 完整代码](#2. 完整代码)
  • [3. 时空复杂度](#3. 时空复杂度)
      • [时间复杂度: O ( N log ⁡ N ) O(N \log N) O(NlogN)](#时间复杂度: O ( N log ⁡ N ) O(N \log N) O(NlogN))
      • [空间复杂度: O ( log ⁡ N ) O(\log N) O(logN)](#空间复杂度: O ( log ⁡ N ) O(\log N) O(logN))

1. 整体思路

核心问题

我们需要从数组 nums移除最少 的元素,使得剩余的子数组满足:
max ⁡ ( sub ) ≤ min ⁡ ( sub ) × k \max(\text{sub}) \le \min(\text{sub}) \times k max(sub)≤min(sub)×k

这个问题等价于:找到最长的子序列(或子数组),满足最大值不超过最小值的 k k k 倍

因为我们想移除的越少越好,所以就是要保留的越多越好。

算法逻辑:排序 + 滑动窗口

  1. 排序

    • 首先将数组 nums 从小到大排序。
    • 排序后,对于任意一段连续的子数组 nums[i...j],其最小值必然是 nums[i],最大值必然是 nums[j](或 nums[j-1],取决于窗口定义)。
    • 这使得我们可以在 O ( 1 ) O(1) O(1) 时间内确定任意区间的极值。
  2. 滑动窗口(双指针)

    • 我们枚举每一个元素 nums[i] 作为保留序列的最小值
    • 然后我们需要找到一个最大的范围 (即最大的 j),使得该范围内所有元素都满足 ≤ n u m s [ i ] × k \le nums[i] \times k ≤nums[i]×k。
    • 由于数组已排序,nums[j] 是单调递增的。当 i 向右移动(最小值变大)时,满足条件的上限(最大值)只可能变大或不变,因此 j 指针不需要回退。这就是典型的滑动窗口模式。
    • 对于每个 ij 向右延伸直到不满足条件。
    • 此时,窗口大小 j - i 就是以 nums[i] 为最小值时能保留的最长序列长度。
    • 维护全局最大保留长度 maxKeep
  3. 计算结果

    • 最小移除数量 = 总长度 n - 最大保留数量 maxKeep

2. 完整代码

java 复制代码
import java.util.Arrays;

class Solution {
    // 函数目标:求最小移除数量,使得剩余元素中 max <= min * k
    public int minRemoval(int[] nums, int k) {
        // 1. 排序
        // 这是使用滑动窗口的前提,确保最小值在左,最大值在右
        Arrays.sort(nums);
        
        int maxKeep = 0; // 记录能保留的最大元素个数
        int n = nums.length;
        int j = 0; // 滑动窗口的右边界 (exclusive 或 inclusive 取决于逻辑)
        
        // 2. 遍历每个元素作为窗口的左边界 (即保留序列的最小值)
        for (int i = 0; i < n; i++) {
            // 滑动右边界 j
            // 条件:只要 nums[j] 不超过 nums[i] 的 k 倍,就可以纳入窗口
            // 使用 (long) 转换防止乘法溢出 int 范围
            while (j < n && nums[j] <= (long) nums[i] * k) {
                j++;
            }
            // 此时,窗口范围是 [i, j-1]
            // nums[j] 是第一个不满足条件的数 (或者 j==n)
            // 所以保留的元素个数为 j - i
            maxKeep = Math.max(maxKeep, j - i);
        }
        
        // 3. 返回需要移除的元素个数
        // 总数 - 最多能保留的个数
        return n - maxKeep;
    }
}

3. 时空复杂度

假设数组 nums 的长度为 N N N。

时间复杂度: O ( N log ⁡ N ) O(N \log N) O(NlogN)

  • 排序Arrays.sort 是主导操作,耗时 O ( N log ⁡ N ) O(N \log N) O(NlogN)。
  • 滑动窗口
    • 外层循环 i 从 0 到 N − 1 N-1 N−1。
    • 内层 while 循环中的 j 也是从 0 增加到 N N N。j 指针从不回退。
    • 因此,双指针遍历的总操作次数是 2 N 2N 2N,即 O ( N ) O(N) O(N)。
  • 总计 : O ( N log ⁡ N ) + O ( N ) = O ( N log ⁡ N ) O(N \log N) + O(N) = O(N \log N) O(NlogN)+O(N)=O(NlogN)。

空间复杂度: O ( log ⁡ N ) O(\log N) O(logN)

  • 计算依据
    • 除了常数个变量,主要的额外空间消耗来自于排序的栈空间。
  • 结论 : O ( log ⁡ N ) O(\log N) O(logN)。
相关推荐
爱理财的程序媛6 小时前
openclaw 盯盘实践
算法
MobotStone9 小时前
Google发布Nano Banana 2:更快更便宜,图片生成能力全面升级
算法
颜酱12 小时前
队列练习系列:从基础到进阶的完整实现
javascript·后端·算法
用户57573033462412 小时前
两数之和:从 JSON 对象到 Map,大厂面试官到底在考察什么?
算法
程序猿追12 小时前
“马”上行动:手把手教你基于灵珠平台打造春节“全能数字管家”
算法
ZPC82101 天前
docker 镜像备份
人工智能·算法·fpga开发·机器人
ZPC82101 天前
docker 使用GUI ROS2
人工智能·算法·fpga开发·机器人
琢磨先生David1 天前
Day1:基础入门·两数之和(LeetCode 1)
数据结构·算法·leetcode
颜酱1 天前
栈的经典应用:从基础到进阶,解决LeetCode高频栈类问题
javascript·后端·算法