中位数是有序序列最中间的那个数。如果序列的长度是偶数,则没有最中间的数;此时中位数是最中间的两个数的平均数。
例如:
[2,3,4],中位数是 3
[2,3],中位数是 (2 + 3) / 2 = 2.5
给你一个数组 nums,有一个长度为 k 的窗口从最左端滑动到最右端。窗口中有 k 个数,每次窗口向右移动 1 位。你的任务是找出每次窗口移动后得到的新窗口中元素的中位数,并输出由它们组成的数组。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sliding-window-median
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
import java.util.*;
class Solution {
public double[] medianSlidingWindow(int[] nums, int k) {
Heap leftHeap = new Heap(nums, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o2, o1);
}
});
Heap rightHeap = new Heap(nums, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
});
double[] ans = new double[nums.length - k + 1];
for (int i = 0; i < nums.length; ++i) {
if (leftHeap.empty() || nums[i] <= nums[leftHeap.peek()]) {
leftHeap.push(i);
} else {
rightHeap.push(i);
}
while (leftHeap.size() < rightHeap.size()) {
leftHeap.push(rightHeap.pop());
}
while (rightHeap.size() + 1 < leftHeap.size()) {
rightHeap.push(leftHeap.pop());
}
if (i >= k - 1) {
int length = (leftHeap.size() + rightHeap.size());
if (length % 2 == 1) {
ans[i - (k - 1)] = nums[leftHeap.peek()];
} else {
ans[i - (k - 1)] = (1D * nums[leftHeap.peek()] + nums[rightHeap.peek()]) / 2.0;
}
leftHeap.delete(i - (k - 1));
rightHeap.delete(i - (k - 1));
}
}
return ans;
}
}
class Heap {
private int[] original;
private Comparator<Integer> comparator;
private List<Integer> data;
private Map<Integer, Integer> indexMap;
private int size = 0;
public Heap(int[] original, Comparator<Integer> comparator) {
this.original = original;
this.comparator = comparator;
this.data = new ArrayList<>();
this.indexMap = new HashMap<>();
}
private int parent(int index) {
return (index - 1) >> 1;
}
private int left(int index) {
return index * 2 + 1;
}
private int right(int index) {
return index * 2 + 2;
}
private int originalData(int index) {
return original[data.get(index)];
}
private void heapifyUp(int index) {
while (index != 0) {
int parent = parent(index);
if (comparator.compare(originalData(index), originalData(parent)) < 0) {
swap(index, parent);
index = parent;
} else {
break;
}
}
}
private void heapifyDown(int index) {
int left, right;
while ((left = left(index)) < this.size) {
int goal = index;
if (comparator.compare(originalData(index), originalData(left)) > 0) {
goal = left;
}
if ((right = right(index)) < this.size && comparator.compare(originalData(goal), originalData(right)) > 0) {
goal = right;
}
if (goal == index) {
break;
}
swap(goal, index);
index = goal;
}
}
public void push(int x) {
if (data.size() > this.size) {
data.set(this.size, x);
} else {
data.add(x);
}
indexMap.put(x, this.size);
heapifyUp(this.size++);
}
private void swap(int idx1, int idx2) {
int x1 = data.get(idx1);
int x2 = data.get(idx2);
data.set(idx1, x2);
data.set(idx2, x1);
indexMap.put(x1, idx2);
indexMap.put(x2, idx1);
}
public void delete(int x) {
if (!indexMap.containsKey(x)) {
return;
}
Integer index = indexMap.get(x);
swap(index, --this.size);
indexMap.remove(x);
heapifyUp(index);
heapifyDown(index);
}
public int peek() {
return data.get(0);
}
public int pop() {
int ans = data.get(0);
swap(0, --size);
indexMap.remove(ans);
heapifyDown(0);
return ans;
}
public int size() {
return size;
}
public boolean empty() {
return this.size == 0;
}
}