【中位数】对顶堆维护

基本思路

让大根堆的元素<=mid

让小根堆的元素 > mid

显然如果个数是奇数个,大根堆数量要比小根堆多1

否则取出两堆堆顶元素求平均数

P1168 中位数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

板子题

cpp 复制代码
#include<bits/stdc++.h>

using ll = long long;
using ull = unsigned long long;
using ari = std::array<int, 3>;
using PII = std::pair<int, int>;

const int N = 2e5 + 10;
const int mod = 1e9 + 7;
const double eps = 1e-6;
#define int ll
int n,k;
int a[N];
void solve() {
	std::cin>>n;
	std::priority_queue<int> d;//默认大根堆 
	std::priority_queue<int,std::vector<int>,std::greater<int> > s;
	
//	让大根堆的元素<=mid
//	让小根堆的元素>mid 
	int mid=INT_MIN;
	for(int i=1;i<=n;i++)
	{
		std::cin>>a[i];
		
		if(i==1) mid=a[1];
		if(a[i]<=mid)
		{
			d.push(a[i]); 
		}else s.push(a[i]);
		
		if(i%2)
		{
			while((int)d.size()-(int)s.size()<1)
			{
				d.push(s.top());
				s.pop();
			}
			while((int)d.size()-(int)s.size()>1)
			{
				s.push(d.top());
				d.pop();
			}
			mid=d.top();
			std::cout<<mid<<'\n';
		}
	} 
}
signed main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);

    int t = 1;
    //std::cin>>t;
    while (t--) {
        solve();
    }

    return 0;
}

295. 数据流的中位数 - 力扣(LeetCode)

对顶堆

对奇数个偶数个都求中位数

cpp 复制代码
class MedianFinder {
public:
    std::priority_queue<int> l;//默认大根堆 
	std::priority_queue<int,std::vector<int>,std::greater<int> > r;

    MedianFinder() {

    }
    
    void addNum(int num) {
        double mid=findMedian();
        if(mid>100000||num<=mid) l.push(num);
        else r.push(num);

        int sum=(int)l.size()+(int)r.size();
        if(sum%2)
        {
            while((int)l.size()-(int)r.size()<1){
                l.push(r.top());
                r.pop();
            }
            while((int)l.size()-(int)r.size()>1){
                r.push(l.top());
                l.pop();
            }
        }else{
            while((int)l.size()-(int)r.size()<0){
                l.push(r.top());
                r.pop();
            }
            while((int)l.size()-(int)r.size()>0){
                r.push(l.top());
                l.pop();
            }
        }
    }
    
    double findMedian() {
        if(l.size()==0) return 1000000;
        if((int)l.size()>(int)r.size()) return l.top();
        else return (l.top()+r.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();
 */

有序集合+双指针

第一次用这个数据结构感觉还蛮好用的。

set、multiset、map、multimap

特点:底层实现是红黑树,键值有序,set 和 map 键不可重复,而 multiset 和 multimap 可重复;

复杂度:插入、删除、查找都为O(logN);

感觉就是自动实现一个有序数组,通过左右两个迭代器来维护中位数的位置。

如果插入之后只有一个元素,毫无疑问,起始迭代器就是中位数。

如果插完是奇数,说明原来是偶数,l和r分别指向两个数字,现在要让他们指向一个数字。

如果要插入的数字刚好>=l且<r,说明左指针要往右边移动,右指针要往左边移动。

如果要插入的数字刚好<l,说明右指针要往左边移动。

如果要插入的数字刚好>=r,说明左指针要往右边移动。

如果插完是偶数,说明原来是奇数,l和r指向同一个数字,现在要让他们分别指向两个数字。

如果当前数字<l,左指针左移

如果当前数字>=l,右指针右移

cpp 复制代码
class MedianFinder {
    std::multiset<int> nums;
    std::multiset<int>::iterator l,r;

public:
    MedianFinder() {
        l=nums.end();
        r=nums.end();
    }
    
    void addNum(int num) {
        nums.insert(num);
        int n=nums.size();

        if(n==1){
            l=r=nums.begin();
        }else if(n%2){
            //原来是偶数,最后需要指向一个数
            if(num>=*l&&num<*r){
                l++,r--;
            }else if(num<*l){
                r--;
            }else l++;
        }else{
            if(num<*l){
                l--;
            }else r++;
        }
    }
    
    double findMedian() {
        return (*l+*r)/2.0;
    }
};

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

480. 滑动窗口中位数 - 力扣(LeetCode)

C++ STL prev()和next()函数(深入了解,一文学会)-CSDN博客好文!

cpp 复制代码
class Solution {
public:
    vector<double> medianSlidingWindow(vector<int>& nums, int k) {
        std::multiset<int> s(nums.begin(),nums.begin()+k);
        int len=nums.size();

        int l=0,r=k-1;
        auto mid=next(s.begin(),k/2);//从起点往后移动k/2,最后在k/2+1

        std::vector<double> ans;
        while(r<len)
        {
            double midd=( (double)(*mid) + (double)(*prev(mid,1-k%2)) ) /2;
            ans.push_back(midd);

            if(r+1==len) break;

            s.insert(nums[++r]);
            if(nums[r]<*mid) mid--;
            if(nums[l]<=*mid) mid++;

            s.erase(s.find(nums[l++]));
        }
        return ans;
    }
};

Problem - C - Codeforces

复制代码
k次操作,选择i bi=1,ai++
让max(ai+mid(ci))最大,直接对数组排序,特判中位数前后的中位数

观察样例发现会有特例,特例出现在中位数发生改变的情况,因此二分可能出现的最大中位数。

cpp 复制代码
#include<bits/stdc++.h>

using ll = long long;
using ull = unsigned long long;
using ari = std::array<int, 3>;
using PII = std::pair<int, int>;

const int N = 2e5 + 10;
const int mod = 1e9 + 7;
const double eps = 1e-6;

#define int ll
int n,k;
int c[N];
int tag[N];
struct node{
    int a,b;
}d[N];
bool cmp(node a,node b){
    return a.a<b.a;
}
void solve() {
    std::cin>>n>>k;
    for(int i=1;i<=n;i++)
    {
        std::cin>>d[i].a;
    }
    //k次,选择i bi=1,ai++
    //让max(ai+mid(ci))最大
    for(int i=1;i<=n;i++)
    {
        std::cin>>d[i].b;
    }
    std::sort(d+1,d+1+n,cmp);

    int mid=(n+1)/2;
    //1 2 3 122
    //1 2 3 4 5
    if(n%2){
        //删完变偶数
        for(int i=1;i<=n;i++)
        {
            if(i<mid){
                c[i]=d[mid].a;
                tag[i]=mid;
            }else{
                c[i]=d[mid-1].a;
                tag[i]=mid-1;
            }
        }

    }else{
        //1 2 3 4
        for(int i=1;i<=n;i++)
        {
            if(i<=mid){
                c[i]=d[mid+1].a;
                tag[i]=mid+1;
            }else{
                c[i]=d[mid].a;
                tag[i]=mid;
            }
        }
    }
    ll maxn=0;
    for(int i=1;i<=n;i++)
    {
        if(d[i].b!=0)
        {
            maxn=std::max(maxn,d[i].a+c[i]+k);
        }
        maxn=std::max(maxn,d[i].a+c[i]);
    }

    auto check=[&] (int x) ->bool{//判断最大中位数
        std::vector<int> v;
        int tag;
        for(int i=n-1;i>=1;i--)
        {
            if(d[i].a>=x) v.push_back(i);
            else
            {
                tag=i;
                break;
            }
        }
        int num=n-1;
        if(v.size()>=num/2+1) return 1;

        ll cnt=k;
        for(int i=tag;i>=1;i--){
            if(d[i].b==0) continue;

            int q=x-d[i].a;
            if(cnt>=q) {
                cnt-=q;
                v.push_back(i);
            }else break;
        }
        return v.size()>=(num/2+1);
    };

    ll l=0,r=INT_MAX,res=-1;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(check(mid)){
            res=mid;
            l=mid+1;
        }else r=mid-1;
    }
    maxn=std::max(maxn,d[n].a+res);
    std::cout<<maxn<<'\n';
}
//4 4
//2 1 5 1
//0 1 0 1
//
//1 1 2 5
//1 1 0 0
//2 2 1 1

//1 1 2 5
//2 3 3 5

//5 2
//10 11 4 10 15
//1 1 0 1 0
//
//26
//4 10 10 11 15
//1  1  1  1  0

signed main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);

    int t = 1;
    std::cin>>t;
    while (t--) {
        solve();
    }

    return 0;
}
相关推荐
快乐zbc1 天前
【C++ 基础】:给定一个指针 p,你能判断它是否指向合法的对象吗?
c++
sulikey1 天前
C++类和对象(下):初始化列表、static、友元、内部类等核心特性详解
c++·static·初始化列表·友元·匿名对象·内部类·编译器优化
oioihoii1 天前
C++网络编程:从Socket混乱到优雅Reactor的蜕变之路
开发语言·网络·c++
程序员东岸1 天前
《数据结构——排序(中)》选择与交换的艺术:从直接选择到堆排序的性能跃迁
数据结构·笔记·算法·leetcode·排序算法
程序员-King.1 天前
day104—对向双指针—接雨水(LeetCode-42)
算法·贪心算法
笨鸟要努力1 天前
Qt C++ windows 设置系统时间
c++·windows·qt
神仙别闹1 天前
基于C++实现(控制台)应用递推法完成经典型算法的应用
开发语言·c++·算法
Ayanami_Reii1 天前
进阶数据结构应用-一个简单的整数问题2(线段树解法)
数据结构·算法·线段树·延迟标记
listhi5201 天前
基于改进SET的时频分析MATLAB实现
开发语言·算法·matlab
Keep_Trying_Go1 天前
基于Zero-Shot的目标计数算法详解(Open-world Text-specified Object Counting)
人工智能·pytorch·python·算法·多模态·目标统计