[数据结构] 对顶堆求中位数

博客介绍了中位数的在线求法——对顶堆。对顶堆由小顶堆和大顶堆组成,二者堆顶是数据元素分界线,堆顶对换不影响性质。求中位数时调堆,每次调堆时间复杂度为log(n),还给出了元素量奇偶不同时的中位数求法,并附上例题及代码链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

中位数求法比较多 离线的我不会

比较容易实现的就是 在线的对顶堆

对顶堆的定义:

  • 小顶堆存 大于小顶堆堆顶所有元素
  • 大顶堆存 小于大顶堆堆顶所有元素

那么 小顶堆和大顶堆堆顶 就是整个 当前数据元素分界线

并且 堆顶对换/互相插入 不影响定义性质

那么当求中位数时 只需调堆即可

得益于堆的性质 每次调堆时间复杂度为log(n)

1. 当前总元素量为奇数时

只要保证 大顶堆元素比小顶堆元素多一 则中位数为大顶堆堆鼎

同理 只要保证 小顶堆元素比大顶堆元素多一 则中位数为小顶堆堆顶

2.当前总元素量为偶数时

只要保证大顶堆元素等于小顶堆 则中位数为(大顶堆顶 + 小顶堆顶) / 2.0

例题及代码

https://www.luogu.org/problemnew/show/P1168

/*
    Zeolim - An AC a day keeps the bug away
*/

//pragma GCC optimize(2)
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <string>
#include <cstring>
#include <algorithm>
#include <stack>
#include <queue>
#include <set>
#include <sstream>
#include <map>
#include <ctime>
#include <vector>
#include <fstream>
#include <list>
#include <iomanip>
#include <numeric>
using namespace std;
using namespace std;
typedef long long ll;

const int MAXN = 1e6 + 7;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);     cout.tie(0);

    priority_queue <ll, vector<ll>, greater<ll> > smalp;
    priority_queue <ll> bigp;
    
    vector <ll> ans;
    
    int len;
    
    cin >> len;
    
    for(int i = 1, x; i <= len; i++)
    {
        cin >> x;
        
        if(i == 1)            //不需求中位数时只需按定义插堆即可
            smalp.push(x);
        
        else if(x > smalp.top())
            smalp.push(x);
        else
            bigp.push(x);
        
        if(i & 1)            //当需要求中位数时调堆
        {
            if(smalp.size() > bigp.size())
                while(smalp.size() != bigp.size() + 1)
                    bigp.push(smalp.top()), smalp.pop();
                    
            else
                while(smalp.size() != bigp.size() + 1)
                    smalp.push(bigp.top()), bigp.pop();
 
            ans.push_back(smalp.top());
        }                  
    }
    
    for(int i = 0; i < ans.size(); i++)
    {
        cout<<ans[i]<<'\n';
    }

    return 0;
}

 

### 使用堆数据结构计算中位数 为了有效地使用堆来计算一组动态变化的数据流中的中位数,可以采用两个堆的组合方式:最大堆和最小堆。这种设计能够确保新加入的数据项总能被放置到合适的位置,并保持整体平衡。 #### 数据结构的选择与维护 - **最大堆 (MaxHeap)** 存储较小的一半数值; - **最小堆 (MinHeap)** 存储较大的一半数值; 通过这种方式,在任何时刻: - 如果两堆大小相等,则当前中位数等于这两个堆顶元素平均值。 - 若某一侧多出一个元素,则该额外元素所在一侧即为当前中位数位置[^1]。 #### 插入操作逻辑 当有新的数值到来时,按照如下规则处理: 对于小于或等于`maxheap.top()`的新值应放入最大堆内;反之则进入最小堆。之后调整两者之间可能存在的不平衡状态——如果某个堆超出另一个超过一位以上,则需将其顶部元素移至另一方以恢复均衡。 ```python import heapq class MedianFinder: def __init__(self): self.max_heap = [] # stores the smaller half of numbers, inverted to use min heap as max heap self.min_heap = [] def addNum(self, num: int) -> None: if not self.max_heap or num <= -self.max_heap[0]: heapq.heappush(self.max_heap, -num) else: heapq.heappush(self.min_heap, num) # Balance heaps' sizes if len(self.max_heap) > len(self.min_heap) + 1: moved_num = -heapq.heappop(self.max_heap) heapq.heappush(self.min_heap, moved_num) elif len(self.min_heap) > len(self.max_heap): moved_num = heapq.heappop(self.min_heap) heapq.heappush(self.max_heap, -moved_num) def findMedian(self) -> float: if len(self.max_heap) == len(self.min_heap): return (-self.max_heap[0] + self.min_heap[0]) / 2 else: return -self.max_heap[0] ``` 此方法能够在O(log n)时间内完成每次插入操作,并能在常量时间复杂度下获取中位数,非常适合用于解决在线算法问题或是实时数据分析场景下的需
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值