TOPK问题,在一堆无序数组中找到最大或者最小的K个数。一般使用快速排序,然后查找K个数字,时间复杂度最好在nlog,最坏情况O(n2)。而BFPRT算法,又叫做中位数的中位数算法,由五个发明人的名字缩写构成,其最坏的时间复杂度O(n)。
证明方法参考:这篇文章
快速排序因为基准点默认选取第一个位置,所以在一定情况下会退化为O(n^2)。bfprt通过修改基准点选择方法,以达到稳定的时间复杂度。
代码参考上文链接。
#include<iostream>
#include<algorithm>
using namespace std;
/* 对arry[left]...arry[right]进行插入排序(升序) */
void InsertSort(int arry[], int left, int right)
{
for (int i = left + 1; i <= right; i++)
{
int j = i;
while (j - 1 >= left&&arry[j] < arry[j - 1]) //j - 1 >= left避免数组越界
{
swap(arry[j - 1], arry[j]);
j--;
}
}
}
/* 找到中位数的中位数,返回其下标 */
int FindMidMid(int arry[], int left, int right)
{
if (right - left + 1 <= 5) //如果不足5个
{
InsertSort(arry, left, right);
return (left + right) >> 1;
}
int j = left - 1;
for (int i = left; i <= right; i += 5) //5个一组插入排序取中位数,并统一放在左侧
{
InsertSort(arry, i, i + 4);
swap(arry[++j], arry[i + 2]);
}
return FindMidMid(arry, left, j); //直至仅出现一个的中位数
}
/* 划分,pivot_index为划分基准的下标 */
int Partion(int arry[], int left, int right, int pivot_index)
{
swap(arry[pivot_index], arry[right]); //把基准放置右侧末尾
int j = left;
for (int i = left; i < right; i++) //比基准小的都放在左侧
{
if (arry[i] <= arry[right])
swap(arry[j++], arry[i]);
}
swap(arry[j], arry[right]); //最后把基准换回来
return j;
}
void BFPRT(int arry[], int left, int right, int k)
{
if (left == right)
return;
int pivot_index = FindMidMid(arry, left, right); //找到中位数的中位数的下标,其值作为基准
int index = Partion(arry, left, right, pivot_index); //以基准划分
int num = index - left + 1;
if (num == k)
return;
else if (num > k)
BFPRT(arry, left, index - 1, k);
else
BFPRT(arry, index + 1, right, k - num);
}
int main()
{
int k = 1;
int arry[10] = { 1,1,2,3,1,5,-1,7,8,-10 };
BFPRT(arry, 0, 9, k);
for (int i = 0; i < 10; i++)
cout << arry[i] << " ";
cout << endl;
return 0;
}