分治算法 求第k小元素 O(n) O(nlog2^n)
BFPRT算法:时间复杂度O(n)求第k小的数字(分治算法+快排)
各位小伙伴,由于本篇文章代码太过杂乱。我于 2018年12月25日 对文中介绍的算法进行了重写。点击上面的蓝色字体,可以阅读重写后的文章,修复了一些存在的错误。
最容易想到的算法是采用一种排序算法先将数组按不降的次序排好,然后从排好序的数组中捡出第k个元素。这样的算法在最坏情况下时间复杂度是O(nlog2^n)。
实际上,我们可以设计出在最坏情况下的时间复杂度为O(n)的算法。
利用分治算法并结合快排思想,很容易达到O(n)的时间复杂度。其核心思想在于快排中基准的选取。(根据严蔚敏版教材,一般直接选取第一个元素作为快排基准。但求第k小元素,则依赖于一种中值选取法,以加速剪枝)。
阅读以下内容时,需要先学习快排算法,可以看看这篇文章《[Data Structure]九大内部排序算法》。
下面举个例子,如何达到O(n)选取第k小的元素。
问题:如何在O(n)内,确定A[17]中第k小的元素? A[17] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
这里给出一个分治算法,选取基准的求解过程。
step1 设置一个值r,将A[15]分为长度为r的几个组。(假设r = 5)
A[17] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
G1 G2 G3 G4
分组结果,G1[5] = {0, 1, 2, 3, 4} ;G2[5] = {5, 6, 7, 8, 9};G3[5] = {10, 11, 12, 13, 14};G4[2] = {15, 16}。这里注意第四组,只有2个元素。
step2 分别求取G1~4组别中的中值,G1 = 2, G2 = 7, G3 = 12, G4 = 15。
并由这四个数再组成一个数组G[4] = {2, 7, 12, 15}。
step3 求得G[4] = {2, 7, 12, 15}数组中的中值,mid = 7。
那么,mid = 7便是最终选取出来的快排划分基准。
以下程序中,partition函数(第13行)以上部分即为上述基准选取过程。这里需要说明的是,求得中值后,程序中并没有开辟额外空间,而是在原有A[]基础上进行操作的,将中值放在最前面,故需要swap以保证数据信息不丢失。
//A[low..high]
int select_rank_k(int A[], int low, int high, int k)
{int r_group = ceil((high - low + 1)*1.0 / r);//ceil取上限,总共分为r_group个组//计算每个分组中值,存于A[]最前面for (int i = 1; i <= r_group; ++i) {sort(&A[low + (i - 1)*r], &A[(low + i*r - 1) > high ? high : (low + i*r - 1)]);swap(A[low + i - 1], A[low + (i-1)*r + r / 2]);}//获得每个组的中值的中值(并置于A[low]位置,方便调用快排划分函数)sort(&A[low], &A[low + r_group]);swap(A[low], A[r_group / 2]);int cur = partition(A, low, high);if (cur == k-1){return A[cur];}else if (cur < k){return select_rank_k(A, cur + 1, high, k);}else{return select_rank_k(A, low, cur - 1, k);}
}
2018年6月4日修正
swap(A[low], A[r_group / 2]); 应该更改为swap(A[low], A[low+r_group / 2]);
程序中其它执行步的时间复杂度都至多是n的倍数。如果用T(n)表示算法在数组长度为n的时间复杂度,则当n≥24时,有递归关系
其中c是常数。从上述递推关系式出发,用数学归纳法可以证明,
所以,在最坏情况下,select_rank_k算法的时间复杂度是O(n)。
最后,对整个问题抽象以下,并给出完整DEMO。问题:已知n元数组A[1..n],试确定其中第k小的元素。
补充:36行需修正为swap(A[low], A[low+r_group / 2]);
#include <stdio.h>
#include <algorithm>
#include <math.h>
using namespace std;//划分——每次划分唯一确定一个元素位置
int partition(int A[], int low, int high)
{int pivot = A[low]; //一般采用严蔚敏教材版本,以第1个位置为基准while (low < high){while (low < high && A[high] >= pivot){--high;}A[low] = A[high]; //将比基准小的元素移动到左端while (low < high && A[low] <= pivot){++low;}A[high] = A[low]; //将比基准小的元素移动到右端}A[low] = pivot;return low;
}int r = 5;
//A[low..high]
int select_rank_k(int A[], int low, int high, int k)
{int r_group = ceil((high - low + 1)*1.0 / r);//ceil取上限,总共分为r_group个组//计算每个分组中值,存于A[]最前面for (int i = 1; i <= r_group; ++i) {sort(&A[low + (i - 1)*r], &A[(low + i*r - 1) > high ? high : (low + i*r - 1)]);swap(A[low + i - 1], A[low + (i-1)*r + r / 2]);}//获得每个组的中值的中值(并置于A[low]位置,方便调用快排划分函数)sort(&A[low], &A[low + r_group]);swap(A[low], A[r_group / 2]);int cur = partition(A, low, high);if (cur == k-1){return A[cur];}else if (cur < k){return select_rank_k(A, cur + 1, high, k);}else{return select_rank_k(A, low, cur - 1, k);}
}int main(void)
{int A[15] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };printf("%d\n", select_rank_k(A, 0, 3, 2));return 0;
}
@qingdujun
2017-11-22 北京 怀柔
Reference:陈玉福.计算机算法设计与分析,59-61
分治算法 求第k小元素 O(n) O(nlog2^n)相关推荐
- 求第k小元素:采用特定分治策略
问题[描述算法问题,首选形式化方式(数学语言),其次才是非形式化方式(日常语言)]设L是n个元素的集合,从L中选取第k小的元素,其中1<=k<=n.这里的第k小元素是指,当L按从小到大排好 ...
- 借组磁带机求第K小元素
如果输入在磁带机上, 你的机器只有一个磁带机驱动器和几十字的内存,如何找第K小的数 1. 遍历一遍磁带,随即选择一个数M 2. 再遍历一遍磁带, 计算大于和小于M的个数,这样就可以获得数M在总序列中的 ...
- 算法-寻找第k小元素(C)
序言 刚开始我认为,寻找第k小的元素:简单呀,先对所有元素排序,之后再找不就完事啦,这时时间复杂度在O(nlgn).那有没有更好的排序的方法了呢?答案:当然是有的. 算法基本思路: (1) 当规模小于 ...
- Hoare选择算法 寻找第k小元素C实现 算法的“AWK脚手架和grap运行过程分析”
现实生活中常有找"最大"."最小"及"中位数"等需求,解决这样的问题不用将整个序列排序.寻找"最大"."最小& ...
- BFPRT算法:时间复杂度O(n)求第k小的数字(分治算法+快排)
我自己搭建了博客,以后可能不太在CSDN上发博文了,https://www.qingdujun.com/ . 去年写了一篇<分治算法 求第kkk小元素 O(n)O(n)O(n) & O( ...
- 【最详细】BFPRT算法:时间复杂度O(n)求第k小的数字
去年写了一篇对快排进行改进的算法,可以在时间复杂度 O(n)O(n)O(n)的情况下,找到第kkk小的数字. 那时候,我还不知道这个算法叫BFPRT算法--现在知道了,还知道它又被称为中位数的中位数算 ...
- 求数列中的第1~k小元素
1.问题描述 设计算法实现在一个具有在n各互不相同元素的数组A[1-n]中找出所有前k个最小元素的问题,这里k不是常量,即它是输入数据的一部分.要求算法的时间复杂性为Θ(n). 2. 具体要求 输入的 ...
- 分治算法求n个元素的最大值和最小值
分治算法求n个元素的最大值和最小值 算法思想: 1.将n个数均分为s1和s2 2.分别求解s1和s2的最大值和最小值 s1最大值为max1,s1最 ...
- 解决寻找第K小元素问题——三种不同的算法实现
个人原创,禁止转载--Zetrue_Li 问题描述:在一个序列里找出第K小元素 以下程序基于函数 int select_kth_smallest(list q, int k) 实现 :返回向量q中第k ...
最新文章
- mxnet 配置gpu
- PHP设计模式之装饰者模式
- python找零钱问题_Python基于回溯法子集树模板解决找零问题示例
- tinycore php,tinycore中文支持
- 使用jdbc执行SQL实现登录查询1-带配置文件和工具类
- 带你自学Python系列(八):列表具体操作思维导图总结,再也不用翻书查用法了!...
- 风格迁移与Gram matrix
- User Profile的名称和显示名称
- 合金理论自动化工具包(Alloy-Theoretic Automated Toolkit:ATAT)的安装
- 3dmax如何使用模型快速切片命令
- 搜狗输入法 VS 拼音加加
- 剖析Android shape标签的绘制
- [置顶] 礼物:《红孩儿引擎内功心法修练与Cocos2d-x》之结点系统(场景,层,精灵)...
- Asp.Net 密码加密技术
- 《操作系统真象还原》第十四章 ---- 实现文件系统 任务繁多 饭得一口口吃路得一步步走啊(上一)
- 计算机图形学与虚拟环境pdf,计算机图形学与虚拟现实环境.ppt
- 软考java题目_软件工程软考题目总结
- 睡眠排序算法c语言实现,Linux 进程必知必会
- android studio try catch自动生成,Android Studio:Try-catch异常崩溃了应用程序
- 揭秘小程序上线不到一周,每天2万销售额,究竟怎么做到的?