• 滑动窗口-滑动窗口中位数


    2020-01-10 16:16:41

    问题描述

    中位数是有序序列最中间的那个数。如果序列的大小是偶数,则没有最中间的数;此时中位数是最中间的两个数的平均数。

    例如:

    [2,3,4],中位数是 3

    [2,3],中位数是 (2 + 3) / 2 = 2.5

    给出一个数组 nums,有一个大小为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数,每次窗口向右移动 1 位。你的任务是找出每次窗口移动后得到的新窗口中元素的中位数,并输出由它们组成的数组。

    示例:

    给出 nums = [1,3,-1,-3,5,3,6,7],以及 k = 3。

    窗口位置          中位数
    ---------------       -----
    [1 3 -1] -3 5 3 6 7       1
    1 [3 -1 -3] 5 3 6 7     -1
    1 3 [-1 -3 5] 3 6 7     -1
    1 3 -1 [-3 5 3] 6 7     3
    1 3 -1 -3 [5 3 6] 7     5
    1 3 -1 -3 5 [3 6 7]     6

    因此,返回该滑动窗口的中位数数组 [1,-1,-1,3,5,6]。

    提示:

    你可以假设 k 始终有效,即:k 始终小于输入的非空数组的元素个数。
    与真实值误差在 10 ^ -5 以内的答案将被视作正确答案。

    问题求解

    解法一:双pq

    注意!Java中比较Integer务必使用compareTo,直接相减会出错。

        public double[] medianSlidingWindow(int[] nums, int k) {
            int n = nums.length;
            double[] res = new double[n - k + 1];
            // 排序后的右侧一半数据
            PriorityQueue<Integer> minheap = new PriorityQueue<Integer>((Integer o1, Integer o2) -> o1.compareTo(o2));
            // 排序后的左侧一半数据
            PriorityQueue<Integer> maxheap = new PriorityQueue<Integer>((Integer o1, Integer o2) -> o2.compareTo(o1));
            for (int i = 0; i < n; i++) {
                int num = nums[i];
                if (maxheap.isEmpty() || num <= maxheap.peek()) maxheap.add(num);
                else minheap.add(num);
                if (i >= k) {
                    int remove = nums[i - k];
                    if (maxheap.contains(remove)) maxheap.remove(remove);
                    else minheap.remove(remove);
                }
                while (minheap.size() > maxheap.size()) maxheap.add(minheap.poll());
                while (maxheap.size() > minheap.size() + 1) minheap.add(maxheap.poll());
                if (i >= k - 1) {
                    if (k % 2 != 0) res[i - k + 1] = Double.valueOf(maxheap.peek());
                    else res[i - k + 1] = 0.5 * minheap.peek() + 0.5 * maxheap.peek();
                }
            }
            return res;
        }
    

      

    解法二:插入排序

    leetcode上有一条非常类似的题目,叫sliding window max,那一题可以使用单调队列高效的进行求解。

    本题和上述的题目还是有一点区别的,首先最优的时间复杂度不是O(n),而是O(nlogn),如果要达到O(nlogn)需要自定义bst这个是非常耗时的。有同学使用两个优先队列来进行求解,但是在这个过程中需要维护优先队列,尤其是删除队列中的元素,这个在实现的时候是O(n)的时间复杂度,因此使用两个优先队列并不能很好的降低时间复杂度。

    如何简洁高效的解决问题才是关键。

    首先对于获得中位数,显然需要对数字进行排序。如果每次都重新排序,则每个窗口都需要O(klogk)的时间复杂度,这个是难以接受的。

    事实上,考虑到每次滑动窗口已然有序的事实,如果采用插入排序的思路,那么每次维护排序的性质,就只需要O(n)的时间复杂度,那么和使用两个优先队列的时间复杂度就是一样的了,并且在实际实现的时候要简单和容易很多。

    下面给出使用insert sort的算法来解决该问题的算法,实际的算法表现还是非常不错的。

        public double[] medianSlidingWindow(int[] nums, int k) {
            int n = nums.length;
            double[] res = new double[n - k + 1];
            int[] sorted = new int[k];
            for (int i = 0; i < k; i++) sorted[i] = nums[i];
            Arrays.sort(sorted);
            res[0] = sorted[(k - 1) / 2] * 0.5 + sorted[k / 2] * 0.5;
            for (int i = k; i < n; i++) {
                delete(sorted, nums[i - k]);
                add(sorted, nums[i]);
                res[i - k + 1] = sorted[(k - 1) / 2] * 0.5 + sorted[k / 2] * 0.5;
            }
            return res;
        }
        
        private void delete(int[] nums, int num) {
            int n = nums.length;
            int idx = 0; 
            for (; idx < n; idx++) {
                if (nums[idx] == num) break;
            }
            for (int i = idx + 1; i < n; i++) {
                nums[i - 1] = nums[i];
            }
        }
        
        private void add(int[] nums, int num) {
            int n = nums.length;
            int idx = n - 2;
            for (; idx >= 0; idx--) {
                if (nums[idx] > num) nums[idx + 1] = nums[idx];
                else {
                    nums[idx + 1] = num;
                    break;
                }
            }
            if (idx == -1) nums[0] = num;
        }
    

      

  • 相关阅读:
    VS2010开发工具使用技巧<之简单讲解>
    JS中的数学计算<之简单实例讲解>
    让DIV中文字换行显示
    VS2010编写WebService与在IIS的发布<之简单讲解>
    JSON.parse 与 eval() 对于解析json的问题
    [字符串]与[数组]的互相转换
    A标签实现文件下载功能
    "Chinese_PRC_CI_AS" 和 "Chinese_PRC_90_CI_AI" 之间的排序规则冲突问题
    IE开发人员工具之实用功能讲解
    CSS选择器 < ~ +
  • 原文地址:https://www.cnblogs.com/hyserendipity/p/12176726.html
Copyright © 2020-2023  润新知