算法刷题——数据流的中位数(力扣)

本文介绍了如何使用C++实现MedianFinder类,通过优先队列(最大堆和最小堆)来高效地处理数据流中的整数添加和中位数查询。作者的解法涉及排序和双辅助容器,而官方题解利用了堆数据结构,具有O(logn)添加和O(1)查询的时间复杂度。
摘要由CSDN通过智能技术生成

题目描述

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

  • 例如 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 以内的答案将被接受。

示例:

输入 [“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 次调用 addNum 和 findMedian

我的解法

class MedianFinder {
private:
    vector<int> vect1;
    vector<int> vect2;
public:
    MedianFinder() {

    }
    
    void addNum(int num) {
        vect1.push_back(num);
    }
    
    double findMedian() {
        // 搜索中位数前,先对容器中的元素进行排序
        sort(vect1.begin(),vect1.end());

        // 容器中没有元素
        if(vect1.size() == 0){
            return 0.0;
        }

        // 容器中有元素
        else{
            // 元素数量为奇数
            if(vect1.size() % 2 != 0){
                int index = vect1.size() / 2;
                while(index--){
                    vect2.push_back(vect1.back());
                    vect1.pop_back();
                }
                // 获得中位数
                double res = vect1.back();

                while(!vect1.empty()){
                    vect2.push_back(vect1.back());
                    vect1.pop_back();
                }

                // 将辅助容器中的元素返回存储容器中
                while(!vect2.empty()){
                    vect1.push_back(vect2.back());
                    vect2.pop_back();
                }
                return res;
            }
            else{
                // 元素数量为偶数
                int index = vect1.size() / 2 - 1;
                while(index--){
                    vect2.push_back(vect1.back());
                    vect1.pop_back();
                }
                // 获取两个求中位数的值
                double num1 = vect1.back();
                vect2.push_back(vect1.back());
                vect1.pop_back();
                double num2 = vect1.back();
                vect2.push_back(vect1.back());
                vect1.pop_back();

                while(!vect1.empty()){
                    vect2.push_back(vect1.back());
                    vect1.pop_back();
                }
                double res = (num1 + num2) / 2.0;

                // 将辅助容器中的元素返回存储容器中
                while(!vect2.empty()){
                    vect1.push_back(vect2.back());
                    vect2.pop_back();
                }
                return res;
            }
        }
    }
};
/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

结果

超出时间限制
在这里插入图片描述

分析

时间复杂度:
添加元素:O(1)
查询中位数:O(nlogn),由于需要先对容器里的元素进行排序再进行中位数的查询
空间复杂度:
需要两个额外的容器来完成中位数的查询,O(n)

官方题解

class MedianFinder {
public:
    // 创建一个最大堆,最大的元素位于队列的队首
    priority_queue<int, vector<int>, less<int>> queMin;
    // 创建一个最小堆,最小的元素位于队列的队首
    priority_queue<int, vector<int>, greater<int>> queMax;

    MedianFinder() {

    }
    
    void addNum(int num) {
        // 添加的元素小于中位数,则将元素添加到小于中位数的队列
        if(queMin.size() == 0 || num <= queMin.top()){
            queMin.push(num);
            // 用于保持大于中位数的队列和小于中位数的队列之间的大小不超过1,从而保证中位数的位置
            if(queMax.size() + 1 < queMin.size()){
                queMax.push(queMin.top());
                queMin.pop();
            }
        } else{
            queMax.push(num);
            // 用于保持大于中位数的队列和小于中位数的队列之间的大小不超过1,从而保证中位数的位置
            if(queMax.size() > queMin.size()){
                queMin.push(queMax.top());
                queMax.pop();
            }
        }

    }
    
    double findMedian() {
        if(queMin.size() > queMax.size()){
            return queMin.top();
        }else{
            return (queMax.top() + queMin.top()) / 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),主要为有序集合的开销

查漏补缺

优先队列

在 C++ 中,优先队列是一种特殊的队列数据结构,它的元素按照一定的优先级进行排序。在标准库中,优先队列的实现通常基于堆数据结构,通过堆来维护元素的优先级。

priority_queue<int, vector<int>, less<int>> queMin;

创建了一个名为 queMin 的最大堆(Max Heap)的优先队列(priority_queue)。在 C++ 中,默认情况下,priority_queue 是一个最大堆,也就是说,队列中的最大元素位于队首。

  • int: 表示 priority_queue 中存储的元素类型是整数(int)。
  • vector: 表示使用 std::vector 作为底层容器来存储优先队列的元素。
  • less: 表示使用 std::less 比较函数对象来确定堆的顺序。在这种情况下,less 表示最大堆,即队首元素是最大的。同理,great表示最小堆,即队首元素为最小的。

参考来源

力扣

  • 27
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

好好学习、天天向上。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值