zoukankan      html  css  js  c++  java
  • 中位数

    中位数

    洛谷P1168 中位数

    前言:

    被教练当作数据结构和STL的阶段性测试题,考试时候觉得sort会爆超多(打脸啊,sort可是40ptsQAQ)就没写

    然后顺利的敲了个暴力,20pts滚粗(不过我好像也就编了几分钟...)

    当时的思路就是开两层循环,用一个小根堆来做,暴力嘛,能骗多少骗多少


    题目简述:

    给你长度为n的序列Ai,(n≤10^5,Ai≤10^9)

    要求输出奇数个数的中位数(即输出前1、3、5、7···个数的中位数)


    解题思路:

    • STL:vector+lower_bound函数
      (这大概是这题最短的代码了吧?)

    因为每次插入后再排序肯定会超时的,所以我们在插入前就进行排序(并不是每次从头排序哦!)

    解决了排序,就能让之前在vector中的元素保持单调性,那我们怎么知道当前的元素x该插入什么位置呢?

    如果你对STL函数库足够熟悉的话,那么就能想到,STL中有现成的函数可以帮助我们实现这个期望:lower_bound函数

    scanf("%d",&x);
    a.insert(lower_bound(a.begin(),a.end(),x),x); //lower_bound返回的是在[a.begin(),a.end())这个区间内第一个大于等于x的元素的迭代器
    
    这里的a是一个vector<int>类型的
    补充一下:insert(it,x)用来向vector的任意迭代器it处插入一个元素想,时间复杂度为O(N)
    

    完整代码就不用放了吧?就是边输入边插入,遇到奇数个元素就输出中位数即可

    • STL:大根堆&小根堆

    (这个应该算是解决这道题最常见的思路了)

    要维护中位数,那我们整两个堆,一个大根堆存放小于等于中位数的数,一个小根堆存放大于中位数的数

    很显然,小根堆的堆顶是第一个大于mid的数,大根堆堆顶是第一个小于等于mid的数字(我们设mid为中位数,mid初始就是第一个元素)

    scanf("%d",&mid);  //第一个数直接输入mid即可
    printf("%d
    ",mid);  //第一个也直接输出
    

    每一次输入一个元素x,我们就判断该放入哪个堆:

    scanf("%d",&x);
    if(x>mid) shans.push(x);  //大于中间值,进入小根堆 
    else shan.push(x);  //否则进入大根堆 
    

    如果遇到奇数个元素,我们就先维护两个堆的元素个数相同(因为只有两个堆的元素个数相同,那mid才能是中位数)

    维护元素个数很简单:如果大根堆多于小根堆,就把当前mid存入小根堆,将大根堆对顶赋给mid再删除堆顶,直到元素个数相同(小根堆多的话反过来就是了)

    if(i%2==1) {  //奇数个数就输出
       while(shan.size()!=shans.size()) {  //维护两个堆的元素个数相等,保证mid始终是中位数
    	if(shan.size()>shans.size()) {
    	   shans.push(mid);
    	   mid=shan.top();
    	   shan.pop();
    	} 
            else {
    	   shan.push(mid);
    	   mid=shans.top();
    	   shans.pop();
    	}
        }
        printf("%d
    ",mid);
    }
    

    完整Code:

    如下是第二种思路的代码

    #include <bits/stdc++.h>
    using namespace std;
    int n,x,mid;
    priority_queue<int,vector<int>,less<int> > shan; //大根堆存小于等于mid的数 
    priority_queue<int,vector<int>,greater<int> > shans;  //小根堆存大于mid的数 
    //小根堆的堆顶是第一个大于mid的数,大根堆堆顶是第一个小于等于mid的数字
    
    int main() {
    	scanf("%d",&n);
    	scanf("%d",&mid);  //第一个数直接输入mid即可 
    	printf("%d
    ",mid); 
    	for(register int i=2;i<=n;i++) {
    		scanf("%d",&x);
    		if(x>mid) shans.push(x);  //大于中间值,进入小根堆 
    		else shan.push(x);  //否则进入大根堆 
    		if(i%2==1) {  //奇数个数就输出 
    			while(shan.size()!=shans.size()) {  //维护两个堆的元素个数相等,保证mid始终是中位数 
    				if(shan.size()>shans.size()) {
    					shans.push(mid);
    					mid=shan.top();
    					shan.pop();
    				}
    				else {
    					shan.push(mid);
    					mid=shans.top();
    					shans.pop();
    				}
    			}
    			printf("%d
    ",mid);
    		}
    	}
    	return 0;
    } 
    

    后序:

    还能用平衡树做,但我不配啊(太菜)

    所以在这里只是提一下这种做法,大家可以去看看其他dalao的博客

    最后:第一种超短代码的思路来自:deco大佬的题解 感谢orz


  • 相关阅读:
    基于BGP/EVPN控制平面的VXLAN anycast-VTEP anycast-gateway基本配置
    NetworkManager配置VRF
    IBGP Segment Routing AIGP属性
    EBGP segment routing
    CentOS8创建网桥
    F5 HTTP response body rewrite
    OSPF Segment Routing和MPLS基本配置
    L2TP 和 IPsec over L2TP
    nmap
    LINUX DNS客户端 解析域名慢的问题。
  • 原文地址:https://www.cnblogs.com/Eleven-Qian-Shan/p/13162616.html
Copyright © 2011-2022 走看看