剑指offer——数据流中的中位数

本文介绍了一种高效计算数据流中位数的方法,通过维护两个堆(最大堆和最小堆),确保能快速找到中位数。适用于实时数据处理场景。

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

题目描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

思路 https://blog.youkuaiyun.com/moses1213/article/details/51332959

STL---heap概述  https://blog.youkuaiyun.com/moses1213/article/details/51332599

代码https://www.cnblogs.com/easonliu/p/4441916.html

https://www.nowcoder.com/profile/2156888/codeBookDetail?submissionId=13722304

 

作者用到的方法非常巧妙,注意到我们只需要位于中间的两个数就可以得出中位数,当元素个数为奇数个时可以看成这两个数一样。中位数在前半部分它是最大的,右半部分是最小的。我们只要始终知道前半部分数里的最大的那个数,后半部分里那个最小的数就可以了,其他的不用关心。这明显是堆的功用,前半部分用一个最大堆,后半部分建一个最小堆。中位数要保持两个堆的数目之差不超过1.为了实现平均分配,可以在数据的总数目是偶数时把新数据插入到最小堆中,否则插入最大堆中。

    另一个问题是保持最大堆的所有数据都小于最小堆中的数据。如果插入一个数使得总数目和为偶数,根据之前的原则应该插入最小堆中,但是新插入的元素可能比最大堆中的元素小怎么办?这就违反了最小堆的数据一定大于最大堆得原则。实际上这里插入的元素应该是新元素和最大堆中所有元素的最大值,最大堆正好有返回一个最大值的作用,所以新插入的元素先插入最大堆,然后取出最大值再插入最小堆中。如果插入元素使得数据总数目和为奇数,处理方法类似。

 

 

class Solution {
private:
     vector<int> min; //数组中的后一半元素组成一个最小化堆
     vector<int> max; //数组中的前一半元素组成一个最大化堆
public:
     void Insert(int num) {
         if(((min.size()+max.size()) & 1) == 0) {  //偶数数据的情况下,则在最小堆中插入元素
             if(max.size() > 0 && num < max[0]) {
                 max.push_back(num);
                 push_heap(max.begin(), max.end(), less<int>());
                 num=max[0];
                 pop_heap(max.begin(), max.end(), less<int>());
                 max.pop_back();
             }
             min.push_back(num); //把前一半找到的最大值放到后一半中
             push_heap(min.begin(), min.end(), greater<int>());
         } else {
             if(min.size() > 0 && num > min[0]) {   //奇数数据的情况下,则在最大堆中插入元素
                 min.push_back(num);
                 push_heap(min.begin(), min.end(), greater<int>());
                 num=min[0];
                 pop_heap(min.begin(), min.end(), greater<int>());
                 min.pop_back(); 
             }
             max.push_back(num); //把后一半找到的最大值放到前一半中
             push_heap(max.begin(), max.end(), less<int>());
         }
     }

     double GetMedian() { 
         int size=min.size() + max.size();
         if(size==0) return -1;
         double median = 0;
         if((size&1) != 0) {
             median = (double) min[0];
         } else {
             median = (double) (max[0] + min[0]) / 2;
         }
         return median;
     }
 };

 

更简洁的写法:

class Solution {
    priority_queue<int, vector<int>, less<int> > p;
    priority_queue<int, vector<int>, greater<int> > q;
     
public:
    void Insert(int num){
        if(p.empty() || num <= p.top()) p.push(num);
        else q.push(num);
        if(p.size() == q.size() + 2) q.push(p.top()), p.pop();
        if(p.size() + 1 == q.size()) p.push(q.top()), q.pop();
    }
    double GetMedian(){ 
      return p.size() == q.size() ? (p.top() + q.top()) / 2.0 : p.top();
    }
};

 

资源下载链接为: https://pan.quark.cn/s/67c535f75d4c 在机器人技术中,轨迹规划是实现机器人从一个位置平稳高效移动到另一个位置的核心环节。本资源提供了一套基于 MATLAB 的机器人轨迹规划程序,涵盖了关节空间和笛卡尔空间两种规划方式。MATLAB 是一种强大的数值计算与可视化工具,凭借其灵活易用的特点,常被用于机器人控制算法的开发与仿真。 关节空间轨迹规划主要关注机器人各关节角度的变化,生成从初始配置到目标配置的连续路径。其关键知识点包括: 关节变量:指机器人各关节的旋转角度或伸缩长度。 运动学逆解:通过数学方法从末端执行器的目标位置反推关节变量。 路径平滑:确保关节变量轨迹连续且无抖动,常用方法有 S 型曲线拟合、多项式插值等。 速度和加速度限制:考虑关节的实际物理限制,确保轨迹在允许的动态范围内。 碰撞避免:在规划过程中避免关节与其他物体发生碰撞。 笛卡尔空间轨迹规划直接处理机器人末端执行器在工作空间中的位置和姿态变化,涉及以下内容: 工作空间:机器人可到达的所有三维空间点的集合。 路径规划:在工作空间中找到一条从起点到终点的无碰撞路径。 障碍物表示:采用二维或三维网格、Voronoi 图、Octree 等数据结构表示工作空间中的障碍物。 轨迹生成:通过样条曲线、直线插值等方法生成平滑路径。 实时更新:在规划过程中实时检测并避开新出现的障碍物。 在 MATLAB 中实现上述规划方法,可以借助其内置函数和工具箱: 优化工具箱:用于解决运动学逆解和路径规划中的优化问题。 Simulink:可视化建模环境,适合构建和仿真复杂的控制系统。 ODE 求解器:如 ode45,用于求解机器人动力学方程和轨迹执行过程中的运动学问题。 在实际应用中,通常会结合关节空间和笛卡尔空间的规划方法。先在关节空间生成平滑轨迹,再通过运动学正解将关节轨迹转换为笛卡
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值