题目描述
解题思路
使用排序超时。。。
针对本题,根据以上思路,可以将数据流保存在一个列表中,并在添加元素时 保持数组有序 。此方法的时间复杂度为 O(N) ,其中包括: 查找元素插入位置 O(logN) (二分查找)、向数组某位置插入元素 O(N) (插入位置之后的元素都需要向后移动一位)。
借助 堆 可进一步优化时间复杂度。
借助堆可进一步优化时间复杂度。
建立一个小顶堆A和大顶堆B,各保存列表的一半元素,且规定:
- A保存较大的一半,长度为N / 2(N为偶数)或(N + 1) / 2(N为奇数);
- B保存较小的一半,长度为N / 2(N为偶数)或(N + 1) / 2 (N为奇数);
算法流程:
addNum(num)函数:
- 如果两个堆的大小不相等,表示此时所有数组的长度是奇数。需要向B中插入元素,但为了保证所有的数字都有序,所以需要先在A中插入,排好序后再在B中插入。
- 如果两个堆大小相等,那么插入A,流程也是需要先插入B,再插入A。
假设插入数字 num= 遇到情况 1. 。由于 num 可能属于 “较小的一半” (即属于 B ),因此不能将 nums 直接插入至 A 。而应先将 num 插入至 B ,再将 B 堆顶元素插入至 A 。这样就可以始终保持 A 保存较大一半、 B 保存较小一半。
findMedian() 函数:
- 如果两个堆的大小不相等,表示此时所有数组的长度是奇数。取出A的队头,注意是A,因为在偶数的时候我们插入了A,所以变成了奇数,取就要从A中取。
如果两个堆相等,那么就是偶数,两个堆都取出元素,求平均值即可。
代码
```java class MedianFinder { /**
* 大顶堆和小顶堆实现
*/
Queue
A, B; public MedianFinder() {
A = new PriorityQueue<>(); // 小顶堆,存储较大的一半的值
B = new PriorityQueue<>((x, y) -> y - x); // 大顶堆,存储较小的一半
}
public void addNum(int num) {
if (A.size() != B.size()) {
// 如果两个堆的大小不相等,那么此时就为奇数
// 先将num入A,排好序后,取出A中最小的,再入B
A.add(num);
B.add(A.poll());
} else {
// 如果两个堆的大小不相等,那么此时就为偶数
// 与上相反
B.add(num);
A.add(B.poll());
}
}
public double findMedian() {
// 注意此时不能使用poll,poll出队列之后,后面就乱序了
return A.size() == B.size() ? (A.peek() + B.peek()) / 2.0 : A.peek();
}
} ```