寻找中位数(分治+双堆)

假如有5亿个int,寻找它们的中位数。

思路是先分治,再用双堆法:
首先将int划分为2^16个区域,然后读取数据统计落到各个区域里的数的个数,之后我们根据统计结果就可以判断中位数落到那个区域。然后第二次扫描我们只统计落在这个区域中的那些数就可以了。

双堆法的思路:
序列中的元素,前一半存储在一个最大堆中,后一半存储在一个最小堆中。控制MaxHeap与MinHeap的大小差不能超过1。具体操作如下:

1.如果 num < MaxHeapTop,则
    1.1 如果 MaxHeapSize <= MinHeapSize,将num插入最大堆;
    1.2 如果 MaxHeapSize >  MinHeapSize,将MaxHeapTop从最大堆中移到最小堆,然后将num插入最大堆。
2.如果 MaxHeapTop <= num <= MinHeapTop,则
    2.1 如果 MaxHeapSize <  MinHeapSize,将num插入最大堆;
    2.2 如果 MaxHeapSize >  MinHeapSize,将num插入最小堆;
    2.3 如果 MaxHeapSize == MinHeapSize,随意插,看心情。
3.如果 num > MinHeapTop,则
    3.1 如果 MaxHeapSize >= MinHeapSize,将num插入最小堆;
    3.2 如果 MaxHeapSize <  MinHeapSize,将MinHeapTop从最小堆中移到最大堆,然后将num插入最小堆。

上面的插入情况会保证最大堆和最小堆的元素个数差小于1,中位数就只在最大堆和最小堆的顶部元素中产生:如果最大堆和最小堆的元素个数相等,则中位数为最大堆和最小堆的顶部元素的平均值;否则,中位数为元素个数多的那个堆的堆顶元素。

复杂度分析:
最差情况每次都要对堆做3次调整,复杂度为 32n/2i=1log2i

c++代码:

int median_heap(const vector<int>& arr)
{
    if(arr.empty()) return -1;
    if(arr.size() == 1) return arr[0];
    else if(arr.size() == 2) return (arr[0]+arr[1])/2;

    priority_queue<int> maxheap;
    priority_queue<int, vector<int>, greater<int>> minheap;
    maxheap.push(min(arr[0],arr[1]));
    minheap.push(max(arr[0],arr[1]));

    auto i=arr.begin()+2;
    for(;i!=arr.end();++i)
    {
        // 应放入maxheap
        if(*i < maxheap.top())
        {
            if(maxheap.size() > minheap.size())
            {
                minheap.push(maxheap.top());
                maxheap.pop();
            }
            maxheap.push(*i);
        }
        // 应放入minheap
        else if(*i > minheap.top())
        {
            if(maxheap.size() < minheap.size())
            {
                maxheap.push(minheap.top());
                minheap.pop();
            }
            minheap.push(*i);
        }
        else
        {
            if(maxheap.size() < minheap.size())
                maxheap.push(*i);
            else
                minheap.push(*i);
        }
    }

    if(maxheap.size() > minheap.size())
        return maxheap.top();
    else if(maxheap.size() < minheap.size())
        return minheap.top();
    return (maxheap.top()+minheap.top())/2;
}

参考
十道海量数据处理面试题与十个方法大总结
利用最大堆和最小堆在线寻找中位数

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是中位数问题的分治法伪代码: ``` def find_median(nums): n = len(nums) if n % 2 == 1: # 如果数组元素个数是奇数 return quick_select(nums, n // 2) # 直接找到第 n//2 小的元素即为中位数 else: # 如果数组元素个数是偶数 return (quick_select(nums, n // 2 - 1) + quick_select(nums, n // 2)) / 2 # 找到第 n//2 - 1 小和第 n//2 小的元素,取平均值作为中位数 def quick_select(nums, k): if len(nums) == 1: # 如果只有一个元素,则返回该元素 return nums[0] pivot = random.choice(nums) # 随机选择一个枢轴元素 left = [x for x in nums if x < pivot] # 将小于枢轴的元素放到左边数组 right = [x for x in nums if x > pivot] # 将大于枢轴的元素放到右边数组 mid = [x for x in nums if x == pivot] # 将等于枢轴的元素放到中间数组 if k < len(left): # 如果第 k 小的元素在左边数组中,递归处理左边数组 return quick_select(left, k) elif k < len(left) + len(mid): # 如果第 k 小的元素在中间数组中,直接返回中间数组的第一个元素 return mid[0] else: # 如果第 k 小的元素在右边数组中,递归处理右边数组 return quick_select(right, k - len(left) - len(mid)) ``` 其中,`quick_select` 函数用于找到第 k 小的元素,`find_median` 函数用于找到数组的中位数。在 `find_median` 函数中,如果数组元素个数是奇数,则直接找到第 n//2 小的元素即为中位数;如果数组元素个数是偶数,则找到第 n//2 - 1 小和第 n//2 小的元素,取平均值作为中位数

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值