zhangdizhangdi

最小K个数

题目

LeetCode 中等

原来是 剑指 offer40,原题变了。

设计一个算法,找出数组中最小的 k 个数。以任意顺序返回这 k 个数均可。

示例:
输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]

题解

方法: sort

O(nlogn) O(logn)

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

const arr = [3, 44, 5, 32, 2, 45, 7],
  k = 3
console.log('🌰', getLeastNumbers(arr, k))
执行结果
🌰 [ 2, 3, 5 ]

方法:堆排序

O(nlogk) O(k)

js
function getLeastNumbersHeap(nums, k) {
  let heapSize = nums.length
  const len = nums.length
  buildMaxHeap(heapSize)

  console.log('init maxHeap', [...nums])

  for (let i = 0; i <= len - k - 1; i++) {
    swap(nums, 0, len - 1 - i)
    --heapSize
    maxHeapify(0, heapSize)

    console.log('', i, [...nums])
  }
  return nums.slice(0, k)

  // 自下而上构建一颗大顶堆
  function buildMaxHeap(heapSize) {
    for (let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
      maxHeapify(i, heapSize)
    }
  }
  // 从左向右,自上而下的调整节点
  function maxHeapify(i, heapSize) {
    let l = i * 2 + 1
    let r = i * 2 + 2
    let largest = i
    if (l < heapSize && nums[l] > nums[largest]) {
      largest = l
    }
    if (r < heapSize && nums[r] > nums[largest]) {
      largest = r
    }
    if (largest !== i) {
      swap(nums, i, largest)
      maxHeapify(largest, heapSize)
    }
  }
  //交换
  function swap(arr, i, j) {
    ;[arr[i], arr[j]] = [arr[j], arr[i]]
  }
}

const arr = [0, 0, 0, 1, 2, 2, 3, 7, 6, 1],
  k = 3
console.log('🌰', getLeastNumbersHeap(arr, k))
执行结果
init maxHeap [
  7, 6, 3, 1, 2,
  2, 0, 0, 0, 1
]
 0 [
  6, 2, 3, 1, 1,
  2, 0, 0, 0, 7
]
 1 [
  3, 2, 2, 1, 1,
  0, 0, 0, 6, 7
]
 2 [
  2, 1, 2, 0, 1,
  0, 0, 3, 6, 7
]
 3 [
  2, 1, 0, 0, 1,
  0, 2, 3, 6, 7
]
 4 [
  1, 1, 0, 0, 0,
  2, 2, 3, 6, 7
]
 5 [
  1, 0, 0, 0, 1,
  2, 2, 3, 6, 7
]
 6 [
  0, 0, 0, 1, 1,
  2, 2, 3, 6, 7
]
🌰 [ 0, 0, 0 ]

方法:快排的优化版

O(n) O(n)

js
function getLeastNumbersQuickSelect(arr, k) {
  if (k < 1) return []
  quickSort(0, arr.length - 1)
  return arr.slice(0, k)

  function quickSort(left, right) {
    console.log('li', left, 'ri', right, arr)

    if (left >= right) {
      return
    }

    let pivotNum = arr[right]

    console.log('pivotNum', pivotNum)

    let i = left
    let targetIndex = left
    //从左边开始循环对比
    while (i < right) {
      if (arr[i] <= pivotNum) {
        console.log(
          '小于等于 目标值 indexs i',
          i,
          'targetIndex',
          targetIndex,
          ' | nums',
          arr[i],
          arr[targetIndex],
        )

        swap(arr, targetIndex, i)
        targetIndex++
      }
      i++
    }

    console.log(
      '--- indexs i',
      i,
      'targetIndex',
      targetIndex,
      ' | nums',
      arr[right],
      arr[targetIndex],
    )

    swap(arr, targetIndex, right)

    console.log(arr)
    console.log('----')

    if (targetIndex === k) {
      return
    } else if (targetIndex > k) {
      quickSort(left, targetIndex - 1)
    } else {
      quickSort(targetIndex + 1, right)
    }

    function swap(arr, i, j) {
      ;[arr[i], arr[j]] = [arr[j], arr[i]]
    }
  }
}

const arr = [0, 1, 1, 2, 4, 4, 1, 3, 3, 2],
  k = 6
console.log('🌰', getLeastNumbersQuickSelect(arr, k))
执行结果
li 0 ri 9 [
  0, 1, 1, 2, 4,
  4, 1, 3, 3, 2
]
pivotNum 2
小于等于 目标值 indexs i 0 targetIndex 0  | nums 0 0
小于等于 目标值 indexs i 1 targetIndex 1  | nums 1 1
小于等于 目标值 indexs i 2 targetIndex 2  | nums 1 1
小于等于 目标值 indexs i 3 targetIndex 3  | nums 2 2
小于等于 目标值 indexs i 6 targetIndex 4  | nums 1 4
--- indexs i 9 targetIndex 5  | nums 2 4
[
  0, 1, 1, 2, 1,
  2, 4, 3, 3, 4
]
----
li 6 ri 9 [
  0, 1, 1, 2, 1,
  2, 4, 3, 3, 4
]
pivotNum 4
小于等于 目标值 indexs i 6 targetIndex 6  | nums 4 4
小于等于 目标值 indexs i 7 targetIndex 7  | nums 3 3
小于等于 目标值 indexs i 8 targetIndex 8  | nums 3 3
--- indexs i 9 targetIndex 9  | nums 4 4
[
  0, 1, 1, 2, 1,
  2, 4, 3, 3, 4
]
----
li 6 ri 8 [
  0, 1, 1, 2, 1,
  2, 4, 3, 3, 4
]
pivotNum 3
小于等于 目标值 indexs i 7 targetIndex 6  | nums 3 4
--- indexs i 8 targetIndex 7  | nums 3 4
[
  0, 1, 1, 2, 1,
  2, 3, 3, 4, 4
]
----
li 6 ri 6 [
  0, 1, 1, 2, 1,
  2, 3, 3, 4, 4
]
🌰 [ 0, 1, 1, 2, 1, 2 ]