算法搬运之BFPRT算法
原文连接:http://noalgo.info/466.html
BFPRT算法,又称为中位数的中位数算法,由5位大牛(Blum 、 Floyd 、 Pratt 、 Rivest 、 Tarjan)提出,并以他们的名字命名。参考维基上的介绍Median of medians。
算法的思想是修改快速选择算法的主元选取方法,提高算法在最坏情况下的时间复杂度。其主要步骤为:
首先把数组按5个数为一组进行分组,最后不足5个的忽略。对每组数进行排序(如插入排序)求取其中位数。
把上一步的所有中位数移到数组的前面,对这些中位数递归调用BFPRT算法求得他们的中位数。
将上一步得到的中位数作为划分的主元进行整个数组的划分。
判断第k个数在划分结果的左边、右边还是恰好是划分结果本身,前两者递归处理,后者直接返回答案。
首先看算法的主程序,代码如下。小于5个数的情况直接处理返回答案。否则每5个进行求取中位数并放到数组前面,递归调用自身求取中位数的中位数,然后用中位数作为主元进行划分。
注意这里只利用了中位数的下标,而不关心中位数的数值,目的是方便在划分函数中使用下标直接进行交换。BFPRT算法执行完毕之后可以保证我们想要的数字是排在了它真实的位置上,所以可以直接使用中位数的下标。
int BFPRT(int a[], int l, int r, int id) //求数组a下标l到r中的第id个数
{
if (r - l + 1 <= 5) //小于等于5个数,直接排序得到结果
{
insertionSort(a, l, r); return a[l + id - 1];
}
int t = l - 1; //当前替换到前面的中位数的下标
for (int st = l, ed; (ed = st + 4) <= r; st += 5) //每5个进行处理
{
insertionSort(a, st, ed); //5个数的排序
t++; swap(a[t], a[st+2]); //将中位数替换到数组前面,便于递归求取中位数的中位数
}
int pivotId = (l + t) >> 1; //l到t的中位数的下标,作为主元的下标
BFPRT(a, l, t, pivotId-l+1);//不关心中位数的值,保证中位数在正确的位置
int m = partition(a, l, r, pivotId), cur = m - l + 1;
if (id == cur) return a[m]; //刚好是第id个数
else if(id < cur) return BFPRT(a, l, m-1, id);//第id个数在左边
else return BFPRT(a, m+1, r, id-cur); //第id个数在右边
}
这里的划分函数与之前稍微不同,因为指定了划分主元的下标,所以参数增加了一个,并且第一步需要交换主元的位置。代码如下:
int partition(int a[], int l, int r, int pivotId) //对数组a下标从l到r的元素进行划分
{
//以pivotId所在元素为划分主元
swap(a[pivotId],a[r]);
int j = l - 1; //左边数字最右的下标
for (int i = l; i < r; i++)
if (a[i] <= a[r])
swap(a[++j], a[i]);
swap(a[++j], a[r]);
return j;
}
这里简单分析一下BFPRT算法的复杂度。
划分时以5个元素为一组求取中位数,共得到n/5个中位数,再递归求取中位数,复杂度为T(n/5)。
得到的中位数x作为主元进行划分,在n/5个中位数中,主元x大于其中1/2*n/5=n/10的中位数,而每个中位数在其本来的5个数的小组中又大于或等于其中的3个数,所以主元x至少大于所有数中的n/10*3=3/10*n个。同理,主元x至少小于所有数中的3/10*n个。即划分之后,任意一边的长度至少为3/10,在最坏情况下,每次选择都选到了7/10的那一部分,则递归的复杂度为T(7/10*n)。
在每5个数求中位数和划分的函数中,进行若干个次线性的扫描,其时间复杂度为c*n,其中c为常数。其总的时间复杂度满足 T(n) <= T(n/5) + T(7/10*n) + c * n。
我们假设T(n)=x*n,其中x不一定是常数(比如x可以为n的倍数,则对应的T(n)=O(n^2))。则有 x*n <= x*n/5 + x*7/10*n + c*n,得到 x<=10*c。于是可以知道x与n无关,T(n)<=10*c*n,为线性时间复杂度算法。而这又是最坏情况下的分析,故BFPRT可以在最坏情况下以线性时间求得n个数中的第k个数。
算法搬运之BFPRT算法的更多相关文章
- (搬运)《算法导论》习题解答 Chapter 22.1-1(入度和出度)
(搬运)<算法导论>习题解答 Chapter 22.1-1(入度和出度) 思路:遍历邻接列表即可; 伪代码: for u 属于 Vertex for v属于 Adj[u] outdegre ...
- BFPRT算法
解决的问题:在一个数组中找到最小的k个数 常规解法:1.排序,输出前k个数,时间复杂度O(n*log(n)). 2.利用一个大小为k的大根堆,遍历数组维持大根堆,最后返回大根堆就可以了,时间复杂度O( ...
- 查找第K小的数 BFPRT算法
出处 http://blog.csdn.net/adong76/article/details/10071297 BFPRT算法是解决从n个数中选择第k大或第k小的数这个经典问题的著名算法,但很多人并 ...
- 算法进阶面试题02——BFPRT算法、找出最大/小的K个数、双向队列、生成窗口最大值数组、最大值减最小值小于或等于num的子数组数量、介绍单调栈结构(找出临近的最大数)
第二课主要介绍第一课余下的BFPRT算法和第二课部分内容 1.BFPRT算法详解与应用 找到第K小或者第K大的数. 普通做法:先通过堆排序然后取,是n*logn的代价. // O(N*logK) pu ...
- 经典算法 BFPRT算法详解
内容: 1.原始问题 => O(N*logN) 2.BFPRT算法 => O(N) 1.原始问题 问题描述:给你一个整型数组,返回其中第K小的数 普通解法: 这道题可以利用 ...
- Top K问题-BFPRT算法、Parition算法
BFPRT算法原理 在BFPTR算法中,仅仅是改变了快速排序Partion中的pivot值的选取,在快速排序中,我们始终选择第一个元素或者最后一个元素作为pivot,而在BFPTR算法中,每次选择五分 ...
- 荷兰国旗问题、快排以及BFPRT算法
荷兰国旗问题 给定一个数组arr,和一个数num,请把小于num的数放数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边.要求额外空间复杂度O(1),时间复杂度O(N). 这个问题 ...
- BFPRT算法(求第K小的数字)
BFPRT算法: 1.介绍: BFPRT算法又叫中位数的中位数算法,主要用于在无序数组中寻找第K大或第K小的数,它的最坏时间复杂度为O(n),它是由Blum,Floyd,Pratt,Rivest,Ta ...
- KMP算法和bfprt算法总结
目录 1 KMP算法 1.1 KMP算法分析 1.2 KMP算法应用 题目1:旋转词 题目2:子树问题 2 bfprt算法 2.1 bfprt算法分析 2.2 bfprt算法应用 1 KMP算法 大厂 ...
随机推荐
- Fiddler拦截并动态修改网页内容简易教程
Fiddler默认可以拦截全局http请求,再加上它具备的脚本功能,可以很简单的达到动态修改网页内容的目的. 1.启动Fiddler 2.打开Rules->Customize Rules..., ...
- python time模块计算程序耗时
import time start = time.clock() end = time.clock() consume_time = end - start
- SpringBoot非官方教程 | 第二篇:Spring Boot配置文件详解
转载请标明出处: 原文首发于:https://www.fangzhipeng.com/springboot/2017/07/11/springboot2-config-file/ 本文出自方志朋的博客 ...
- $CRS_HOME/cdata下大量数字命名的文件,占用空间大
问题现象: <CRS_HOME>/cdata目录下存在大量数字命名的文件,导致文件系统爆满 $ls -alrth /opt/oracle/product/CRS/cdata/crs ...
- CodePush自定义更新弹框及下载进度条
CodePush 热更新之自定义更新弹框及下载进度 先来几张弹框效果图 非强制更新场景 image 强制更新场景 image 更新包下载进度效果 image 核心代码 这里的热更新Modal框,是封装 ...
- Navicat for Mysql中错误提示索引过长1071-max key length is 767 byte
1.建用户信息表 tb_person_info create table tb_person_info( user_id int(10) auto_increment, `name` varchar( ...
- boost::asio::ip::tcp中几个重要类型
typedef basic_stream_socket socket; 流式套接字,提供同/异步发送接收数据,连接,绑定,设置套接字选项等功能 对于socket中的connect()方法,它只针对某一 ...
- ABAP术语-Method
Method 原文:http://www.cnblogs.com/qiangsheng/archive/2008/03/05/1091077.html Component of classes in ...
- js分割字符串
js分割字符串 我想达到通过 : 分割 只要第一次分割,后面的内容不使用分割 不行,没找到可以直接用的方法,不过可以通过其它方式达到效果 eg: str.split(':',2)[0] (第一个分隔符 ...
- 【mysql学习-2】
part-1: USE mysql;CREATE TABLE tb_x(id INT,NAME CHAR(10));INSERT INTO tb_x VALUES(5,"a");S ...