【java】剑指offer40_最小的K个数

122 篇文章 0 订阅

题目描述:

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

示例 1:

输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:

输入:arr = [0,1,2,1], k = 1
输出:[0]
限制:

0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000

参考解题思路一:排序,对原数组从小到大排序,取前k个数

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        int[] vec = new int[k];
        Arrays.sort(arr);
        for (int i = 0; i < k; ++i) {
            vec[i] = arr[i];
        }
        return vec;
    }
}

 复杂度分析

时间复杂度:O(nlogn),其中 n是数组 arr 的长度。算法的时间复杂度即排序的时间复杂度。

空间复杂度:O(logn),排序所需额外的空间复杂度为 O(logn)。

参考解题思路二:大根堆

用一个大根堆实时维护数组的前 k 小值。首先将前 k个数插入大根堆中,随后从第k+1 个数开始遍历,如果当前遍历到的数比大根堆的堆顶的数要小,就把堆顶的数弹出,再插入当前遍历到的数。最后将大根堆里的数存入数组返回即可。

    public int[] getLeastNumbers(int[] arr, int k) {
        int[] array = new int[k];
        // 边界值判断,k为0时
        if (k == 0) {
            return array;
        }
        // 大根堆
        PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });
        for (int i = 0 ; i < k; i++) {
            queue.offer(arr[i]);
        }
        for (int i = k; i < arr.length; i++) {
            if (arr[i] < queue.peek()) {
                queue.poll(); // 取出堆顶元素
                queue.offer(arr[i]); // 重新放入新元素
            }
        }
        for (int i = 0; i < k; i++) {
            array[i] = queue.poll();
        }
        return array;
    }

复杂度分析

时间复杂度:O(nlogk),其中 n是数组 arr 的长度。由于大根堆实时维护前 k小值,所以插入删除都是O(logk) 的时间复杂度,最坏情况下数组里 n个数都会插入,所以一共需要 O(nlogk) 的时间复杂度。

空间复杂度:O(k),因为大根堆里最多 k个数。

 参考解题思路三:快速排序

快速排序原理:快速排序算法有两个核心点,分别为 “哨兵划分” 和 “递归” 。

  • 哨兵划分操作: 以数组某个元素(一般选取首元素)为 基准数 ,将所有小于基准数的元素移动至其左边,大于基准数的元素移动至其右边。
  • 递归: 对 左子数组 和 右子数组 递归执行 哨兵划分,直至子数组长度为 1 时终止递归,即可完成对整个数组的排序。
public int[] getLeastNumbers3(int[] arr, int k) {
        if (k == 0) {
            return arr;
        }
        return quickSort(arr, k, 0, arr.length-1);
    }

    public int[] quickSort(int[] arr, int k, int l, int r) {
        int i = l;
        int j = r;
        while (i < j) {
            // 最右边的值比目标值arr[l]都大
            while (i < j && arr[j] >= arr[l]) {
                j--;
            }
            // 最左边的值都比目标值小
            while (i < j && arr[i] <= arr[l]) {
                i++;
            }
            // 找到左右需要互换的值
            swap(arr, i, j);
        }
        // 交换哨兵值
        swap(arr, i, l);
        // 前k小的数值小于找到的i--递归左子数组执行哨兵划分
        if (i > k) {
            return quickSort(arr, k, l, i-1);
        }
        // 前k小的数值大于找到的i--递归右子数组执行哨兵划分
        if (i < k) {
            return quickSort(arr, k, i+1, r);
        }
        // 等于k时--截取数组前K个数字大小
        return Arrays.copyOf(arr, k);
    }

    public void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }

复杂度分析:
时间复杂度 O(NlogN) : 库函数、快排等排序算法的平均时间复杂度为 O(NlogN) 。
空间复杂度 O(N) : 快速排序的递归深度最好(平均)为 O(logN) ,最差情况(即输入数组完全倒序)为O(N)。

作者:jyd
链接:https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/solution/jian-zhi-offer-40-zui-xiao-de-k-ge-shu-j-9yze/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值