|
1 | 1 | package com.inuker.solution;
|
2 | 2 |
|
3 | 3 | import java.util.Collections;
|
| 4 | +import java.util.Comparator; |
| 5 | +import java.util.Map; |
4 | 6 | import java.util.TreeMap;
|
5 | 7 |
|
6 | 8 | /**
|
7 | 9 | * Created by liwentian on 2017/9/12.
|
| 10 | + * https://leetcode.com/articles/sliding-window-median/ |
| 11 | + * 和 #295 Find Median from Data Stream 比较类似 |
8 | 12 | */
|
9 | 13 |
|
10 | 14 | public class SlidingWindowMedian {
|
11 | 15 |
|
| 16 | + /** |
| 17 | + * 这题如果用PriorityQueue则复杂度为O(nk),因为其删除复杂度为O(n) |
| 18 | + * 改为TreeMap会降为O(nlgk) |
| 19 | + */ |
12 | 20 | public double[] medianSlidingWindow(int[] nums, int k) {
|
13 |
| - double[] res = new double[nums.length - k + 1]; |
14 |
| - TreeMap<Integer, Integer> minHeap = new TreeMap<Integer, Integer>(); |
15 |
| - TreeMap<Integer, Integer> maxHeap = new TreeMap<Integer, Integer>(Collections.reverseOrder()); |
| 21 | + MyMap up = new MyMap(); |
| 22 | + MyMap down = new MyMap(Comparator.reverseOrder()); |
| 23 | + double[] result = new double[nums.length - k + 1]; |
| 24 | + for (int i = 0; i < nums.length; i++) { |
| 25 | + up.myAdd(nums[i]); |
| 26 | + down.myAdd(up.myPollFirst()); |
16 | 27 |
|
17 |
| - int minHeapCap = k / 2; //smaller heap when k is odd. |
18 |
| - int maxHeapCap = k - minHeapCap; |
| 28 | + if (i >= k) { |
| 29 | + if (up.containsKey(nums[i - k])) { |
| 30 | + up.myRemove(nums[i - k]); |
| 31 | + } else { |
| 32 | + down.myRemove(nums[i - k]); |
| 33 | + } |
| 34 | + } |
19 | 35 |
|
20 |
| - for (int i = 0; i < k; i++) { |
21 |
| - maxHeap.put(nums[i], maxHeap.getOrDefault(nums[i], 0) + 1); |
| 36 | + if (up.size < down.size) { |
| 37 | + up.myAdd(down.myPollFirst()); |
| 38 | + } |
| 39 | + if (i >= k - 1) { |
| 40 | + result[i - k + 1] = up.size == down.size |
| 41 | + ? ((double) up.firstKey() + down.firstKey()) / 2 : up.firstKey(); |
| 42 | + } |
22 | 43 | }
|
23 |
| - int[] minHeapSize = new int[]{0}; |
24 |
| - int[] maxHeapSize = new int[]{k}; |
25 |
| - for (int i = 0; i < minHeapCap; i++) { |
26 |
| - move1Over(maxHeap, minHeap, maxHeapSize, minHeapSize); |
| 44 | + return result; |
| 45 | + } |
| 46 | + |
| 47 | + class MyMap extends TreeMap<Integer, Integer> { |
| 48 | + int size; |
| 49 | + |
| 50 | + MyMap() { |
| 51 | + super(); |
27 | 52 | }
|
28 | 53 |
|
29 |
| - res[0] = getMedian(maxHeap, minHeap, maxHeapSize, minHeapSize); |
30 |
| - int resIdx = 1; |
| 54 | + MyMap(Comparator<Integer> comparator) { |
| 55 | + super(comparator); |
| 56 | + } |
31 | 57 |
|
32 |
| - for (int i = 0; i < nums.length - k; i++) { |
33 |
| - int addee = nums[i + k]; |
34 |
| - if (addee <= maxHeap.keySet().iterator().next()) { |
35 |
| - add(addee, maxHeap, maxHeapSize); |
36 |
| - } else { |
37 |
| - add(addee, minHeap, minHeapSize); |
38 |
| - } |
| 58 | + void myAdd(int n) { |
| 59 | + put(n, getOrDefault(n, 0) + 1); |
| 60 | + size++; |
| 61 | + } |
39 | 62 |
|
40 |
| - int removee = nums[i]; |
41 |
| - if (removee <= maxHeap.keySet().iterator().next()) { |
42 |
| - remove(removee, maxHeap, maxHeapSize); |
| 63 | + boolean myRemove(int n) { |
| 64 | + int count = getOrDefault(n, 0); |
| 65 | + if (count == 0) { |
| 66 | + return false; |
| 67 | + } |
| 68 | + if (count == 1) { |
| 69 | + remove(n); |
43 | 70 | } else {
|
44 |
| - remove(removee, minHeap, minHeapSize); |
| 71 | + put(n, count - 1); |
45 | 72 | }
|
| 73 | + size--; |
| 74 | + return true; |
| 75 | + } |
46 | 76 |
|
47 |
| - //rebalance |
48 |
| - if (minHeapSize[0] > minHeapCap) { |
49 |
| - move1Over(minHeap, maxHeap, minHeapSize, maxHeapSize); |
50 |
| - } else if (minHeapSize[0] < minHeapCap) { |
51 |
| - move1Over(maxHeap, minHeap, maxHeapSize, minHeapSize); |
| 77 | + int myPollFirst() { |
| 78 | + Map.Entry<Integer, Integer> entry = firstEntry(); |
| 79 | + if (entry.getValue() == 1) { |
| 80 | + pollFirstEntry(); |
| 81 | + } else { |
| 82 | + put(entry.getKey(), entry.getValue() - 1); |
52 | 83 | }
|
53 |
| - |
54 |
| - res[resIdx] = getMedian(maxHeap, minHeap, maxHeapSize, minHeapSize); |
55 |
| - resIdx++; |
| 84 | + size--; |
| 85 | + return entry.getKey(); |
56 | 86 | }
|
57 |
| - return res; |
58 |
| - } |
59 |
| - |
60 |
| - public double getMedian(TreeMap<Integer, Integer> bigHeap, TreeMap<Integer, Integer> smallHeap, int[] bigHeapSize, int[] smallHeapSize) { |
61 |
| - return bigHeapSize[0] > smallHeapSize[0] ? (double) bigHeap.keySet().iterator().next() : ((double) bigHeap.keySet().iterator().next() + (double) smallHeap.keySet().iterator().next()) / 2.0; |
62 |
| - } |
63 |
| - |
64 |
| - //move the top element of heap1 to heap2 |
65 |
| - public void move1Over(TreeMap<Integer, Integer> heap1, TreeMap<Integer, Integer> heap2, int[] heap1Size, int[] heap2Size) { |
66 |
| - int peek = heap1.keySet().iterator().next(); |
67 |
| - add(peek, heap2, heap2Size); |
68 |
| - remove(peek, heap1, heap1Size); |
69 |
| - } |
70 |
| - |
71 |
| - public void add(int val, TreeMap<Integer, Integer> heap, int[] heapSize) { |
72 |
| - heap.put(val, heap.getOrDefault(val, 0) + 1); |
73 |
| - heapSize[0]++; |
74 |
| - } |
75 |
| - |
76 |
| - public void remove(int val, TreeMap<Integer, Integer> heap, int[] heapSize) { |
77 |
| - if (heap.put(val, heap.get(val) - 1) == 1) heap.remove(val); |
78 |
| - heapSize[0]--; |
79 | 87 | }
|
80 | 88 | }
|
0 commit comments