前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >leetcode480. Sliding Window Median

leetcode480. Sliding Window Median

作者头像
眯眯眼的猫头鹰
发布2020-05-11 17:33:55
3730
发布2020-05-11 17:33:55
举报

题目要求

Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value.

Examples:

2,3,4 , the median is 3

2,3, the median is (2 + 3) / 2 = 2.5

Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Your job is to output the median array for each window in the original array.

For example,

Given nums = 1,3,-1,-3,5,3,6,7, and k = 3.

代码语言:javascript
复制
Window position                Median
---------------               -----
[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

Therefore, return the median sliding window as 1,-1,-1,3,5,6.

Note:

You may assume k is always valid, ie: k is always smaller than input array's size for non-empty array.

假设现在有一个长度为n的整数数组,和长度为k的滑动窗口(k一定小于等于n)。要求找出在每个窗口中的中位数。中位数是指将一组数字按照从小到大排序,位于中间位置上的数值。如果n为奇数,则中位数是第n/2+1个数,否则中位数是第n/2个数和第n/2+1个数的和的平均值。

思路和代码

可以先回顾一下Leetcode上另一个求中位数的题目。这道题目是求一个数据流在任意一个时刻的中位数。这道题目也可以复用那一题的思路。即通过两个优先队列分别始终保存该数组小的一半的所有数字和大的一半的所有数字。只是在这题中需要不断的移出已经不在这个窗口中的数字,并加入新流入窗口的数字。代码如下:

代码语言:javascript
复制
    public double[] medianSlidingWindow(int[] nums, int k) {
        Queue<Integer> small = new PriorityQueue<>(Comparator.reverseOrder());
        Queue<Integer> large = new PriorityQueue<>();

        double[] result = new double[nums.length - k + 1];
        boolean isEven = k % 2 == 0;
        for (int i = 0; i < nums.length; i++) {
            large.offer(nums[i]);
            small.offer(large.poll());
            if (large.size() < small.size()) {
                large.offer(small.poll());
            }
            if (small.size() + large.size() < k) {
                continue;
            }

            double median = isEven ? ((double) large.peek() + (double) small.peek()) / 2.0 : (double) large.peek();
            result[i - k + 1] = median;
            if (large.peek() <= nums[i - k + 1]) {
                large.remove(nums[i - k + 1]);
            } else {
                small.remove(nums[i - k + 1]);
            }
        }
        return result;
    }

在最优解中还有另一种思路。而这种思路理论上也是这题最直观的思路。其实这题一共有三个诉求,即快速的在一个有序的数据结构中写入和删除数据,并且能够快速的在有序的数据结构中找到第k个元素。因此我们可以通过一个二叉查找树来实现在lgN的时间内找到第k个元素,并插入/删除某一个值的元素。它通过在二叉查找树的每一个节点记录左子树的节点个数,来帮助快速定位位于有序数组中第k个元素的位置。

代码语言:javascript
复制
    Node root;

    public double[] medianSlidingWindow(int[] nums, int k) {
        if (nums.length == 0)
            return new double[0];
        if (k == 1) {
            double[] medians = new double[nums.length];
            for (int i = 0; i < nums.length; ++i)
                medians[i] = (double) nums[i];
            return medians;
        }
        double[] medians = new double[nums.length - k + 1];

        for (int i = 0; i < k; ++i) {
            insert(nums[i]);
        }

        if (k % 2 == 1) {
            medians[0] = (double) search(root, k / 2 + 1, 0);
        } else {
            medians[0] = ((double) search(root, k / 2, 0) + (double) search(root, k / 2 + 1, 0)) / 2;
        }

        for (int i = k; i < nums.length; ++i) {
            delete(root, nums[i - k]);
            insert(nums[i]);
            if (k % 2 == 1) {
                medians[i - k + 1] = (double) search(root, k / 2 + 1, 0);
            } else {
                medians[i - k + 1] = ((double) search(root, k / 2, 0) + (double) search(root, k / 2 + 1, 0)) / 2;
            }
        }

        return medians;
    }

    private static class Node {
        int key;
        int cnt;
        int leftSize;
        Node left;
        Node right;

        public Node(int k) {
            key = k;
            cnt = 1;
            leftSize = 0;
            left = null;
            right = null;
        }
    }

    private void insert(int k) {
        Node x = root;
        if (x == null) {
            root = new Node(k);
            return;
        }
        while (true) {
            if (x.key == k) {
                x.cnt++;
                return;
            } else if (x.key > k) {
                x.leftSize++;
                if (x.left == null) {
                    x.left = new Node(k);
                    return;
                }
                x = x.left;
            } else {
                if (x.right == null) {
                    x.right = new Node(k);
                    return;
                }
                x = x.right;
            }
        }
    }

    private void delete(Node x, int k) {
        if (x == null)
            return;
        if (x.key == k) {
            x.cnt--;
        } else if (k < x.key) {
            x.leftSize--;
            delete(x.left, k);
        } else {
            delete(x.right, k);
        }
    }

    private int search(Node x, int n, int presum) {
        if (n > x.cnt + x.leftSize + presum)
            return search(x.right, n, x.cnt + x.leftSize + presum);
        else if (n > x.leftSize + presum)
            return x.key;
        else
            return search(x.left, n, presum);
    }
    public static void main(String[] args) {
        SlidingWindowMedian_480 s = new SlidingWindowMedian_480();
        s.medianSlidingWindow(
                new int[] {1,3,-1,-3,5,3,6,7}, 3);
    }
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目要求
  • 思路和代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档