BFPRT算法的应用场景:
在一个数组中求第k个大的值或第k个小的值
BFPRT可以使用O(N)的时间复杂度解决该问题。
BFPRT算法是在随机快速排序算法进行优化的,随机快速排序的思路如下:
1)随机的选取一个元素与最后一个元素进行交换位置,随机选取的数成为最后一个元素
2)遍历数组 小于最后一个元素的放在左边 大于最后一个元素的放在右边
3)将最后一个元素放到中间位置
经过上面的过程后数组分为三部分 小于随机选取的数 等于随机选取的数 大于随机选取的数
BFPRT算法是在以上算法进行的优化,算法的时间复杂度严格的O(N)
随机选取一个元素变为选取中位数
步骤如下:
1)将数组分为5个一组的多个数组 不足五个的形成一组O(N)
2)组内进行排序O(N)
3)每个数组中位数取出构成新的数组O(N)
4)递归的调用BFPRT 直到得到一个数组只剩一个数 返回T(N/5)
以上均为选取元素的部分
5)小于中位数左边 等于中位数中间 大于中位数右边O(N)
6)如果第K个值在左边 递归左边 在右边 递归右边 中间 返回
当数组的个数是N个时 排序之后得到中位数数组N/5 而求这N/5的中位数 这N/5个中位数中有N/10个元素比中位数大,所以所有的元素中至少有3N/10个元素比选出的中位数大,所以至少有7N/10个元素比中位数小
所以在步骤6中最坏情况是T(7N/10)
T(N)=T(N/5)+T(7N/10)+O(N)
例子如下:
数组:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 第10小的数
分组:1 2 3 4 5
6 7 8 9 10
11 12 13 14 15
得到中位数数组 3 8 13
最终得到中位数是8 在中位数数组中 至少13是比8大的,而13是它所在数组中位数 所以13所在的数组中至少还有2个数比8大 所以至少3个数比8大
得到中位数是8 8的下标为7
重新得到数组:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
递归9 10 11 12 13 14 15中找第10-7-1小的数
/*
* 求一个数组的第k个大的数 O(N)
*
* 荷兰国旗问题可以实现O(N)求出数组的第k大的数(基于概率得到)
* 类似于排序 然后找打第k个位置上的值
* 随机的选取一个值 将大于这个数的值放右边 小于这个数的值放左边 等于这个数的值放右边
* 如果第k大的值在等于k的范围内 可以直接得到结果
* 如果第k大的值在左边 递归的做左边 在右边 递归的做右边
*
* BFPRT算法
* 1.五个一组 O(1)
* 2.组内排序 O(N)
* 3.每个组中位数取出组成新的数组 O(N)
* 4.递归调用BFPRT找到中位数 T(N/5)
* 以上均为取划分值
* 5.大于右边 小于左边 等于中间O(N)
* 6.如果第k大的值在左边 递归的做左边 在右边 递归的做右边
*
* 中位数的个数是N/5 中位数数组的中位数假设为c
* 至少比c的大的中位数个数N/10 至少比c大的中位数个数3N/10
*
* 如果k在左边 左边的最坏情况是最多有7N/10个人比中位数小T(7N/10)
* 同理如果k在右边 右边的最坏情况是最多有7N/10个人比中位数大
*
* T(N)=T(N/5)+T(7N/10)+O(N)
*
* 时间复杂度O(N)
* */
#include
#include
#include
#include
class CSolution{
public:
int BFPRT(std::vector vData,int iL,int iR){
if(iR-iL==0)
return vData[iR];
std::vector> vDat;
std::vector vD;
//五个一组分组
for(int i=iL;i<=iR;i=i+5){
vD.clear();
for(int j=i;j vMedium;
//组内排序提取中位数组成新数组
for(int i=0;i &vData,int iK,int iL,int iR){
int iMedium=BFPRT(vData,iL,iR);
//将目标与最后一个元素交换
int iIndexTarget=getIndex(vData,iMedium);
swap(vData,iIndexTarget,iR);
int iLess=iL-1;
int iMore=iR+1;
int num=vData[iR];
int l=iL;
while(lnum){
swap(vData,l,--iMore);
}
else
l++;
}
int iCountLess=iLess+1-iL;//比中位数小的个数
int iCountMore=iR-(iMore-1);//等于中位数的个数
int iCountEqual=iMore-iLess-1;//大于中位数的个数
if(iK<=iCountLess){
return getBiggerK(vData,iK,iL,iL+iCountLess-1);//当第K个值在小于中位的的一边时 递归求左边
}else if(iK>iCountLess+iCountEqual){
return getBiggerK(vData,iK-iCountLess-iCountEqual,iL+iCountLess+iCountEqual,iR);//在右边 递归求右边
}
else
return iMedium;//当等于中位数的值时返回中位数的值即可
}
//得到中位数所在的下标
int getIndex(std::vector &vData,int target){
for(int i=0;i &vData,int iL,int iR){
int tmp=vData[iL];
vData[iL]=vData[iR];
vData[iR]=tmp;
}
};
int main()
{
std::vector vData={2,344,1,123,23,3,4,354,45,6,7,8,123,12,7};
//std::vector vData={2,1,3,4,6,7,7};
CSolution cS1;
//std::cout<