优先级队列(4)_数据流的中位数

个人主页:C++忠实粉丝
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C++忠实粉丝 原创

优先级队列(4)_数据流的中位数

收录于专栏【经典算法练习
本专栏旨在分享学习算法的一点学习笔记,欢迎大家在评论区交流讨论💌

目录

[1. 题目链接](#1. 题目链接)

[2. 题目描述](#2. 题目描述)

[3. 解法](#3. 解法)

算法思路:

代码展示:

[4. 题目总结:](#4. 题目总结:)


1. 题目链接

OJ链接 : 数据流的中位数

2. 题目描述

中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。

  • 例如 arr = [2,3,4] 的中位数是 3
  • 例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5

实现 MedianFinder 类:

  • MedianFinder() 初始化 MedianFinder 对象。

  • void addNum(int num) 将数据流中的整数 num 添加到数据结构中。

  • double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

示例 1:

复制代码
输入
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
输出
[null, null, null, 1.5, null, 2.0]

解释
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1);    // arr = [1]
medianFinder.addNum(2);    // arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3);    // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

提示:

  • -105 <= num <= 105
  • 在调用 findMedian 之前,数据结构中至少有一个元素
  • 最多 5 * 104 次调用 addNumfindMedian

3. 解法

算法思路:

这是一道关于 这种数据结构的一个 经典应用.

我们可以将整个数组 按照大小 平均分成两个部分 (如果不能平分, 那就让较小部分的元素多一个), 较小的部分称为左侧部分, 较大的部分称为右侧部分 :

  1. 将左侧部分放入 大根堆 中, 然后将右侧部分放入 小根堆

  2. 这样就能在O(1) 的时间内拿到中间的一个数或者两个数, 进而求平均数

如下图所示 :

于是问题就变成了 如何将一个一个从数据流中过来的数据, 动态调整到大根堆或者小根堆中, 并且保证两个堆的元素一致, 或者左侧堆的元素比右侧堆的元素多一个

为了方便叙述, 将左侧的 大根堆 记为left, 右侧的 小根堆 记为rght, 数据流中来的 数据 记为x

其实, 就是一个分类讨论的过程:

  1. 如果左右堆的 数量相同 , left.size() == right.size():

a. 如果两个堆都是空的, 直接将数据x放到left中

b. 如果两个堆非空:

i. 如果元素要放入左侧, 也就是x <= left.top(): 那就直接放, 因为不会影响我们制定的规则

ii. 如果要放到右侧

  1. 可以先将x放入right中

  2. 然后把right的堆顶元素放入left中

  3. 如果左右堆的数量 不相同 , 那就是left.size() > right.size():

a. 这个时候我们关心的是x是否会放入left中, 导致left变得过多:

' i. 如果x放入right中, 也就是x >= right.top(), 直接放

ii. 反之, 就需要放入left中

  1. 可以先将x放入left中

  2. 然后把left的堆顶元素放入right中

只要么一个新来的元素按照 上述规则 执行, 就能保证 left 中放着整个数组排序后的 左半部分, right中放着整个数组排序后的 右半部分, 就能在O(1)的时间内求出平均数.

代码展示:

cpp 复制代码
class MedianFinder {
    priority_queue<int> left;
    priority_queue<int, vector<int>, greater<int>> right;
public:
    MedianFinder() {}
    
    void addNum(int num) 
    {
        if(left.size() == right.size())
        {
            if(left.empty() || num <= left.top()) left.push(num);
            else 
            {
                right.push(num);
                left.push(right.top());
                right.pop();
            }
        }
        else
        {
            if(num <= left.top()) 
            {
                left.push(num);
                right.push(left.top());
                left.pop();
            }
            else right.push(num);
        }
    }
    
    double findMedian() 
    {
        if(left.size() == right.size()) return (left.top() + right.top()) / 2.0;
        else return left.top();
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

4. 题目总结:

时间复杂度

插入操作 addNum 的时间复杂度为O(logn),在最坏情况下需要调整堆。调正堆的时间复杂度为O(logN)

查找中位数 findMedian 的时间复杂度为O(1)。
空间复杂度

空间复杂度为O(n),用于存储所有插入的数字。

相关推荐
wabs6663 小时前
关于贪心算法的思考
算法·贪心算法
社交怪人3 小时前
【判断大小】信息学奥赛一本通C语言解法(题号1043)
算法
Snasph4 小时前
GNU Make 用户手册(中文版)
服务器·算法·gnu
江澎涌4 小时前
拆解与 AI 的一次对话
人工智能·算法·程序员
sheeta19984 小时前
LeetCode 每日一题笔记 日期:2026.06.02 题目:3635. 最早完成陆地和水上游乐设施的时间 II
笔记·算法·leetcode
Lsk_Smion5 小时前
力扣实训 _ [102].层序遍历--前序--后续_递归与非递归的实现
数据结构·算法·leetcode
Lsk_Smion6 小时前
力扣实训 _ [25].K个一组链表
数据结构·链表
小欣加油6 小时前
leetcode3751 范围内总波动值I
java·数据结构·c++·算法·leetcode
代码中介商7 小时前
C++左值与右值:核心判断法则详解
开发语言·c++
玖玥拾7 小时前
C/C++ 基础笔记(七)
c语言·c++