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 倍 。
因为我们想移除的越少越好,所以就是要保留的越多越好。
算法逻辑:排序 + 滑动窗口
-
排序:
- 首先将数组
nums从小到大排序。 - 排序后,对于任意一段连续的子数组
nums[i...j],其最小值必然是nums[i],最大值必然是nums[j](或nums[j-1],取决于窗口定义)。 - 这使得我们可以在 O ( 1 ) O(1) O(1) 时间内确定任意区间的极值。
- 首先将数组
-
滑动窗口(双指针):
- 我们枚举每一个元素
nums[i]作为保留序列的最小值。 - 然后我们需要找到一个最大的范围 (即最大的
j),使得该范围内所有元素都满足 ≤ n u m s [ i ] × k \le nums[i] \times k ≤nums[i]×k。 - 由于数组已排序,
nums[j]是单调递增的。当i向右移动(最小值变大)时,满足条件的上限(最大值)只可能变大或不变,因此j指针不需要回退。这就是典型的滑动窗口模式。 - 对于每个
i,j向右延伸直到不满足条件。 - 此时,窗口大小
j - i就是以nums[i]为最小值时能保留的最长序列长度。 - 维护全局最大保留长度
maxKeep。
- 我们枚举每一个元素
-
计算结果:
- 最小移除数量 = 总长度
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)。