BFPRT算法(TOPK问题的O(n)时间复杂度方法)

        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;
}

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值