LeetCode 38 最小的k个数

题目:输入整数数组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

方法一:排序

本题使用排序算法解决最直观,对数组 arr 执行排序,再返回前 k 个元素即可。快速排序

快速排序原理:

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

哨兵划分操作:以数组某个元素(一般选取首元素)为基准数,将所有小于基准数的元素移至其左边,大于基准数的元素移动至其右边。

递归:对左子树组和右子树组递归进行哨兵划分,直至子数组长度为1时终止递归,即可完成对整个数组的排序。

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        quickSort(arr, 0, arr.length - 1);
        return Arrays.copyOf(arr, k);
    }
    private void quickSort(int[] arr, int l, int r) {
        // 子数组长度为 1 时终止递归
        if (l >= r) return;
        // 哨兵划分操作(以 arr[l] 作为基准数)
        int i = l, j = r;
        while (i < j) {
            while (i < j && arr[j] >= arr[l]) j--;
            while (i < j && arr[i] <= arr[l]) i++;
            swap(arr, i, j);
        }
        swap(arr, i, l);
        // 递归左(右)子数组执行哨兵划分
        quickSort(arr, l, i - 1);
        quickSort(arr, i + 1, r);
    }
    private void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

方法二:基于快速排序的数组划分

题目只要求返回最小的k个数,对这k个数的顺序没有要求。因此,只需要将数组划分为最小的k个数和其他数字两部分即可,而快速排序的哨兵划分可完成此目标。

根据快速排序的原理,如果某次哨兵划分后基准数正好是第k+1小的数字,那么此时基准数左边的所有数字便是题目所求的最小的k个数

根据此思路,考虑每次哨兵划分后,判断基准数在数组中的索引是否等于k,若true则直接返回此时数组的前k个数字即可。

算法流程:

getLeastNumbers() 函数:

  1. 若 k 大于数组长度,则直接返回整个数组;
  2. 执行并返回 quick_sort() 即可;

quick_sort() 函数:(注意,此时 quick_sort() 的功能不是排序整个数组,而是搜索并返回最小的 k 个数。)

  1. 哨兵划分:
  • 划分完毕后,基准数为 arr[ i ] ,左 / 右子数组区间分别为 [ l , i - 1 ], [ i + 1, r ];

     2. 递归或返回:

  • 若 k < i ,代表第 k + 1 小的数字在 左子数组 中,则递归左子数组;
  • 若 k > i ,代表第 k + 1 小的数字在 右子数组 中,则递归右子数组;
  • 若 k = i ,代表此时 arr[ k ] 即为第 k + 1 小的数字,则直接返回数组前 k 个数字即可;
class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if (k >= arr.length) return arr;
        return quickSort(arr, k, 0, arr.length - 1);
    }
    private int[] quickSort(int[] arr, int k, int l, int r) {
        int i = l, j = r;
        while (i < j) {
            while (i < j && arr[j] >= arr[l]) j--;
            while (i < j && arr[i] <= arr[l]) i++;
            swap(arr, i, j);
        }
        swap(arr, i, l);
        if (i > k) return quickSort(arr, k, l, i - 1);
        if (i < k) return quickSort(arr, k, i + 1, r);
        return Arrays.copyOf(arr, k);
    }
    private void swap(int[] arr, int i, int j) {
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值