-
题目:
- 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。
- 返回 滑动窗口中的最大值 。
- 1 <= nums.length <= 10^5
- -10^4 <= nums[i] <= 10^4
- 1 <= k <= nums.length
-
解法一:
- 使用 TreeSet 存储滑动窗口内的数字、它底层是红黑树(TreeMap 包装的),可在 O(logn) 时间复杂度添加与删除对应值、找到最大值。
- 但是因为有相同数字、添加只会保留一个数,导致删除时多删了后面的数,因此加入每个数都加上不同的小数。此时添加与删除都是不同的数,同时比较时、小数不同不会影响整数不分,也不会影响整数的大小比较情况,注意整数相同取哪个都可以。
- 注意每个数小数必须不同,因此考虑根据每个数下标来设置,[0, n) 除以大于等于 n 的最小的 10 的次方更加方便与不易有精度问题,
- 注意如果是负数,则需要减去小数,否则负数的整数部分会被改变,
- 注意个数最大是十万个,所以小数标识最大就是十万,不会超过 int
代码一:
/**
* 使用 TreeSet 存储滑动窗口内的数字、它底层是红黑树(TreeMap 包装的),可在 O(logn) 时间复杂度添加与删除对应值、找到最大值
* 但是因为有相同数字、添加只会保留一个数,导致删除时多删了后面的数,因此加入每个数都加上不同的小数,
* 此时添加与删除都是不同的数,同时比较时、小数不同不会影响整数不分,也不会影响整数的大小比较情况,注意整数相同取哪个都可以,
* 每个数小数必须不同,因此考虑根据每个数下标来设置,[0, n) 除以大于等于 n 的最小的 10 的次方更加方便与不易有精度问题,
* 还要注意如果是负数,则需要减去小数,否则负数的整数部分会被改变,
* 注意个数最大是十万个,所以小数标识最大就是十万,不会超过 int
*/
public int[] maxSlidingWindow(int[] nums, int k) {
// 判空
if (nums == null || nums.length < k || k <= 0) {
return null;
}
int numsLen = nums.length;
int[] res = new int[numsLen - k + 1];
// 大于等于 numsLen 的最小的 10 的次方
int numsLenTen = getMinNumsLenTen(numsLen);
// 使用 TreeSet 存储滑动窗口、它底层是红黑树(TreeMap 包装的),可在 O(logn) 时间复杂度添加与删除对应值、找到最大值
TreeSet<Double> slidingWindowSet = new TreeSet<>();
for (int i = 0; i < k - 1; i++) {
double numDecimal = getNumDecimal(nums[i], i, numsLenTen);
slidingWindowSet.add(numDecimal);
}
slidingWindowSet.forEach(e -> System.out.print(e + " "));
System.out.println();
for (int i = k - 1; i < numsLen; i++) {
double numDecimal = getNumDecimal(nums[i], i, numsLenTen);
slidingWindowSet.add(numDecimal);
slidingWindowSet.forEach(e -> System.out.print(e + " "));
System.out.println();
res[i - k + 1] = slidingWindowSet.last().intValue();
double numPrevDecimal = getNumDecimal(nums[i - k + 1], i - k + 1, numsLenTen);
slidingWindowSet.remove(numPrevDecimal);
}
return res;
}
/**
* 通过下标 i 和除以的数 numsLenTen,以及原数 num,获取调整后的小数
*/
private double getNumDecimal(int num, int i, int numsLenTen) {
double res = 0;
if (num >= 0) {
res = num + (double)i / numsLenTen;
} else {
res = num - (double)i / numsLenTen;
}
return res;
}
/**
* 大于等于 numsLen 的最小的 10 的次方
*/
private int getMinNumsLenTen(int numsLen) {
int res = 10;
while (res < numsLen) {
res *= 10;
}
return res;
}
- 解法二:
- 在第一种 TreeSet 的基础上,我们可以优化下,TreeSet 直接存储不会相同的下标,然后重写比较器、比较下标对应的数组的值即可,
- 注意比较器返回 0 会控制删除,需要保证下标相等才返回 0
代码二:
/**
* 在第一种 TreeSet 的基础上,我们可以优化下,TreeSet 直接存储不会相同的下标,然后重写比较器、比较下标对应的数组的值即可,
* 注意比较器返回 0 会控制删除,需要保证下标相等才返回 0
*/
public int[] maxSlidingWindowOptimize(int[] nums, int k) {
// 判空
if (nums == null || nums.length < k || k <= 0) {
return null;
}
int numLen = nums.length;
int[] res = new int[numLen - k + 1];
TreeSet<Integer> numsTreeSet = new TreeSet<>(new Comparator<Integer>() {
// 不管等于的升序
@Override
public int compare(Integer o1, Integer o2) {
// 不要用加减法、避免越界
if (nums[o1] > nums[o2]) {
return 1;
} else if (nums[o1] < nums[o2]){
return -1;
} else {
return Integer.compare(o1, o2);
}
}
});
for (int i = 0; i < k - 1; i++) {
numsTreeSet.add(i);
}
for (int i = k - 1; i < numLen; i++) {
numsTreeSet.add(i);
// numsTreeSet.forEach(e -> System.out.print(e + " "));
// System.out.println();
res[i - k + 1] = nums[numsTreeSet.last()];
numsTreeSet.remove(i - k + 1);
}
return res;
}