[路飞]_最小的k个数

266 阅读2分钟

剑指 Offer 40. 最小的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]

解题思路

调用API

调用数组排序sort方法,将数组排序;返回前K个数

代码

var getLeastNumbers = function(arr, k) {
    arr.sort((a,b)=>a-b);
    return arr.slice(0,k)
};

借用快速排序

在上述思路中调用sort方法将数组整个排序,实际只需要知道排序K个数即可得到答案;所以上面的思路还有继续优化的空间

已知:题目要求返回最小的k个数,并没有要求返回有序的最小K个数;
所以可以借用快排思想;

快排

找到基准值 pivot ,大于基准值的放在基准值右侧,小于基准值的放在基准值左侧;
假如基准值正好在K这个位置,是不是基准值左侧的数据就是最小的k个数呀?

举个栗子:

现在有数组array = [5,2,6,11,8,4]

graph LR
5 --> 2--> 6 --> 11--> 8--> 4

第1步:对数组从 0 到array.length-1 找基准值,假设依数组第一个数为基准值;当前数组基准值为5;
第2步:快排思想排序,大于5的放在数组右侧,小于数组的放在左侧

graph LR
 2 --> 4 -->5--> 6 --> 11--> 8

第3步:假设此时K=2;基准值5在排序后的数组下标为index;此时k===index,直接返回数组前K个即可;
第4步:第3步的另一个分支,如果k大于2;此时index=2;index < k;所以 index+1 到 array.length-1 执行第1步
第3步:第3步的另一个分支,如果k小于2;此时index=2;index > k;所以 1 到 index 执行第1步

根据上述思路执行代码如下;

着重理解partition函数

代码

var getLeastNumbers = function (arr, k) {
  // 快速排序方法
  const len = arr.length
  let left = 0
  let right = len - 1
  let idx = partition(arr, left, right)
  while (idx !== k) {
    if (idx < k) {
      left = idx + 1
    } else {
      right = idx - 1
    }
    idx = partition(arr, left, right)
  }
  return arr.slice(0, k)
  function partition(arr, start, end) {
    let pivot = arr[start]
    let index = start
    for (let i = start; i <= end; i++) {
      if (arr[i] < pivot) {
        index++
        ;[arr[i], arr[index]] = [arr[index], arr[i]]
      }
    }
    ;[arr[start], arr[index]] = [arr[index], arr[start]]
    return index
  }
}