方法一:

思想:首先对整个数组进行划分,利用Partition函数,以数组中某个数为基准(这里以首项为标准)将数组划分为两部分——左边部分的所有数都小于基准,右边部分都大于基准,并返回基准数的下标值。

然后,如果要找到第k小个数,就将k的大小与数组左半边元素的个数(若为 j,包括基准)进行比较,如果k小于j,则对左边部分进行递归,找第k小个数;若k大于j,则对右边部分进行递归,找第k减去j个小数。

#include<iostream>
using namespace std;
template<typename T>
int Partition(T a[], int l, int r)
{int i = l, j = r + 1;T x = a[l];while (true){while (a[++i] < x && i <= r);while (a[--j] > x);if (i >= j)break;T m;m = a[i];a[i] = a[j];a[j] = m;}a[l] = a[j];a[j] = x;return j;
}
template<typename T>
T select(T a[], int left, int right, int k)
{if (left == right)return a[left];int i = Partition(a, left, right);int j = i - left + 1;if (k <= j)return select(a, left, i, k);elsereturn select(a, i + 1, right, k - j);}
int main()
{int m, k;cin>> m >> k;int* p = new int[m];for (int i = 0; i < m; i++)cin >> p[i];int h = select(p, 0, m - 1, k);cout << h;return 0;}

分析一下Partition函数:

以数组 a = {3 2 5 1 4 6 9}为例,首先以3为基准,令x=a[0](一般写为x=a[left], left为数组左边的最小下标值),i= 0,

j=6+1(最大下标值加一),接着进循环。i自增1,判断a[i]是否小于x,若小于则继续往下判断,否则退出循环。然后,j自减1,判断a[j]是否大于x,若大于继续循环,否则退出循环。可知第一次退出时,i=2,j=3。然后交换a[i]与a[j]的值,数组变为{3 2 1 5 4 6 9},继续循环,直到i>=j时退出循环,可知退出循环时i=3,j=2。将a[0]与a[j]的值交换,返回下标j旳值。数组变为{1 2 3 5 4 6 9}

左半部分(下标值小于2的部分)都小于基准x,即3,右边都大于3。

方法二:

如果能在线性时间内找到一个划分基准,使得按这个基准所划分出的2个子数组的长度都至少为原数组长度的ε倍(0<ε<1是某个正常数),那么就可以在最坏情况下用O(n)时间完成选择任务。

例如,若ε=9/10,算法递归调用所产生的子数组的长度至少缩短1/10。所以,在最坏情况下,算法所需的计算时间T(n)满足递归式T(n)≤T(9n/10)+O(n) 。由此可得T(n)=O(n)。

步骤:

(1)将n个输入元素划分成n/5(向上取整)个组,每组5个元素,最多只可能有一个组不是5个元素。用任意一种排序算法,将每组中的元素排好序,并取出每组的中位数,共n/5(向上取整)个。

(2)递归调用select来找出这n/5(向上取整)个元素的中位数。如果n/5(向上取整)是偶数,就找它的2个中位数中较大的一个。以这个元素作为划分基准。

例子:

按递增顺序,找出下面29个元素的第18个元素:8,31,60,33,17,4,51,57,49,35,11,43,37,3,13,52,6,19,25,32,
    54,16,5,41,7,23,22,46,29.
(1) 把前面25个元素分为5(=floor(29/5))组:  (8,31,60,33,17),(4,51,57,49,35),(11,43,37,3,13),(52,6,19,25,32),(54,16,5,41,7);
(2) 提取每一组的中值元素,构成集合{31,49,13,25,16};
(3) 递归地使用算法求取该集合的中值,得到m=25;
(4) 根据m=25, 把29个元素划分为3个子数组(按原有顺序)
P={8,17,4,11, 3,13,6,19,16,5,7,23,22}
Q={25}

R={31,60,33,51,57,49,35,43,37,52,32,54,41,46,29}

(5) 由于|P|=13,|Q|=1,k=18,所以放弃P,Q,使k=18-13-1=4,对R递归地执行本算法;
(6) 将R划分成3(floor(15/5))组:{31,60,33,51,57},{49,35,43,37,52},{32,54,41,46,29}
(7) 求取这3组元素的中值元素分别为:{51,43,41},这个集合的中值元素是43;
(8) 根据43将R划分成3组: {31, 33, 35,37,32, 41, 29},{43},{60, 51,57, 49, 52,54, 46}
(9)将左半边元素的个数与18作比较,若小于18,则对右边进行递归,否则对左边进行递归。

参考文章:https://blog.csdn.net/m0_37579232/article/details/80178000

#include<iostream>
using namespace std;template<typename T>
void sort(T a[], int m, int n)
{for (int i = m; i <= n; i++){int x = i;for (int j = i + 1; j <= n; j++)if (a[x] > a[j])x = j;if (x != i){T num = a[x];a[x] = a[i];a[i] = num;}}
}
template<typename T>
int Partition(T a[], int l, int r,T midnum)
{int point=0;for(int q=l;q<=r;q++)if (a[q] == midnum){point = q;break;}T df = a[l];a[l] = a[point];a[point] = df;int i = l, j = r + 1;T x = a[l];while (true){while (a[++i] < x && i <=r);        while (a[--j] > x);if (i >= j)break;T m;m = a[i];a[i] = a[j];a[j] = m;}a[l] = a[j];a[j] = x;return j;
}template<typename T>
T select(T a[], int left, int right, int k)
{if(right-left<75){sort(a, left, right);return a[left + k - 1];}for (int i = 0; i <= (right - left - 4) / 5; i++){sort(a, left + 5 * i, left + 5 * i + 4);T jk = a[left + 5 * i + 2];a[left + 5 * i + 2] = a[left + i];a[left + i] = jk;}T x = select(a, left, left + (right - left - 4) / 5, (right - left - 4) / 10);int local = Partition(a, left, right, x), j = local - left + 1;if (k <= j)return select(a, left, local, k);elsereturn select(a, local + 1, right, k - j);}int main()
{int m,k;cin >> m>>k;int* p = new int[m];for (int i = 0; i < m; i++)cin >> p[i];int n = select(p, 0, m - 1, k);cout << n;return 0;
}

线性时间选择(C++):求第k小的数相关推荐

  1. 两个有序数组合起来求第k小的数+左老师专访ACM大神(笔记)8月5日斗鱼直播实录

    1.长度相等的两个有序数组寻找上中位数 注:上中位数1 2 3 4 5 6为3(偶数两个中位数为前面那个) 思路:去掉不可能为上中位数的,剩下的简化组合求上中位数. 1.1 奇数序列 位置 位置 位置 ...

  2. C++:求第k小的数

    问题:给定n(1<=n<=1000000)个元素,求第k小数(1<=k<=n). 输入:一组样例.第一行输入两个整数n和k.第二行输入n个不同的int范围内的数. 输出:输出一 ...

  3. 求第k小的数(洛谷P1923题题解,Java/C++语言描述)

    题目要求 题目链接 分析 <查找第K大/小元素算法> 这题不使用线性复杂度算法是过不去的(除非你用C++的[内置函数]or[快读+sort()]) Java应该是不可能过去的,你用C++可 ...

  4. 求第k小的数 O(n)复杂度

    博客转载地址:https://blog.csdn.net/flyawayl/article/details/53538369 思路: 利用快速排序的思想,把数组递归划分成两部分.设划分为x,数组左边是 ...

  5. 借组磁带机求第K小元素

    如果输入在磁带机上, 你的机器只有一个磁带机驱动器和几十字的内存,如何找第K小的数 1. 遍历一遍磁带,随即选择一个数M 2. 再遍历一遍磁带, 计算大于和小于M的个数,这样就可以获得数M在总序列中的 ...

  6. 第K小的数BFPRT算法

    介绍 BFPRT是解决求一个数组中第K小的数的算法,可以时间O(N)的时间复杂度,而使用排序求第K小的数的算法的时间复杂度为O(NlogN),因此BFPRT算法更加高效 思想 (1)分组:将原数组每五 ...

  7. hdu3949(线性基,求第k小的异或和

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3949 XOR Time Limit: 2000/1000 MS (Java/Others)    Me ...

  8. 【最详细】BFPRT算法:时间复杂度O(n)求第k小的数字

    去年写了一篇对快排进行改进的算法,可以在时间复杂度 O(n)O(n)O(n)的情况下,找到第kkk小的数字. 那时候,我还不知道这个算法叫BFPRT算法--现在知道了,还知道它又被称为中位数的中位数算 ...

  9. 分治算法 求第k小元素 O(n) O(nlog2^n)

    BFPRT算法:时间复杂度O(n)求第k小的数字(分治算法+快排) 各位小伙伴,由于本篇文章代码太过杂乱.我于 2018年12月25日 对文中介绍的算法进行了重写.点击上面的蓝色字体,可以阅读重写后的 ...

  10. BFPRT算法:时间复杂度O(n)求第k小的数字(分治算法+快排)

    我自己搭建了博客,以后可能不太在CSDN上发博文了,https://www.qingdujun.com/ . 去年写了一篇<分治算法 求第kkk小元素 O(n)O(n)O(n) & O( ...

最新文章

  1. python中不可不知的一个重要的集合模块
  2. python九十八类_Python领域最伟大工程师Kenneth Reitz,教你写代码
  3. 两种大小端判断的方式
  4. HDU 1950 Bridging signals
  5. ModBus协议寄存器
  6. 面试官系统精讲Java源码及大厂真题 - 01 开篇词:为什么学习本专栏
  7. javascript导入EXCEL数据
  8. android右上角设置按钮,Android:如何在右上角的按钮角添加三角形
  9. 金字塔c_C/C++编程笔记:C语言入门题之正倒金字塔,正反三角形代码详解
  10. python培训班靠谱吗-python培训机构靠谱吗?python培训班
  11. json文件转换成label.png等一系列文件
  12. 解决办法:RuntimeWarning: numpy.dtype size changed, may indicate binary incompatibility. Expected 96
  13. 1. Windows网络编程(C++ Socket编程)
  14. 网站优化快速排名软件大全,用不用随你不做建议
  15. 各种域名都代表什么意思?
  16. 解决Minimum supported Gradle version is 4.6. Current version is 2.4.0
  17. python爬虫爬取深交所数据
  18. python 实现使用163邮箱发送有图片的邮件
  19. 一触即达!佰达慧兴携新风向标开展新零售电商产业升级
  20. 经验分享:移动客户端设计开发经验-流程篇

热门文章

  1. 使用OpenGL绘制shp文件
  2. 啊哈C语言第四章 第九节
  3. python win32api键盘_Python win32api.keybd_event模拟键盘输入
  4. 微信公众号--H5商城在线客服的解决方案:使用微信公众号的对话能力插件实现在线客服沟通
  5. Ubuntu18.04 下的Gif录制工具
  6. 汇编程序debug的使用完整使用
  7. debug常规的基本使用
  8. Centos 7 修改 Mysql 5.7 默认端口号
  9. go程序设计语言学习 popCount
  10. 国产中文电脑常用文档搜索-bbdoc,支持文件名和内容搜索,比Everything、listary、DocFetcher更简单易用