面试题40:数组中最小的k个数

2019-11-08  本文已影响0人  繁星追逐

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

思路一
运用快速排序的思想,选择按照把最小的k个数移到另一边,不需要进行完全排序
代码如下:

public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
         int len = input.length;
         ArrayList<Integer> list = new ArrayList<>();
         if (input == null || len == 0 || k < 0 || k > len){
             return list;
         }
         select(input, k-1);
         for (int i=0;i<k;i++){
             list.add(input[i]);
         }
         return list;
    }

    private void select(int[] input, int index) {
        int i=0;
        int j=input.length-1;
        while (i<j){
            int k=partition(input,i,j);
            if (k == index) break;
            if (k > index) j=k-1;
            if (k < index) i=k+1;
        }
    }

    private int partition(int[] input, int low, int high) {
        int i=low;
        int j=high+1;
        //选取输入的第一个数而不是第0个数
        int value=input[low];
        //循环的终止条件为由以下条件控制
        while (true){
            //循环与基准值比较大小,移动指针
            while (input[--j] > value) if (j == low) break;
            while (input[++i] < value) if (i == high) break;
            //必须是大于等于的关系,不然越界
            if (i >= j) break;
            swap(input, i, j);
        }
        swap(input, low, j);
        return j;
    }

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

思路二:
使用java内置的优先队列(默认小根堆)完成排序过程,逆序创建一个数量为k的大根优先队列,每次取出最大的顶部,最后遍历完剩下的便是最小的k个数。

/**
     * 使用Java内置的优先队列
     * @param input
     * @param k
     * @return
     */
    public ArrayList<Integer> GetLeastNumbers_Solution1(int [] input, int k) {
        int len = input.length;
        ArrayList<Integer> list = new ArrayList<>();
        if (input == null || len == 0 || k < 0 || k > len){
            return list;
        }
        //要逆转顺序(将其更改为最大优先级队列),只需在内联比较器中更改顺序或使用reversed作为,保证输出是从大到小
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
        // 只要size大于k,不断剔除最大值,最后优先队列里只剩最小的k个
        for (int a : input){
            maxHeap.offer(a);
            if (maxHeap.size() > k){
                maxHeap.poll();
            }
        }
        list.addAll(maxHeap);
        return list;
     }

同样的思路,直接使用堆排序的方法

/**
     * 最大堆实现,堆排序实现
     * @param input
     * @param k
     * @return
     */
    public ArrayList<Integer> GetLeastNumbers_Solution2(int [] input, int k) {
        int len = input.length;
        ArrayList<Integer> list = new ArrayList<>();
        if (input == null || len == 0 || k < 0 || k > len){
            return list;
        }
        //建立堆
        for (int i=len/2-1;i>=0;i--){
            adjustHeap(input, i, len);
        }

        //替换第一个元素并调整堆,直到取到合适数量的元素
        int N = len-1;
        // 元素交换之后,毫无疑问,最后一个元素无需再考虑排序问题了。
        // 接下来我们需要排序的,就是已经去掉了部分元素的堆了,这也是为什么此方法放在循环里的原因

        for (int j=N;j>N-k;j--){
            list.add(input[0]);
            swap(input, 0, j);
            adjustHeap(input, 0, j);
        }
        return list;
    }

    private void adjustHeap(int[] input, int i, int len) {
        int temp = input[i];
        for (int k=2*i+1;k<len;k=2*k+1){
            if (k+1<len && input[k] > input[k+1]){
                k++;
            }
            if (input[k] < temp){
                swap(input, k, i);
                i = k;
            }else {
                break;
            }
        }
    }

上一篇 下一篇

猜你喜欢

热点阅读