一、背景
在一大堆数中求其前k大或前k小的问题,简称TOP-K问题。而目前解决TOP-K问题最有效的算法即是BFPRT算法,其又称为中位数的中位数算法,该算法由Blum、Floyd、Pratt、Rivest、Tarjan提出,最坏时间复杂度为O(n)O(n)。
在首次接触TOP-K问题时,我们的第一反应就是可以先对所有数据进行一次排序,然后取其前k即可,但是这么做有两个问题:
(1):快速排序的平均复杂度为O(nlogn)O(nlogn),但最坏时间复杂度为O(n2)O(n2),不能始终保证较好的复杂度。
(2):我们只需要前k大的,而对其余不需要的数也进行了排序,浪费了大量排序时间。
除这种方法之外,堆排序也是一个比较好的选择,可以维护一个大小为k的堆,时间复杂度为O(nlogk)O(nlogk)。
那是否还存在更有效的方法呢?受到快速排序的启发,通过修改快速排序中主元的选取方法可以降低快速排序在最坏情况下的时间复杂度(即BFPRT算法),并且我们的目的只是求出前k,故递归的规模变小,速度也随之提高。下面来简单回顾下快速排序的过程,以升序为例:
(1):选取主元(首元素,尾元素或一个随机元素);
(2):以选取的主元为分界点,把小于主元的放在左边,大于主元的放在右边;
(3):分别对左边和右边进行递归,重复上述过程。
原文链接:https://blog.csdn.net/laojiu_/article/details/54986553
仅用荷兰国旗算法也能达到数学期望为O(N)的时间复杂度,但是也有可能存在O(N2)的最差情况,BFPRT就是在荷兰国旗算法的基础上,加入寻找一个好的中位数,让时间复杂度稳定在O(N)
二、算法套路
BFPRT算法套路
1. 对整个数组进行分组,每组5个数,不满5个的凑成最后一组
2.对每个组进行组内排序, 时间复杂度O(N)
为什么时间复杂度O(N),解释:
排序算法时间复杂度为O(NlogN), 当N等于5时候,即为O(1)
对N/5个数组进行排序,所以时间复杂度为O(N)
3.拿出排序后的每个数组的中位数,组成一个新的N/5长度数组
4.递归掉BFPRT
5.拿到BFPRT的返回的num, 小于放左边,等于放中间,大于放右边。即快排里的荷兰国旗pattition算法。
伪代码:
int bfprt(int[] arr, int k){
1.
2.
3.生成一个N/5大小的new_arr
4.bfprt(new_arr, new_arr.length/2);
5.
}
三、代码
public static int[] getMinKNumsByBFPRT(int[] arr, int k) {if (k <1 || k > arr.length) {return arr;}int minKth &#61; getMinKthByBFPRT(arr, k);int[] res &#61; new int[k];int index &#61; 0;for (int i &#61; 0; i !&#61; arr.length; i&#43;&#43;) {if (arr[i] &#61; pivotRange[0] && i <&#61; pivotRange[1]) {return arr[i];} else if (i pivotValue) {swap(arr, cur, --big);} else {cur&#43;&#43;;}}int[] range &#61; new int[2];range[0] &#61; small &#43; 1;range[1] &#61; big - 1;return range;}public static int getMedian(int[] arr, int begin, int end) {insertionSort(arr, begin, end);int sum &#61; end &#43; begin;int mid &#61; (sum / 2) &#43; (sum % 2);return arr[mid];}public static void insertionSort(int[] arr, int begin, int end) {for (int i &#61; begin &#43; 1; i !&#61; end &#43; 1; i&#43;&#43;) {for (int j &#61; i; j !&#61; begin; j--) {if (arr[j - 1] > arr[j]) {swap(arr, j - 1, j);} else {break;}}}}public static void swap(int[] arr, int index1, int index2) {int tmp &#61; arr[index1];arr[index1] &#61; arr[index2];arr[index2] &#61; tmp;}public static void printArray(int[] arr) {for (int i &#61; 0; i !&#61; arr.length; i&#43;&#43;) {System.out.print(arr[i] &#43; " ");}System.out.println();}public static void main(String[] args) {int[] arr &#61; { 6, 9, 1, 3, 1, 2, 2, 5, 6, 1, 3, 5, 9, 7, 2, 5, 6, 1, 9 };// sorted : { 1, 1, 1, 1, 2, 2, 2, 3, 3, 5, 5, 5, 6, 6, 6, 7, 9, 9, 9 }printArray(getMinKNumsByBFPRT(arr, 10));}