TOP-K问题(查找第k小的数)+ 窗口
一、BFPRT算法
在一个数组中找出第k大的数
1、暴力解法:先排序,再找
2、快速排序:参考荷兰国旗问题,随机选数,分为左中右三个部分,然后按数量选择左边或右边区域,继续按荷兰国旗问题分三块,直到取到序号k在中间等于区域,则此时的中间数就是第k大的数。
3、BFPRT算法:
BFPRT算法跟快排的算法只有在选取划分值的情况上不同,其他全部一样。
【步骤】
- 分组(假设每五个一组,最后剩余的不到五个的一组) O(1)
- 分组之后每个小组之内排序,跨组不排序,五个数排序,总共需要划分的时间复杂度为O(N)。
- 将每个组的中位数拿出,构成新的数组,此时新数组长度为N/5(最后不到五个的可以拿上中位数,也可以拿下中位数) O(N)
- 调用BFPRT算法,此时递归过程中不再寻找k项,而是选择中间的中位数 T(N/5)
- 下面就是利用上述num,进行荷兰国旗问题排序 O(N)
- 选择走左边或者走右边
之所以要这样来选出中位数作为基准值,是因为这样分组后的左右规模就是确定了的,此时估计至少有多少个数比k更大,则确定了左右部分的最大规模。
【代码】
//用BFPRT方法得到第k个最小的值
public static int getMinKthByBFPRT(int[] arr,int k ){
int[] copyArr = copyArray(arr);
//得到数组中第k-1位置上的值就是第k小的值
return bfprt(copyArr,0,copyArr.length-1,K-1);
}
//bfprt方法主体,在bigin和end范围上求第i小的数
public static int bfprt(int[] arr,int begin,int end,int i){
if(begin == end){
return arr[begin];
}
//求中位数的中位数
int pivot = medianOfMedians(arr,begin,end);
//求完第二轮的中位数之后就开始进行划分
int[] privotRange = partition(arr,begin,end,pivot);
//正好i位置等于相等部分则返回
if(i>= pivotRange[0] && i <= pivotRange[1]){
return arr[i];
//i小于排序起始位置的情况,走左边
}else if(i<pivotRange[0]){
return bgprt(arr,begin,pivotRange[0]-1,i);
}else{
//i大于终止位置的情况,走右边
return bfprt(arr,pivotRange[1]+1,end,i);
}
}
public static int medianOfMedians(int[] arr,in