Kth Number问题的三种解法(java实现)

  • 问题描述

给定一个无序数组,找到其中第k大的数。

  • 解决方案

方案1:堆解法

维护一个包含k个数的堆,时间复杂度O(nlogk)

方案2:快排变形

方案3:BFPRT算法

本质上是快排变性算法的优化,每次在partion选取主元时取中位数,而快排主元是随机的。

堆解法

   public static int kthNumberHeapSolution(int[] arr,int k){
        //将前k个元素调整为最小堆
        for(int i = 1;i < k;i ++){
            int cur = i;
           while(cur > 0 && arr[cur] < arr[(cur - 1) / 2]){
               int temp = arr[cur];
               arr[cur] = arr[(cur - 1)/2];
               arr[(cur - 1) / 2] = temp;
               cur = (cur - 1) / 2;
           }
        }
        for(int i = k;i < arr.length;i ++){
            if(arr[i] > arr[0]){
                int t = arr[0];
                arr[0] = arr[i];
                arr[i] = t;
                //调整堆
                int cur = 0;
                while(cur * 2 + 1 < k){
                    if(cur * 2 + 2 < k && arr[cur] > arr[cur * 2 + 2] &&
                            arr[cur * 2 + 1] > arr[cur * 2 + 2]){
                        int temp = arr[cur];
                        arr[cur] = arr[cur * 2 + 2];
                        arr[cur * 2 + 2] = temp;
                        cur = cur * 2 + 2;
                    }
                    else if(arr[cur] > arr[cur * 2 + 1]){
                        int temp = arr[cur];
                        arr[cur] = arr[cur * 2 + 1];
                        arr[cur * 2 + 1] = temp;
                        cur = cur * 2 + 1;
                    }
                    else
                        break;
                }
            }
        }
        return arr[0];
    }

快排变形

public static int kthNumberQuickSortSolution(int[] arr,int k){
        return kthNumberQuickSortSolutionHelp(arr,0,arr.length - 1,k);
    }

    public static int kthNumberQuickSortSolutionHelp(int[] arr,int l,int r,int k){

        int t = l + (int)((r - l) * Math.random());

        int[] partions = partion(arr,l,r,t);
        int max = r - partions[0] + 1;
        int min = r - partions[1] + 1;
        if(max >= k && min <= k)
            return arr[partions[0]];
        else if (max < k){
            return kthNumberQuickSortSolutionHelp(arr,l,partions[0] - 1,k - max);
        }
        else{
            return kthNumberQuickSortSolutionHelp(arr,partions[1] + 1,r,k );
        }
    }

    public static int[] partion(int[] arr,int l,int r,int k){
        int temp = arr[k];
        arr[k] = arr[l];
        arr[l] = temp;
        int less = l ;
        int more = r;
        for(int i = l + 1;i <= more;i ++){
            if(arr[i] < temp){
                int t = arr[i];
                arr[i] = arr[less];
                arr[less] = t;
                less ++;
            }
            else if(arr[i] > temp){
                int t = arr[i];
                arr[i] = arr[more];
                arr[more] = t;
                more --;
                i --;
            }
        }
        return new int[]{less,more};
    }

BFPRT解法

 public static int BFPRT(int[] arr,int k){
        return kthNumberBFPRT(arr,0,arr.length - 1,k);
    }

    public static int kthNumberBFPRT(int[] arr,int l,int r,int k){
        int mid = findMid(arr,l,r);
        int[] partions = partion(arr,l,r,mid);
        int max = r - partions[0] + 1;
        int min = r - partions[1] + 1;
        if(max >= k && min <= k)
            return arr[partions[0]];
        else if (max < k){
            return kthNumberBFPRT(arr,l,partions[0] - 1,k - max);
        }
        else{
            return kthNumberBFPRT(arr,partions[1] + 1,r,k );
        }
    }

    public static int findMid(int[] arr,int l,int r){
        if(r - l < 4){
            for(int i = l + 1;i <= r;i ++){
                for(int j = i;j > l;j --){
                    if(arr[j] < arr[j - 1]){
                        int temp = arr[j];
                        arr[j] = arr[j - 1];
                        arr[j - 1] = temp;
                    }
                    else break;
                }
            }
            return l + ((r - l ) >> 1);
        }

        for(int i = l;i + 4 <= r;i += 5){
            for(int j = l + 1 ;j < l + 5;j ++){
                for(int k = j;k >= l + 1;k --){
                    if (arr[k] < arr[k - 1]){
                        int temp = arr[k];
                        arr[k] = arr[k - 1];
                        arr[k - 1] = temp;
                    }
                    else
                        break;
                }
            }
        }
        int index = l;
        for(int i = l;i + 4 <= r;i += 5){
            int temp = arr[i + 2];
            arr[i + 2] = arr[index];
            arr[index] = temp;
            index ++;
        }
        return findMid(arr,l,index - 1);
    }

算法测试


public static void main(String[] args) {
        int len = 1000000;
        int k = 1000;
        int[] arr = new int[len];
        for(int i = 0;i < len;i ++)
            arr[i] = i;

        long start = System.nanoTime();
        System.out.println(kthNumberHeapSolution(arr,k));
        long end = System.nanoTime();
        System.out.println("堆方法用时:" + (end- start) / 1000000 + "ms") ;

        start = System.nanoTime();
        System.out.println(kthNumberQuickSortSolution(arr,k));
        end = System.nanoTime();
        System.out.println("快排方法用时:" + (end- start) / 1000000 + "ms") ;

        start = System.nanoTime();
        System.out.println(BFPRT(arr,k));
        end = System.nanoTime();
        System.out.println("BFPRT方法用时:" + (end- start) / 1000000 + "ms") ;

    }

通过对比发现,堆解法明显和k的大小有关系。而快排变形解法时间复杂度一般优于BFPRT解法,可能是因为BFPRT解法在找中位数时耗费了计算资源,因为理论上BFPRT解法时间复杂度为O(N)。

展开阅读全文
©️2020 CSDN 皮肤主题: 黑客帝国 设计师: 上身试试 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值