给定一个由整数arr []和一个整数k组成的数组,任务是找到大小为k的每个窗口的中位数,该窗口的大小从左开始,每次向右移一个位置。
例子:
Input: arr[] = {-1, 5, 13, 8, 2, 3, 3, 1}, k = 3
Output: 5 8 8 3 3 3
Input: arr[] = {-1, 5, 13, 8, 2, 3, 3, 1}, k = 4
Output: 6.5 6.5 5.5 3.0 2.5
方法:创建一个配对类来保存项目及其索引。它还实现了可比较的接口,以便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