Acwing54.数据流中的中位数——使用大根堆和小根堆

题目描述:
如何得到一个数据流中的中位数?
如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
数据范围
数据流中读入的数据总数 [1,1000]。

示例:

输入:1, 2, 3, 4
输出:1,1.5,2,2.5
解释:每当数据流读入一个数据,就进行一次判断并输出当前的中位数。

算法思路:
本题,一开始我想到是时使用指针的方式,预先定义一个index指针指向第一个元素,当输入数据流的个数是奇数的时候,那么就不移动指针,且指针指向的元素就为中位数;当输入数据流的个数是偶数的时候,先移动指针+1,然后当前index指向的元素和index-1的元素相加求平均即为中位数;使用此方式在数据集较小的情况下能够Accept,但是数据集一大,就会limit时间限制。
于是采用第二种方式,使用大根堆和小根堆来存储元素,大根堆存储小于或等于中位数的元素,小根堆存储大于中位数的元素,当数据流中元素个数为偶数时,大根堆和小根堆的大小是一样的;当数据流中元素个数为奇数时,大根堆=小根堆+1;那么就可以当偶数是将大根堆的结点和小根堆的结点元素取出求平均即可;当元素个数为奇数时,则取大根堆的根结点。

算法实现:
1、使用大小根堆方式(建议采用):

class Solution {
    //使用大根堆和小根堆来存储元素
    //大根堆,父结点的元素大于左右子结点,存储小于等于中位数的数
    PriorityQueue<Integer> small = new PriorityQueue<>((a,b)->b-a);
    //小根堆,父结点的元素小于左右子结点,存储大于中位数的数
    PriorityQueue<Integer> large = new PriorityQueue<>((a,b)->a-b);
    
    public void insert(Integer num){
        //当数据流中数字个数为偶数时,大根堆和小根堆大小一样
        //当数据流当中的数字个数为奇数时,大根堆的大小=小根堆大小+1
        if(small.isEmpty()||num<small.peek()){
            //当大根堆为空或者数据流当前的元素小于大根堆元素
            //大根堆使用offer()进行元素的添加与删除
            small.offer(num);
            if(small.size()>large.size()+1){
                //如果大根堆中加入的元素使得当前大根堆与小根堆大小不府
                //将大根堆最大的元素加入到小根堆当中
                large.offer(small.poll());
            }
        }else{
            //加入的元素较大
            //加入到小根堆当中
            large.offer(num);
            //小根堆只能大于或者小于大根堆中的元素
            if(large.size()>small.size()){
                small.offer(large.poll());
            }
        }
    }
    public Double getMedian(){
        //返回中位数
        if(small.size()==large.size()){
            //表示数据流中的元素为偶数
            double num1 = small.peek();
            double num2 = large.peek();
            return (num1+num2)/2;
        }else{
            //数据流中奇数
            double num = small.peek();
            return num;
        }
    }
}

2、使用指针

class Solution {
    //定义一个集合用来存储元素
    List<Integer> list = new ArrayList<>();
    //定义一个指针用来查找中间两个元素
    int index = 0;
    double res = 0.0;
    public void insert(Integer num) {
        //往集合当中添加元素
        list.add(num);
    }
    //所有数字排序之后
    public Double getMedian() {
        int[] arr = new int[list.size()];
        for(int i=0;i<arr.length;i++){
            arr[i] = list.get(i);
        }
        Arrays.sort(arr);
        for(int i=0;i<arr.length;i++){
            list.set(i,arr[i]);
        }
        //该方法是用来获取中间两个数的平均值
        //当奇数次不移动index
        if(list.size()%2==1){
            res = list.get(index);
            return res;
        }else{
            //偶数次需要移动index
            // System.out.println(list.size());
            //此时移动index指针指向下一位
            index++;
            double i1 = list.get(index);
            double i2 = list.get(index-1);
            res = (i1+i2)/2;
            return res;
        }
     }
}     
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在C语言,可以使用set来实现大根堆小根堆大根堆是指根节点的值大于等于其子节点的值,而小根堆是指根节点的值小于等于其子节点的值。 引用给出了使用priority_queue实现大根堆小根堆的示例代码。其,priority_queue<int> maxHeap;表示定义了一个大根堆,而priority_queue<int, vector<int>, greater<int>> minHeap;则表示定义了一个小根堆。如果要使用自定义的结构体或类作为元素,需要重载<运算符来定义堆的顺序。 引用的代码示例展示了使用deque来实现堆的功能。其使用dfs函数遍历树的节点,并根据节点的值来判断是插入1还是-1到unordered_set<int> record,以判断是大根堆还是小根堆。 因此,在C语言,可以使用set来实现大根堆小根堆的功能。123 #### 引用[.reference_title] - *1* [c++大根小根堆](https://blog.csdn.net/qq_44289340/article/details/125861694)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] - *2* *3* [PAT_甲级_1155 Heap Paths (30point(s)) (C++)【DFS/大根堆小根堆判断】](https://blog.csdn.net/qq_41528502/article/details/105289948)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值