📜  数组中滑动窗口的中位数

📅  最后修改于: 2021-05-04 22:05:43             🧑  作者: Mango

给定一个由整数arr []和一个整数k组成的数组,任务是找到大小为k的每个窗口的中位数,该窗口的大小从左开始,每次向右移一个位置。

例子:

方法:创建一个配对类来保存项目及其索引。它还实现了可比较的接口,以便Treeset将调用compareTo()方法以查找节点。注意,只有当它们的索引相等时,这两对才相等。这很重要,因为窗口可能包含重复项,并且如果我们仅检查值,则最终可能会在单个remove()调用中删除多个项目。

这个想法是要维护两个对对象的排序集(minSet和maxSet),它们的对对象的长度分别为(k / 2)(k / 2)+1,具体取决于k是偶数还是奇数,minSet将始终包含第一组数字(较小的窗口k和maxSet将包含第二组数字(较大)。

当我们移动窗口时,我们将从其中两个集合中删除元素(log n),并添加一个新元素(log n),以维护上面指定的minSet和maxSet规则。

下面是上述方法的实现:

// Java implementation of the approach
import java.util.TreeSet;
  
public class GFG {
  
    // Pair class for the value and its index
    static class Pair implements Comparable {
        private int value, index;
  
        // Constructor
        public Pair(int v, int p)
        {
            value = v;
            index = p;
        }
  
        // This method will be used by the treeset to
        // search a value by index and setting the tree
        // nodes (left or right)
        @Override
        public int compareTo(Pair o)
        {
  
            // Two nodes are equal only when
            // their indices are same
            if (index == o.index) {
                return 0;
            }
            else if (value == o.value) {
                return Integer.compare(index, o.index);
            }
            else {
                return Integer.compare(value, o.value);
            }
        }
  
        // Function to return the value
        // of the current object
        public int value()
        {
            return value;
        }
  
        // Update the value and the position
        // for the same object to save space
        public void renew(int v, int p)
        {
            value = v;
            index = p;
        }
  
        @Override
        public String toString()
        {
            return String.format("(%d, %d)", value, index);
        }
    }
  
    // Function to print the median for the current window
    static void printMedian(TreeSet minSet,
                            TreeSet maxSet, int window)
    {
  
        // If the window size is even then the
        // median will be the average of the
        // two middle elements
        if (window % 2 == 0) {
            System.out.print((minSet.last().value()
                              + maxSet.first().value())
                             / 2.0);
            System.out.print(" ");
        }
  
        // Else it will be the middle element
        else {
            System.out.print(minSet.size() > maxSet.size()
                                 ? minSet.last().value()
                                 : maxSet.first().value());
            System.out.print(" ");
        }
    }
  
    // Function to find the median
    // of every window of size k
    static void findMedian(int arr[], int k)
    {
        TreeSet minSet = new TreeSet<>();
        TreeSet maxSet = new TreeSet<>();
  
        // To hold the pairs, we will keep renewing
        // these instead of creating the new pairs
        Pair[] windowPairs = new Pair[k];
  
        for (int i = 0; i < k; i++) {
            windowPairs[i] = new Pair(arr[i], i);
        }
  
        // Add k/2 items to maxSet
        for (int i = 0; i < k / 2; i++) {
            maxSet.add(windowPairs[i]);
        }
  
        for (int i = k / 2; i < k; i++) {
  
            // Below logic is to maintain the
            // maxSet and the minSet criteria
            if (arr[i] < maxSet.first().value()) {
                minSet.add(windowPairs[i]);
            }
            else {
                minSet.add(maxSet.pollFirst());
                maxSet.add(windowPairs[i]);
            }
        }
  
        printMedian(minSet, maxSet, k);
  
        for (int i = k; i < arr.length; i++) {
  
            // Get the pair at the start of the window, this
            // will reset to 0 at every k, 2k, 3k, ...
            Pair temp = windowPairs[i % k];
            if (temp.value() <= minSet.last().value()) {
  
                // Remove the starting pair of the window
                minSet.remove(temp);
  
                // Renew window start to new window end
                temp.renew(arr[i], i);
  
                // Below logic is to maintain the
                // maxSet and the minSet criteria
                if (temp.value() < maxSet.first().value()) {
                    minSet.add(temp);
                }
                else {
                    minSet.add(maxSet.pollFirst());
                    maxSet.add(temp);
                }
            }
            else {
                maxSet.remove(temp);
                temp.renew(arr[i], i);
  
                // Below logic is to maintain the
                // maxSet and the minSet criteria
                if (temp.value() > minSet.last().value()) {
                    maxSet.add(temp);
                }
                else {
                    maxSet.add(minSet.pollLast());
                    minSet.add(temp);
                }
            }
  
            printMedian(minSet, maxSet, k);
        }
    }
  
    // Driver code
    public static void main(String[] args)
    {
        int[] arr = new int[] { 0, 9, 1, 8, 2,
                                7, 3, 6, 4, 5 };
        int k = 3;
  
        findMedian(arr, k);
    }
}
输出:
1 8 2 7 3 6 4 5