实现一种栈,它可以快速查找堆栈中所有元素的中值。对于N个元素,若N是偶数,则中值定义为第N/2个最小元素;若N是奇数,则中值定义为第(N+1)/2个最小元素
用快排三个测试点超时不过,做不出来。
看了柳神的树状数组+二分查找解法后学习了一种高级数据结构:树状数组,在面对动态数据流的时候区间查询和修改都只要(O(logn)),强烈推荐这篇:树状数组(详细分析+应用),看不懂打死我!
除此之外,问了GPT,GPT给出了个求中位数的好方法,使用大根堆和小根堆,个人觉得非常巧妙,操作的时间复杂度都只要O(logn):
以下是通过小根堆和大根堆来找到中位数的思路:
使用两个堆:
- 大根堆(max-heap): 存储较小的一半数据,堆顶是这一部分的最大值。
- 小根堆(min-heap): 存储较大的一半数据,堆顶是这一部分的最小值。
平衡堆:
- 当插入一个新元素时,如果它比大根堆的堆顶小,插入大根堆;否则插入小根堆。
- 插入后,如果两个堆的大小相差超过1,则将多余元素从较大的堆中移动到较小的堆中,以保持两个堆的平衡。
查找中位数:
- 如果两个堆的大小相同,中位数是两个堆顶的平均值。
- 如果堆的大小不同,中位数是元素更多的那个堆的堆顶。
我按照这个思路尝试实现了下,发现能过,这里如果直接用priority_queue的话,由于priority_queue不能直接删除特定元素,不是很方便,所以这里用multiset来表示大小根堆(minHeap.begin()的元素在minHeap中最小,prev(maxHeap.end())的元素在maxHeap中最大)
AC代码:
#include <bits/stdc++.h>
using namespace std;
stack<int> s;
multiset<int> maxHeap, minHeap;
void pushNum(int num)
{
if (maxHeap.empty() || num <= (*prev(maxHeap.end())))
maxHeap.insert(num);
else
minHeap.insert(num);
if (maxHeap.size() > minHeap.size() + 1)
{ // 调整
minHeap.insert(*prev(maxHeap.end()));
maxHeap.erase((prev(maxHeap.end())));
}
else if (minHeap.size() > maxHeap.size())
{
maxHeap.insert(*minHeap.begin());
minHeap.erase(minHeap.begin());
}
}
void popNum(int num)
{
if (num <= (*prev(maxHeap.end())))
maxHeap.erase(maxHeap.find(num));
else
minHeap.erase(minHeap.find(num));
if (maxHeap.size() > minHeap.size() + 1)
{ // 调整
minHeap.insert(*prev(maxHeap.end()));
maxHeap.erase(prev(maxHeap.end()));
}
else if (minHeap.size() > maxHeap.size())
{
maxHeap.insert(*minHeap.begin());
minHeap.erase(minHeap.begin());
}
}
int main()
{
int n, key;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
char str[15];
scanf("%s", &str);
if (str[1] == 'o')
{
if (s.empty())
printf("Invalid\n");
else
{
printf("%d\n", s.top());
popNum(s.top());
s.pop();
}
}
else if (str[1] == 'u')
{
scanf("%d", &key);
pushNum(key);
s.push(key);
}
else
{
if (s.empty())
printf("Invalid\n");
else
{
printf("%d\n", (*prev(maxHeap.end())));
}
}
}
}