剑指Offer对答如流系列 - 最小的k个数

Posted jefferychenxiao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指Offer对答如流系列 - 最小的k个数相关的知识,希望对你有一定的参考价值。

面试题40:最小的k个数

题目描述

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

问题分析

最容易想到的方法就是排序,取相应数字的元素即可。我这里为了方便直接使用了快排来做这个。

想拉开与别人的差距,难免需要优化时间效率,我们可以这样做:依次遍历n个整数,用一个容器存放最小的k个数字,每遇到比容器中最大的数字还小的数字时,将最大值替换为该数字。容器可以使用最大堆或者红黑树来实现。堆相比红黑树更容易实现,我们这里采用堆。

问题解答

思路一(不推荐)

    public ArrayList<Integer> GetLeastNumbers(int [] input, int k) {
        ArrayList<Integer> leastNumbers = new ArrayList<>();
        if(input==null || k<=0 || k>input.length) {
            return leastNumbers;
        }

        int start=0;
        int end=input.length-1;
        int index=partition(input,start,end);

        while(index!= k-1){
            if(index< k-1){
                start=index+1;
                index=partition(input,start,end);
            }else{
                end=index-1;
                index=partition(input,start,end);
            }
        }
        for(int i=0;i<k;i++){
            leastNumbers.add(input[i]);
        }
        return leastNumbers;
    }

    private int partition(int[] arr, int start,int end){
        int pivotKey=arr[start];
        while(start<end){
            while(start<end && arr[end]>=pivotKey) {
                end--;
            }
            swap(arr,start,end);
            while(start<end && arr[start]<=pivotKey){
                start++;
            }
            swap(arr,start,end);
        }
        return start;
    }

    private void swap(int[] arr, int i,int j){
        int temp=arr[i];
        arr[i]=arr[j];
        arr[j]=temp;
    }

思路二(推荐)

  public ArrayList<Integer> GetLeastNumbers(int [] input, int k) {
        ArrayList<Integer> leastNumbers = new ArrayList<>();
        if(input==null || k<=0 || k>input.length) {
            return leastNumbers;
        }
        int[] numbers=new int[k];  //用于放最小的k个数
        //先放入前k个数
        System.arraycopy(input, 0, numbers, 0, k);

        for(int i = k/2-1; i>=0; i--){
            adjustHeap(numbers,i,k-1);//将数组构造成最大堆形式
        }
        for(int i = k; i<input.length; i++){
            if(input[i]<numbers[0]){ //存在更小的数字时
                numbers[0]=input[i];
                adjustHeap(numbers,0,k-1);//重新调整最大堆
            }
        }
        for(int n:numbers)
            leastNumbers.add(n);
        return leastNumbers;
    }

    //最大堆的调整方法
    private void adjustHeap(int[] arr,int start,int end){
        int temp=arr[start];
        int child=start*2+1;
        while(child<=end){
            if(child+1<=end && arr[child+1]>arr[child]) {
                child++;
            }
            if(arr[child]<temp) {
                break;
            }
            arr[start]=arr[child];
            start=child;
            child=child*2+1;
        }
        arr[start]=temp;
    }

以上是关于剑指Offer对答如流系列 - 最小的k个数的主要内容,如果未能解决你的问题,请参考以下文章

剑指Offer对答如流系列 - 数据流中的中位数

剑指Offer对答如流系列 - 扑克牌的顺子

剑指Offer对答如流系列 - 二进制中 1 的个数

剑指Offer对答如流系列 - 剪绳子

剑指offer系列55---最小的k个数

剑指Offer对答如流系列 - 链表中倒数第k个结点