Leetcode 239. 滑动窗口最大值(TreeSet 解法)

  • 题目:

    • 给你一个整数数组 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;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值