概念
BFPRT算法即是选取中位数的中位数
的方式,找出数组n个元素中第k大的数
。我们可以根据快速排序得到该值,但是快速排序的平均复杂度为O(nlog(n)),最坏时间复杂度为O(n^2)。而堆排序也是一个较好的方法,维护一个大小为k的堆,时间复杂度为O(nlog(k))。而BFPTR算法。它的最坏时间复杂度为O(n)
。
BFPRT算法原理
之前讲过一篇文章关于快速排序文章,我们便继续从那里开始引入。快速排序的大致过程如下:
- 先从序列中选取一个数最为基准数
- 将比这个数大的数全部放到它的右边,把小于或者等于它的数全部放到它的左边
一趟快速排序也叫做Partion,即将序列划分为两部分,一部分比基数小,另一部分比基数大,然后再进行分治的过程,每一次Partion不一定都能保证划分得很均匀,所以最坏情况下得时间复杂度不能保证总是为O(nlog(n))。而在BFPTR算法中,仅仅是改变了快速排序Partion中的pivot值的选取
,在快速排序中,我们始终选择第一个元素或者最后一个元素作为Pivot,而在BFPTR算法中,每次选择五分中位数的中位数作为pivot`,这样做的目的就是使得划分比较合理,从而避免了最欢情况的发生。算法步骤如下:
- 将n个元素划为⌊n/5⌋组,每组5个,至多只有一组由n mod 5 个元素组成。
- 寻找⌈n/5⌉个组中每一个组的中位数,这个过程可以用插入排序。
- 对步骤2中的⌈n/5⌉个中位数,重复步骤1和步骤2,递归下去,直到剩下一个数字。
- 最终剩下的数字即为pivot,把大于它的数全放左边,小于等于它的数全放右边。
- 判断pivot的位置与k的大小,有选择的对左边或右边递归。
下面以一个例子具体分析,设A = {
{8,33,17,51,57}},{49,35,11,25,37},{14,3,2,13,52},{12,6,29,32,54},{5,16,22,23,7}},数组A包含25个元素。首先,将数组A分成 5组,每组5个元素然后,对每组元素由小到大排序再按每组的中位数由小到大排 序得到如图2.2所示的数据方阵由各组中位数组成 一个中位数数组,如图2.2 中中间长条矩形中的元素取中位数数组的中位数,得到中位数的中位数mm=29. 最后以29为基准,将数组A划分成三部分: A1 ={ala< mm}, A2 ={ala= mm} 和A3={ala> mm} .如图所示
图中左下角矩形中的元素均小于等千中位数的中位数mm(29), 而右上角矩形中的元素均大千等千中位数的中位数mm(29).
如果对含有 n 个元素的数组调用BFPRT算法的计算时间复杂度为T(n),由于每组p个元素,对于p = 5, n可取44(44的取值为保证该式(7n/10)+1.2<=⌊3n/4⌋成立,当然最小值可以取到39,去掉向下取整符号,看它相应的余数最大是多少,相应的剪掉就好了,比方这里最大余数是3/4)。可以得到q = ⌈n/p⌉个中位数,且只可能有一组不是5个元素,找中位数的中位数时丢弃这一组,但不会影响最终结果,所以在第10步的计算时间复杂度为T(⌊n/5⌋)(因为丢弃了一组)。当n>=44时,不等式(7n/10)+1.2<=⌊3n/4⌋成立。从而,在该算法中第12-20步的计算时间复杂度为T(⌊3n/4⌋)。此外,算法第3-6步的时间复杂度为O(1);算法第7步时间复杂度为O(1);算法第8步和第9步的计算时间复杂度均为O(n)。从而可得到下列关于T(n)的递归方程,即,
算法实现如下:
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <algorithm>
using namespace std;
const int N = 10005;
int a[N];
//插入排序
void InsertSort(int a[], int l, int r