Description:
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.
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.
问题描述:
给定一个数组,有一个大小为k的窗口从数组的最左边往最右边滑动,每次滑动1位,你需要找出窗口对应的中位数
问题分析
我们可以维护两个TreeMap,一个为maxMap,存放小于等于中位数的元素,且键值逆序,一个为minMap,且键值顺序,存放大于中位数的元素
令数组长度为len.那么minMap的size为len / 2,maxMap的size为 len - len / 2
我们在遍历数组的过程中维护这两个map,若len为奇数,那么返回maxMap的第一个元素,若
len为偶数,返回maxMap与minMap第一个元素的平均数
解法1(使用TreeMap):
class Solution {
public double[] medianSlidingWindow(int[] nums, int k) {
int len = nums.length;
double[] res = new double[len - k + 1];
TreeMap minMap = new TreeMap();
TreeMap maxMap = new TreeMap(Collections.reverseOrder());
//minMap的元素个数,注意这个值是不会变的
int minCap = k / 2;
//maxMap的元素个数,注意这个值是不会变的
int maxCap = k - minCap;
for(int i = 0;i < k;i++){
maxMap.put(nums[i], maxMap.getOrDefault(nums[i], 0) + 1);
}
int[] minSize = new int[]{0};
int[] maxSize = new int[]{k};
for(int i = 0;i < minCap;i++){
moveOneToOther(maxMap, minMap, maxSize, minSize);
}
int residx = 0;
res[residx++] = getMedian(maxMap, minMap, maxSize, minSize);
for(int i = 0;i < len - k;i++){
int add = nums[i + k];
//注意这里,添加元素时需要满足maxMap保存小于等于中位数的元素
//minMap保存大于中位数的元素
if(add <= maxMap.keySet().iterator().next()){
addOne(add, maxMap, maxSize);
}else{
addOne(add, minMap, minSize);
}
int remove = nums[i];
//删除的逻辑与添加一致
if(remove <= maxMap.keySet().iterator().next()){
removeOne(remove, maxMap, maxSize);
}else{
removeOne(remove, minMap, minSize);
}
//需要维护minMap和maxMap的size
if(minSize[0] > minCap){
moveOneToOther(minMap, maxMap, minSize, maxSize);
}else if(minSize[0] < minCap){
moveOneToOther(maxMap, minMap, maxSize, minSize);
}
res[residx++] = getMedian(maxMap, minMap, maxSize, minSize);
}
return res;
}
public double getMedian(TreeMap maxMap, TreeMap minMap, int[] maxSize, int[] minSize){
return maxSize[0] > minSize[0] ? (double)maxMap.keySet().iterator().next() : ((double)maxMap.keySet().iterator().next() + (double)minMap.keySet().iterator().next()) / 2.0;
}
public void addOne(int num, TreeMap map, int[] size){
map.put(num, map.getOrDefault(num, 0) + 1);
size[0]++;
}
public void moveOneToOther(TreeMap map1, TreeMap map2, int[] size1, int[] size2){
int peek = map1.keySet().iterator().next();
addOne(peek, map2, size2);
removeOne(peek, map1, size1);
}
public void removeOne(int num, TreeMap map, int[] size){
if(map.put(num, map.getOrDefault(num, 0) - 1) == 1) map.remove(num);
size[0]--;
}
}
解法2(使用PriorityQueue):
/*
维护两个PriorityQueue,maxHeap保存小于中位数的元素且逆序,minHeap保存大于等于中位数的元素且顺序
*/
public class Solution {
PriorityQueue minHeap = new PriorityQueue();
PriorityQueue maxHeap = new PriorityQueue(
new Comparator(){
public int compare(Integer i1, Integer i2){
return i2.compareTo(i1);
}
}
);
public double[] medianSlidingWindow(int[] nums, int k) {
int n = nums.length - k + 1;
if (n <= 0) return new double[0];
double[] result = new double[n];
for(int i = 0; i <= nums.length; i++){
if(i >= k){
result[i - k] = getMedian();
remove(nums[i - k]);
}
if(i < nums.length) add(nums[i]);
}
return result;
}
private void add(int num){
if(num < getMedian()) maxHeap.add(num);
else minHeap.add(num);
if(maxHeap.size() > minHeap.size()) minHeap.add(maxHeap.poll());
if(minHeap.size() - maxHeap.size() > 1) maxHeap.add(minHeap.poll());
}
private void remove(int num){
if(num < getMedian()) maxHeap.remove(num);
else minHeap.remove(num);
if(maxHeap.size() > minHeap.size()) minHeap.add(maxHeap.poll());
if(minHeap.size() - maxHeap.size() > 1) maxHeap.add(minHeap.poll());
}
private double getMedian(){
if(maxHeap.isEmpty() && minHeap.isEmpty()) return 0;
if(maxHeap.size() == minHeap.size()) return ((double)maxHeap.peek() + (double)minHeap.peek()) / 2.0;
else return (double)minHeap.peek();
}
}