LeetCode刷题记录----295.数据流的中位数(Hard) - 指南
2025-09-17 13:57 tlnshuju 阅读(8) 评论(0) 收藏 举报2025/9/17
题目(Hard):

我的思路:
我这两个思路都超时的,建议不要看,直接去看优化思路。
思路一:用一个堆来存储加入的数字,然后每次要获取中位数的时候,弹出来一半的元素并存粗到一个列表中,根据是堆中是奇数还是偶数个元素,来获取中位数是列表末尾的值,还是列表末尾和倒数第二个值的平均值。最后再把列表中的元素塞回堆中。
public class MedianFinder {
private PriorityQueue heap;
public MedianFinder() {
heap = new PriorityQueue();
}
public void AddNum(int num) {
heap.Enqueue(num, num);
}
public double FindMedian() {
int count = heap.Count;
List temp = new List();
for(int i = 0; i <= count/2; i++){
temp.Add(heap.Dequeue());
}
double res;
if(count % 2 != 0)
res = (double)temp.Last();
else{
res = (temp[temp.Count-1] + temp[temp.Count-2])/2.0;
}
for(int i = 0; i < temp.Count; i++){
heap.Enqueue(temp[i], temp[i]);
}
return res;
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.AddNum(num);
* double param_2 = obj.FindMedian();
*/
一看就很麻烦,不要学这个
思路二:直接用一个列表来存储加入元素,然后每次加入的时候对列表进行排序。获取中位数的时候直接获取有序列表的中间位置值即可。
这里排序用快速排序了
public class MedianFinder {
private List nums;
public MedianFinder() {
nums = new List();
}
public void AddNum(int num) {
nums.Add(num);
//快速排序
Qsort(0, nums.Count-1);
}
public double FindMedian() {
int count = nums.Count;
double res = 0;
if(count%2 != 0)
res = (double)nums[count/2];
else
res = (nums[count/2] + nums[count/2-1])/2.0;
return res;
}
private void Qsort(int left, int right)
{
if(left >= right) return;
int randomIndex = new Random().Next(left, right+1);
int privot = nums[randomIndex];
Swap(randomIndex, right);
int store_index = left;
for(int i = left; i < right; i++){
if(nums[i] < privot){
Swap(store_index, i);
store_index++;
}
}
Swap(store_index, right);
//然后再递归对左右区间也这样进行排序
Qsort(left, store_index-1);
Qsort(store_index+1, right);
}
private void Swap(int i, int j){
if(i == j) return;
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.AddNum(num);
* double param_2 = obj.FindMedian();
*/
也超时的
优化思路:
利用两个堆,一个大根堆,一个小根堆。
大根堆存储数据中较小的一半值
小根堆存储数据中较大的一半值
一直保持大根堆中的所有元素 <= 小根堆中所有元素

大概这个样子,如果做到了的话,我们每次获取中位数,只要看两个堆的堆顶元素即可。
添加的步骤:
①先加入大根堆(默认它是比较小的值先)
②然后让大根堆出堆一个元素,加入到小根堆中(保持小根堆中元素都比大根堆的大)
③检查两个堆中元素是否均衡,如果大根堆元素数量 < 小根堆中元素数量:就把小根堆的一个元素出堆后加入大根堆(为了保证中位数的开始位置一定是在大根堆堆顶位置)
获取中位数的步骤:
如果总数量为奇数,则直接获取大根堆堆顶元素
如果总数量为偶数,则直接获取大根堆和小根堆堆顶元素的平均值
具体代码如下:
public class MedianFinder {
//通过双堆(一个最大堆,一个最小堆)
//最大堆中存储数据中较小的一半,最大堆中存储数据较大的一半
//一直维持最小堆中的元素都>=最大堆中的元素
private PriorityQueue maxHeap;
private PriorityQueue minHeap;
public MedianFinder() {
maxHeap = new PriorityQueue();
minHeap = new PriorityQueue();
}
public void AddNum(int num) {
//先加入最大堆
maxHeap.Enqueue(num, -num);
//然后把最大堆的堆顶元素加入最小堆中(保持最小堆中元素恒大于等于最大堆)
num = maxHeap.Dequeue();
minHeap.Enqueue(num, num);
//之后维持两个堆中数量的均衡
if(maxHeap.Count != minHeap.Count){
num = minHeap.Dequeue();
maxHeap.Enqueue(num, -num);
}
}
public double FindMedian() {
int totalCount = maxHeap.Count + minHeap.Count;
if(totalCount % 2 != 0)
return (double)maxHeap.Peek();
else
return (maxHeap.Peek() + minHeap.Peek())/2.0;
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.AddNum(num);
* double param_2 = obj.FindMedian();
*/
时间复杂度:添加元素O(logN),查找中位数O(1)
空间复杂度:O(N)
其他:
官解中还有个有序数组 + 双指针的思路,但是我脑子有点不够用了,先不看了
总结:
①这题很巧妙地利用了大小根堆的性质,用大根堆和小根堆分别存储一半数据的方式,来达到使得中位数部分有序,并能够快速获取的功能
浙公网安备 33010602011771号