美文网首页
BFPRT 算法

BFPRT 算法

作者: 思想永不平凡 | 来源:发表于2020-03-20 10:56 被阅读0次

本节以今天leetcode打卡题为例来介绍下BFPRT算法。



最小的k个数

输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。

示例 1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]

示例 2:

输入:arr = [0,1,2,1], k = 1
输出:[0]

限制:
0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000

这个题很简单,常规做法,排序后选择k个数。

方法一

使用C++自带的sort函数。

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        sort(arr.begin(),arr.end());
        if(k==0){
            return {};
        }
        vector<int> res(arr.begin(),arr.begin()+k);
        return res;
    }
};

方法二

使用堆排序方法。

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        vector<int> res;
        if(k == 0){
            return res;
        }
        priority_queue<int> h;
        for(int num : arr){
            if(h.size() < k){
                h.push(num);
            }else{
                if(h.top() <= num){
                    continue;
                }else{
                    h.pop();
                    h.push(num);
                }
            }
        }
        while(!h.empty()){
            res.push_back(h.top());
            h.pop();
        }
        return res;
    }
};

有关priority_queue可以参考往期博客

这些方法都比较简单,接下来我们将介绍本节重点,BFPRT算法。

BFPRT 算法

分析这个题目,它是一个TOP-K问题,先排序,后选择k个数当然是一种不错的想法。

对于排序,如果使用性能比较好的快速排序,其平均时间复杂度为O(n\log(n)),最坏时间复杂度为O(n^2),如果使用堆排序,需要维护一个大小为k的堆(大顶堆,小顶堆),时间复杂度为O(n\log(k)),但是无论哪种排序方法,对于本题而言,其实会有些多余,因为我们只需要前k个数或者说后n-k个数,那些不需要的数也排序了。

针对这个问题,有一个比较好的算法-BFPTR算法,又称为中位数的中位数算法,它的最坏时间复杂度为O(n)。该算法的思想是修改快速选择算法的主元选取方法,提高算法在最坏情况下的时间复杂度。

在BFPTR算法中,改变了快速排序主元素pivot值的选取,在快速排序中,我们始终选择第一个元素或者最后一个元素作为pivot,而在BFPTR算法中,每次选择五分中位数的中位数作为pivot,这样做的目的就是使得划分比较合理,从而避免了最坏情况的发生。
其算法步骤为:

  1. n个元素划为\lfloor n/5 \rfloor组,每组5个,至多只有一组有n \% 5个元素。
  2. 寻找这\lceil n/5 \rceil个组中每一个组的中位数,这个过程可以用插入排序。
  3. 对步骤2中的\lceil n/5 \rceil个中位数,重复步骤1和步骤2,递归下去,直到剩下一个数字。
  4. 最终剩下的数字即为pivot,把大于它的数全放左边,小于等于它的数全放右边。
  5. 判断pivot的位置与k的大小,有选择的对左边或右边递归。

求第k大与求第n-k+1等价。

来看BFPRT算法的程序:

#include<bits/stdc++.h>

using namespace std;

//插入排序
void insert_sort(vector<int>& arr,int l,int r){
    for(int i=l+1;i<=r;i++){
        if(arr[i-1]>arr[i]){
            int t=arr[i];
            int j=i;
            while(j>l && arr[j-1]>t){
                arr[j]=arr[j-1];
                j--;
            }
            arr[j]=t;
        }
    }
}

//寻找中位数的中位数
int find_mid(vector<int>& arr,int l,int r){
    if(l==r){
        return l;
    }
    int i=0,n=0;
    for(i=l;i<r-5;i+=5){
        insert_sort(arr,i,i+4);
        n=i-l;
        swap(arr[l+n/5],arr[i+2]);
    }

    int num=r-i+1;
    if(num>0){
        insert_sort(arr,i,i+num-1);
        n=i-l;
        swap(arr[l+n/5],arr[i+num/2]);
    }
    n=n/5;
    if(n==l){
        return l;
    }
    return find_mid(arr,l,l+n);
}

//进行划分过程
int partion(vector<int>& arr,int l,int r,int p){
    swap(arr[p],arr[l]);
    int i=l;
    int j=r;
    int pivot=arr[l];
    while(i<j){
        while(arr[j]>=pivot && i<j){
            j--;
        }
        arr[i]=arr[j];
        while(arr[i]<=pivot && i<j){
            i++;
        }
        arr[j]=arr[i];
    }
    arr[i]=pivot;
    return i;
}

int BFPRT(vector<int>& arr,int l,int r,int k){
    int p=find_mid(arr,l,r);
    int i=partion(arr,l,r,p);

    int m=i-l+1;
    if(m==k){
        return arr[i];
    }
    if(m>k){
        return BFPRT(arr,l,i-1,k);
    }
    return BFPRT(arr,i+1,r,k-m);
}

int main(){
    int n;
    cin>>n;
    vector<int> arr(n,0);
    for(int i=0;i<n;i++){
        cin>>arr[i];
    }
    int k;
    cin>>k;
    cout<<"The "<<k<<"th number is "<<BFPRT(arr,0,n-1,k)<<endl;
    for(int a : arr){
        cout<<a<<" ";
    }
    cout<<endl;
    return 0;
}

测试一下:

10
9 8 2 6 4 7 2 1 3 0
3
The 3th number is 2
0 1 2 2 3 4 6 9 8 7

那么这题的BFPRT解法便是:

class Solution {
public:
    vector<int> getLeastNumbers(vector<int>& arr, int k) {
        if(k==0){
            return {};
        }
        BFPRT(arr,0,arr.size()-1,k);
        vector<int> res(arr.begin(),arr.begin()+k);
        return res;
    }
    //插入排序
    void insert_sort(vector<int>& arr,int l,int r){
        for(int i=l+1;i<=r;i++){
            if(arr[i-1]>arr[i]){
                int t=arr[i];
                int j=i;
                while(j>l && arr[j-1]>t){
                    arr[j]=arr[j-1];
                    j--;
                }
                arr[j]=t;
            }
        }
    }

    //寻找中位数的中位数
    int find_mid(vector<int>& arr,int l,int r){
        if(l==r){
            return l;
        }
        int i=0,n=0;
        for(i=l;i<r-5;i+=5){
            insert_sort(arr,i,i+4);
            n=i-l;
            swap(arr[l+n/5],arr[i+2]);
        }

        int num=r-i+1;
        if(num>0){
            insert_sort(arr,i,i+num-1);
            n=i-l;
            swap(arr[l+n/5],arr[i+num/2]);
        }
        n=n/5;
        if(n==l){
            return l;
        }
        return find_mid(arr,l,l+n);
    }

    //进行划分过程
    int partion(vector<int>& arr,int l,int r,int p){
        swap(arr[p],arr[l]);
        int i=l;
        int j=r;
        int pivot=arr[l];
        while(i<j){
            while(arr[j]>=pivot && i<j){
                j--;
            }
            arr[i]=arr[j];
            while(arr[i]<=pivot && i<j){
                i++;
            }
            arr[j]=arr[i];
        }
        arr[i]=pivot;
        return i;
    }

    int BFPRT(vector<int>& arr,int l,int r,int k){
        int p=find_mid(arr,l,r);
        int i=partion(arr,l,r,p);

        int m=i-l+1;
        if(m==k){
            return arr[i];
        }
        if(m>k){
            return BFPRT(arr,l,i-1,k);
        }
        return BFPRT(arr,i+1,r,k-m);
    }
};

结果:


图片.png

本文参考了:

相关文章

  • 2021-02-01 BFPRT算法

    BFPRT算法

  • BFPRT算法

    1.在数组中,每相邻5个数成一组,2.每个小组找出中位数3.然后在递归调用一次4.小于放左边,等于放中间,大于放右边

  • bfprt算法

    前言 在一个数组中求其第k大或者第k小的数的问题(其实就是找按降序或升序排好序的数组的下标为k-1的元素),简称T...

  • BFPRT 算法

    本节以今天leetcode打卡题为例来介绍下BFPRT算法。 最小的k个数 输入整数数组 arr ,找出其中最小的...

  • JavaScript BFPRT 算法

    BFPRT 算法 背景 在一组数中求其前 k 小的数,简称TOP-K问题。而目前解决TOP-K问题最有效的算法即是...

  • 线性查找法(BFPRT)

    BFPRT算法解决的问题十分经典,即从某n个元素的序列中选出第k大(第k小)的元素,通过巧妙的分 析,BFPRT可...

  • Median of medians无序数组寻找中位数最差O(n)

    在无序数组中寻找中位数,最差复杂度为O(n). 实现算法为Median of medians,又叫BFPRT算法。...

  • 基本算法——BFPRT线性查找算法

    BFPRT算法解决的问题十分经典,即从某n个元素的序列中选出第k大(第k小)的元素,通过巧妙的分 析,BFPR...

  • 左神算法笔记——BFPRT算法

    简介 通常我们需要在一大堆数中求前k大的数。比如在搜索引擎中求当天用户点击次数排名前10000的热词,在文本特征选...

  • 算法进阶二

    BFPRT算法: 介绍窗口以及窗口内最大值或最小值的更新结构(单调双向队列) 介绍单调栈结构

网友评论

      本文标题:BFPRT 算法

      本文链接:https://www.haomeiwen.com/subject/orudyhtx.html