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

📅  最后修改于: 2021-09-22 09:49:15             🧑  作者: Mango

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

例子:

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

这个想法是根据k是偶数还是奇数来维护长度为 (k / 2)(k / 2) + 1的 Pair 对象的两个排序集(minSet 和 maxSet),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

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程。