BFPRT算法,寻找第K小(大)或前K小(大)的最快方法

BFPRT算法,是Blum、Floyd、Pratt、Rivest、Tarjan提出,最坏时间复杂度为O(n)。它是针对TOP-K问题的极好方法。

算法思想:

  1. 将原始序列中所有元素按5个元素一组进行划分,最后一组可能少于5个元素,对每一组元素进行插入排序选出中间的元素即为中位数,这个过程每组可以同时进行;
  2. 递归寻找各组中位数组合起来的新子序列的中位数和其下标;
  3. 利用快速排序的思想,将这个中位数作为枢纽(与序列第一个元素交换位置),进行快速排序,并得到枢纽的最终位置i,那么此枢纽左边都小于等于它,右边都大于等于它。
  4. 判断i和k的关系,如果i+1 = k,则直接返回,若i+1>k,说明第k小的元素在枢纽的左侧,那么递归枢纽左侧子序列,若i+1<k,说明第k小的元素在枢纽的右侧,那么递归枢纽右侧子序列

java代码如下:

import java.util.ArrayList;
/**
 * @author LiuZhiguo
 * @date 2019/10/11 22:13
 */
public class BFPRTSort {
    void insertSort(int[] R, int low, int high){
        int temp;
        int j;
        for (int i = low + 1; i <= high; i++){
            temp = R[i];
            j = i - 1;
            while (j >= low && R[j] > temp)
                R[j + 1] = R[j--];
            R[j + 1] = temp;
        }
    }
    //递归寻找中位数的中位数
    int findMid(int[] R, int low, int high) {
        if (low == high)
            return R[low];
        int i,k=0;
        //将序列划分为每5个数一组,找到每组的中位数
        for (i=low;i+4<=high;i+=5){
            insertSort(R,i,i+4);
            k = i-low; //每组的首元素下标
            swap(R[low+(k/5)], R[i+2]); //将每组中位数交换到最前面
        }
        int n = high-i+1;
        if (n>0) {
            insertSort(R,i,high);
            k = i-low;
            swap(R[low+(k/5)], R[i+n/2]);
        }
        k = k/5;//得到组数
        if (k==0)
            return R[low];
        return findMid(R,low,low+k);
    }
    //寻找中位数的下标
    int findMidId(int[] R,int low, int high, int mid){
        for (int i=0;i<=high;i++){
            if (mid == R[i])
                return i;
        }
        return -1;
    }
    //进行划分过程
    int partion(int[] R,int low, int high, int index){
        if (low <= high){
            //将中位数和首元素交换
            swap(R[index], R[low]);
            int temp = R[low];
            int i=low,j=high;
            while (i != j){
                while (i<j&& temp<R[j])
                    j--;
                R[i] = R[j];
                while (i<j&& temp>R[i])
                    i++;
                R[j] = R[i];
            }
            R[i] = temp;
            return i;
        }
        return -1;
    }
    int BFPRT(int[] R,int low, int high, int k){
        // 中位数
        int median = findMid(R,low,high);
        // 中位数的下标
        int index = findMidId(R,low,high,median);
        int newIndex = partion(R,low,high,index);
        int rank = newIndex - low +1;
        if (rank == k)
            return R[newIndex];
        else if (rank > k)
            return BFPRT(R,low,newIndex-1,k);
        else return BFPRT(R,newIndex+1,high,k-rank);
    }
    private void swap(int x,int y) {
        x=x+y;
        y=x-y;
        x=x-y;
    }
    public static void main(String[] args){
        BFPRTSort bfprt = new BFPRTSort();
        int[] array = {49,38,65,97,76,13,27,49};
        int result = bfprt.BFPRT(array,0,array.length-1,3);
        System.out.println(array[2]);//找到第三小的元素
        //找出前三小的元素
        ArrayList<Integer> res = new ArrayList<Integer>();
        for (int i=0;i<3;i++){
            res.add(array[i]);
        }
        System.out.println(res);
    }
}

运行结果:

38
[27, 13, 38]

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值