本文为twenz根据个人经验整理,转载请注明来源,谢谢!

中位数即为一系列数中的大小在中间位置的数,快速找中位数的有效方法有:

1.排序法:先对数组进行排序,时间复杂度为O(nlogn),然后选择中间的数

2.快排的筛选法(类似于找第k大的数):思想是选定一个数,找比它大的数和小的数,然后根据数量再在大的部分或者小的部分循环递归找,时间复杂度应该为O(n)

如果给定两个有序的系列,需要查找他们共同的中位数,时间复杂度又会是多少呢?

1. 直接对两个有序系列进行逐个元素比较,时间复杂度为O(m+n)

2. 采用二分查找(类似于找第k大点),先以一个序列的基准点(二分来确定)来在另外一个系列中二分查找,再不断更新两系列的上下界限,时间复杂度应该在O(logm*logn). 下面是一个程序的基本框架,不一定正确,只是提供架构思想,待以后碰到类似问题我将更新该代码:

double findMid(int pm,int m)
{
    if(pm == 0 || pm == m){
        if(m%2)return a[m/2]*1.0;
        else return (a[m/2]+a[m/2-1])/2.0;
    }
    int low1=0,up1=pm-1,low2=pm,up2=m-1,mid1,mid2,num;
    while(low2<=up2){
        mid2=(low2+up2)/2;
        while(low1<=up1){
            mid1=(low1+up1)/2;
            if(a[mid1]<=a[mid2])low1=mid1+1;
            else up1=mid1-1;
        }
        num = mid1+(mid2-pm);
        if(a[mid1]<=a[mid2]){
            num +=1;
            if(num == (m-1)/2)break;
            else if(num < (m-1)/2){
                low2=mid2+1;
                up2=m-1;
                low1=mid1+1;
                up1=pm-1;
            }
            else{
                low2=pm;
                up2=mid2-1;
                low1=0;
                up1=mid1-1;
            }
        }
        else{
            if(num == (m-1)/2)break;
            else if(num < (m-1)/2){
                low2=mid2+1;
                up2=m-1;
                low1=mid1;
                up1=pm-1;
            }
            else{
                low2=pm;
                up2=mid2-1;
                low1=0;
                up1=mid1-1;
            }
        }
    }
    int ans,x;
    if(num != (m-1)/2){
        if(num > (m-1)/2)mid2--;
        ans = (m-1)/2-(mid2-pm+1);
    }
    else ans = mid2;
    if(m%2)return a[ans]*1.0;
    else {
        if(ans<pm){
            if(mid2<m-1&&ans<pm-1)x=a[ans+1]>a[mid2+1]?a[mid2+1]:a[ans+1];
            else if(mid2<m-1)x=a[mid2=1];
            else if(ans<pm-1)x=a[ans+1];
            return (x+a[ans])/2.0;    
        }
        else{
            if(ans<m-1&&a[mid1]>a[ans])x=a[ans+1]>a[mid1]?a[mid1]:a[ans+1];
            else if(ans<m-1&&mid1<pm-1&&a[mid1]<=a[ans])x=a[ans+1]>a[mid1+1]?a[mid1+1]:a[ans+1];
            else if(ans<m-1)x=a[ans+1];
            else if(a[mid1]>a[ans])x=a[mid1];
            else if(mid1<pm-1&&a[mid1]<=a[ans])x=a[mid1+1];
            return (x+a[ans])/2.0;    
        }
    }
}

但是如果给定的是一个动态的不断增加的系列,期间需要不断地查询中位数,该怎么办呢?

1. 采用插入排序,这样每次查询的都是有序的系列。时间复杂度O(n*n)

2. 利用上面的两个有序序列的思想,以一区间段M作为标准将系列分成两个有序系列,其中第二个系列采用插入排序(M*M*n/M),如果达到M就和1系列归并(n*n/M),查找中位数则采用上面的方法(n*logn*logM)。总时间复杂度为O(nM+n*n/M+nlogn*logM),则M取n的平方根可能较好

3. 还有一种思路就是仍然采用插入排序,但是使用双向链表插入排序,此外以M作为区间记录M倍数的位置的节点,这样插入过程只需找区间(n/M)以及在区间中找(M),则每次的时间复杂度为(n/M+M),而一个中位数指针维护指向中间节点,插入大的考虑右移小的左移O(1)。这样总的时间复杂度为O(n*(n/M+M)+1)及为O(n^1.5)

4. 二叉排序树方法(http://baike.baidu.com/view/647462.htm):排序O(nlogn),构建二叉排序树插入O(logn),查找O(logn)

5. 红黑树(http://hi.baidu.com/yellobd/blog/item/b7972850bf03b13242a75b83.html): 插入及查找均为O(logn),总复杂度O(nlogn)

6. 如果查询的第k大数的k呈现非递减关系,则可以采用维护最大堆和最小堆。最小堆k个数。时间复杂度O(nlogn)

下面这道题即是:http://acm.scs.bupt.cn/onlinejudge/newoj/Statistic/Statistic.php?problem_id=67 采用方法3的代码:

#include<iostream>
#include<stdio.h>
using namespace std;
#define M 200
#define N 100001
struct Node{
    int v;
    Node *prev,*next;
}node[N];
Node *inde[N/M+2],*mid,*last,*start;
void insert(Node *add,int lm,int m)
{
    if(last == NULL){
        start = last = mid = add;
        return;
    }
    if(last->v <= add->v){
        last->next = add;
        add->prev = last;
        last = add;
    }
    else if(start->v > add->v){
        add->next = start;
        start->prev = add;
        start = add;
        for(int i = lm-1;i >= 0;i --)inde[i] = inde[i]->prev;
    }
    else{
        int i = lm-1;
        while(i>=0 && inde[i]->v>add->v){
            inde[i] = inde[i]->prev;
            i --;
        }
        Node *p;
        if(i == lm-1)p=last->prev;
        else{
            p=inde[i+1];
            if(p->v<=add->v)inde[i+1]=add;
        }
        while(p->v>add->v)p=p->prev;
        add->prev = p;
        add->next = p->next;
        p->next->prev = add;
        p->next = add;
    }
    if(mid->v<=add->v){
        if(m%2)mid=mid->next;
    }
    else{
        if(m%2==0)mid=mid->prev;
    }
}
int main()
{
  int n,m,lm;
  while(scanf("%d",&n)!=EOF)
  {
     int i,x,tt;
     m = 0;
     lm = 0;
     start=mid=last=NULL;
     for(i=0;i<n;i++)
     {
      scanf("%d",&x);
      if(x == 1){
          scanf("%d",&tt);
          Node *add = &node[m++];
          add->v = tt;
          add->next=add->prev=NULL;
          insert(add,lm,m);
          if(m%M == 0)
              inde[lm++]=last;
      }
      else if(x == 2){
          double ans = mid->v;
          if(m%2==0)ans=(ans+mid->next->v)/2.0;
          printf("%.1lf\n",ans);
      }
    }
  }
  return 0;
}

---------------------------------方法4------------------------

#include <algorithm>
#include <iostream>
using namespace std;
#define N 100001
int a[N],b[N],tag[N],lm;
struct Node{
    int v;
    int count;
    bool tag;
    Node *l,*r;
}node[N];
Node * makeTree(int low,int up){
    int mid = (low+up)/2;
    while(mid>low&&a[mid]==a[mid-1])mid--;
    Node *root = &node[lm++];
    root->count = 0;
    root->tag = false;
    root->v=a[mid];
    if(low<=mid-1)root->l=makeTree(low,mid-1);
    else root->l=NULL;
    if(mid+1<=up)root->r=makeTree(mid+1,up);
    else root->r=NULL;
    return root;
}
void insert(Node *root,int xx){
    if(root->v>xx){
        root->count++;
        insert(root->l,xx);
    }
    else if(root->v==xx&&root->tag==false)
        root->tag=true;
    else
        insert(root->r,xx);
}
int look(Node *root,int middle)
{
    if(root->count >= middle)return look(root->l,middle);
    else if(root->count == middle-1 && root->tag)return root->v;
    if(root->tag)return look(root->r,middle-1-root->count);
    return look(root->r,middle-root->count);
}
int main()
{
    int i,j,x,n,m,resn;
    while(scanf("%d",&n)!=EOF)
    {
        m = 0;
        resn = 0;
        lm = 0;
        for(i=0;i<n;i++)
        {
            scanf("%d",&x);
            if(x == 1){
                scanf("%d",&a[m]);
                b[m]=a[m];
                m ++;
            }
            else if(x == 2){
                tag[resn++]=m;
            }
        }
        sort(a,a+m);
        Node *root = makeTree(0,m-1);
        j = 0;
        for(i=0;i<m;i++){
            insert(root,b[i]);
            while(j < resn && tag[j] == i+1){
                double ans = look(root,i/2+1);
                if(i%2)ans = (ans+look(root,i/2+2))/2;
                printf("%.1lf\n",ans);
                j ++;
            }
        }
    }
    return 0;
}

中位数(第k大数)快速求法相关推荐

  1. 快速查找无序数组中的第K大数?

    1.题目分析: 查找无序数组中的第K大数,直观感觉便是先排好序再找到下标为K-1的元素,时间复杂度O(NlgN).在此,我们想探索是否存在时间复杂度 < O(NlgN),而且近似等于O(N)的高 ...

  2. POJ 2104 K-th Number(区间第k大数)(平方切割,归并树,划分树)

    题目链接: http://poj.org/problem? id=2104 解题思路: 由于查询的个数m非常大.朴素的求法无法在规定时间内求解. 因此应该选用合理的方式维护数据来做到高效地查询. 假设 ...

  3. [转]第(前)k大数问题

    以前做过这种题,可是又给忘了.脑子还是活动不开-- 在网上搜了搜,发现这种问题有很多种解法,并且衍生出来很多新的问题.贴出来给大家看看. 转自http://summerbell.javaeye.com ...

  4. c++求区间第k大数_寻找第K大的数的方法总结

    今天看算法分析是,看到一个这样的问题,就是在一堆数据中查找到第k个大的值. 名称是:设计一组N个数,确定其中第k个最大值,这是一个选择问题,当然,解决这个问题的方法很多,本人在网上搜索了一番,查找到以 ...

  5. python【蓝桥杯vip练习题库】ALGO-1区间k大数查询

    试题 算法训练 区间k大数查询 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个. 输入格式 第一行包含一个数n,表示 ...

  6. 洛谷 P3332 [ZJOI2013]K大数查询 解题报告

    P3332 [ZJOI2013]K大数查询 题目描述 有\(N\)个位置,\(M\)个操作.操作有两种,每次操作如果是\(\tt{1\ a\ b\ c}\)的形式表示在第\(a\)个位置到第\(b\) ...

  7. BZOJ3110: [Zjoi2013]K大数查询

    BZOJ3110: [Zjoi2013]K大数查询 Description 有N个位置,M个操作. 操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如 ...

  8. 蓝桥杯-区间k大数查询(java)

    算法训练 区间k大数查询 时间限制:1.0s 内存限制:256.0MB问题描述给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个.输入格式第一行包含一个数n,表示序列长度.第二行包含n个 ...

  9. poj 2985(并查集+线段树求K大数)

    解题思路:这道题并查集很容易,合并时找到父节点就直接加上去就ok了.关键是如何求K大数,我一直在想用线段树怎么写,一开始想如果直接记录数的大小那肯定是没戏了,借鉴了一下别人的思路:区间[a,b]记录的 ...

最新文章

  1. python函数修饰器_Python函数装饰器指南
  2. 双指针算法之快慢指针(一):力扣【判断链表是否有环】leetcode-141、142
  3. 好文推荐(对JScript初,中级者有用):面向对象的Jscript[转贴]
  4. spring AoP学习 -----AoP织入器ProxyFactory剖析
  5. php python插件,Python:开发Sublime插件,方便PHP开发
  6. DeeCamp2019年笔试题A卷
  7. 最常用的数据库脚本前十名
  8. ie浏览器查看vue中js_浅析 Vue.js 中那些空间换时间的操作
  9. imread读不到图片_家用水表怎么读?超强实用技能快速get
  10. 向SqlParameter内动态添加参数
  11. 房产中介管理系统网站完整源码
  12. Windows7系统蓝屏-解决办法, 错误代码:0x0000007F
  13. 什么互动管理必将风行于天下?(转)
  14. Redis学习之scard命令
  15. 修改系统时区(基于Debian的系统)--用Enki学Linux系列(15)
  16. 关于esp32蓝牙模块的使用——esp32学习笔记
  17. 跨专业考清华大学的计算机,18级学长跨考清华大学计算机考研经验分享
  18. Android自定义键盘(KeyboardView)
  19. 人工智能+游戏 会带来什么
  20. 初识document.onkeydown及其兼容性问题

热门文章

  1. 京东企业购APP发布4.0版本 并更名为京东慧采
  2. 【K-Means】基于经纬度的城市聚类
  3. 解决配置.zshrc后zsh命令突然失效的问题
  4. nlp自己制作一个语料库_第119天的nlp论文总结了一个论点注释的科学出版物的语料库...
  5. 计算机网络范围覆盖类别,计算机网络如何分类和计算机网络的类别
  6. session.removeAttribute(“ “)和session.invalidate()的区别
  7. oracle清理历史备份,oracle数据库备份删除操作
  8. 相亲、相亲,广大年轻人的噩梦,那么我们就来采集一下相亲网站数据叭~
  9. 数据分析进阶 - 相关分析(皮尔逊相关系数)
  10. 5G通信-帧结构及RE和RB