BFPRT算法:求第K小或者第K大的数

问题:

一个数组中求第k小或者第k大的数

思路(转自http://blog.csdn.net/liuxiao214/article/details/78597508):

不通过排序求第k小的数,时间复杂度为O(N)。

主要是利用快排中的partition过程。

1、找到一个划分值,按照partition的过程,分为小于区、等于区、大于区,则可知等于区是在整个数组有序后不变的部分。

2、求第K小的数,就是数组有序后下标为k-1的数。

3、所以,如果等于区包含这个k-1,则等于区的数就是第k小的数,直接返回。否则,继续partition过程。

BFPRT的思想是对划分值的选择进行优化,不再是随机选取划分值,而是通过这样一个过程:

1、将数组分为5个一组,不足5个的自动成一组。划分组所用时间为O(1)。

2、将每个组进行组内排序,可用插入排序。因为排序只有5个数,时间复杂度可记为O(1),所有组都排序为O(N)。

3、得到每个组内的上中位数,然后将这些上中位数组成新的数组mediums。

4、求出mediums数组中的上中位数pvalue,不使用排序,用的是递归调用BFPRT的过程,求上中位数就是求mediums数组第mediums.size()/2小的数。

5、此时得到的pvalue就是选取的划分值,然后进行partition过程即可。

为什么要这样选取划分值,这是因为,假设数组长度为n,则mediums数组的长度为n/5,则得到的pvalue在medium中会有n/10的数比其小,而这n/10的数,在自己的5个数的小组中,又会有3个数比pvalue小,所以,至少有n/10*3即3n/10个数比pvalue小,至多有7n/10个数比pvalue大,可以确定的淘汰掉3n/10的数。这样划分比较均衡。

6、刚才拿到pvalue划分值之后,进行partition过程,会返回等于区的边界下标。

7、如果k在等于的范围内,则返回pvalue;k在等于区的左边,则递归调用左边小于区的部分;k在等于区的右边,则递归调用大于区的部分。

代码:

public static int[] getMinKNumsByBFPRT(int[]arr,int k){
        if (k<1||k>arr.length){
            return arr;
        }

        int min = getMinKthByBFPRT(arr,k);
        int[] help = new int[k];
        int index = 0;
        for (int i =0;i=pArr[0]&&i<=pArr[1]){
            return arr[i];
        }else if (im){
                swap(arr,--more,cur);
            }else if (arr[cur]=begin&&arr[j]>arr[j+1];j--){
                swap(arr,j,j+1);
            }
        }
    }

    public static void swap(int[] arr, int index1, int index2) {
        int tmp = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = tmp;
    }

你可能感兴趣的:(BFPRT算法:求第K小或者第K大的数)