一刷332-剑指 Offer 40. 最小的k个数(e)

题目;
输入整数数组 arr ,找出其中最小的 k 个数。例如,输入451627388个数字,
则最小的4个数字是1234---------------------
示例:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]

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

限制:
0 <= k <= arr.length <= 10000
0 <= arr[i] <= 10000
-------------
思考:基于快速排序的数组划分
本题使用排序算法解决最直观,对数组 arr 执行排序,再返回前 k 个元素即可。(使用快速排序)
-------------
快速排序原理:
快速排序算法有两个核心点,分别为 “哨兵划分” 和 “递归” 。
哨兵划分操作: 以数组某个元素(一般选取首元素)为 基准数 ,
将所有小于基准数的元素移动至其左边,大于基准数的元素移动至其右边。

为哨兵划分操作流程。通过一轮 哨兵划分 ,
可将数组排序问题拆分为 两个较短数组的排序问题 (本文称之为左(右)子数组)。
-----
递归: 对 左子数组 和 右子数组 递归执行 哨兵划分,直至子数组长度为 1 时终止递归,
	  即可完成对整个数组的排序。
-----------------------
如下图所示,为示例数组 [2,4,1,0,3,5] 的快速排序流程。
观察发现,快速排序和 二分法 的原理类似,都是以log 时间复杂度实现搜索区间缩小。

在这里插入图片描述

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

根据快速排序原理,如果某次哨兵划分后 基准数正好是第 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 left, int right) {
        int i = left, j = right;
        while (i < j) {//将当前arr[left]作为基准数
            while (i < j && arr[j] >= arr[left]) j--;//右侧出现比arr[left]小的
            while (i < j && arr[i] <= arr[left]) i++;//左侧出现比arr[left]大的
            swap(arr, i, j);//进行交换
        }
        swap(arr, i, left);//交换left 和 i  区间被分为[left, i - 1] [i + 1, right]
        if (i > k) return quickSort(arr, k, left, i - 1);
        if (i < k) return quickSort(arr, k, i + 1, right);
        return Arrays.copyOf(arr, k);
    }
    private void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}
/**
if (i < k) return quickSort(arr, k, i + 1, right);
若 k < i,代表第 k + 1小的数字在 左子数组 中,则递归左子数组;
-----------
if (i > k) return quickSort(arr, k, left, i - 1);
若 k > i,代表第 k + 1 小的数字在 右子数组 中,则递归右子数组;
------------------
return Arrays.copyOf(arr, k);
若 k = i ,代表此时 arr[k] 即为第 k + 1小的数字,则直接返回数组前 k 个数字即可;
-------------------
Arrays 的 copyOf () 方法 传回的数组是新的数组对象,改变传回数组中的元素值,不会影响原来的数组。. copyOf ()的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值
 */

LC

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值