剑指offer_40_最小的k个数【javascript】

32 阅读2分钟

题目链接

leetcode.cn/problems/zu…

题目

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

题解

直接排序

复杂度分析:

  • 时间复杂度是O(NlogN),
  • 空间复杂度是O(logN)。
var getLeastNumbers = function(arr, k) {
  return arr.sort().slice(0, k);
//   return arr.sort((a, b) => a - b).slice(0, k);
};

基于快速排序

复杂度分析

  • 时间复杂度是O(NlogN)
  • 空间复杂度是O(N)
/**
 * @param {number[]} arr
 * @param {number} k
 * @return {number[]}
 */
var inventoryManagement = function(arr, k) {
    const length = arr.length;
    if (k >= length) return arr;
    arr = quickSort(arr, 0, arr.length - 1)

    return arr.slice(0, k);
};

const swap = (array, i, j) => {
    [array[i], array[j]] = [array[j], array[i]];
};

function quickSort(array, start, end) {
    if (end - start < 1) {
    return;
    }
    const target = array[start];
    let l = start;
    let r = end;
    while (l < r) {
        while (l < r && array[r] >= target) {
            r--;
        }
        swap(array, l, r);
        while (l < r && array[l] < target) {
            l++;
        }
        swap(array, l, r);
    }
    quickSort(array, start, l - 1);
    quickSort(array, l + 1, end);
    return array;
}

最大堆

/**
 * @param {number[]} arr
 * @param {number} k
 * @return {number[]}
 */
// 默认最大堆
const defaultCmp = (x, y) => x > y;
// 交换元素
const swap = (arr, i, j) => ([arr[i], arr[j]] = [arr[j], arr[i]]);
// 堆类,默认最大堆
class Heap {
    constructor(cmp = defaultCmp) {
        this.container = [];
        this.cmp = cmp;
    }
    // 插入
    insert(data) {
        const { container, cmp } = this;
        container.push(data);
        let index = this.size() - 1;
        while (index) {
            let parent = (index - 1) >> 1;
            if (!cmp(container[index], container[parent])) {
                return;
            }
            swap(container, index, parent);
            index = parent;
        }
    }
    // 弹出堆顶,并返回
    pop() {
        const { container, cmp } = this;
        if (!this.size()) {
            return null;
        }

        swap(container, 0, this.size() - 1);
        const res = container.pop();
        const length = this.size();
        let index = 0,
            exchange = index * 2 + 1;

        while (exchange < length) {
            // // 以最大堆的情况来说:如果有右节点,并且右节点的值大于左节点的值
            let right = index * 2 + 2;
            if (right < length && cmp(container[right], container[exchange])) {
                exchange = right;
            }
            if (!cmp(container[exchange], container[index])) {
                break;
            }
            swap(container, exchange, index);
            index = exchange;
            exchange = index * 2 + 1;
        }

        return res;
    }
    // 获取堆大小
    size() {
        return this.container.length;
    }
    // 获取堆顶
    peek() {
        if (this.size()) return this.container[0];
        return null;
    }
}

const getLeastNumbers = (arr, k) => {
    const res = [];
    // 创建最大堆
    const maxHeap = new Heap();
    // 先将数组前k个元素放入堆
    for (let i = 0; i < k; i++) {
        maxHeap.insert(arr[i]);
    }
    // 再遍历数组其余元素
    const len = arr.length;
    for (let i = k; i < len; i++) {
        // 若遇到比堆顶小的元素,堆顶弹出,当前数入堆
        if (arr[i] < maxHeap.peek()) {
            maxHeap.pop();
            maxHeap.insert(arr[i]);
        }
    }
    // 最后从堆中弹出k个数即可
    for (let i = 0; i < k; i++) {
        res.push(maxHeap.pop());
    }
    return res;
};

由于堆的大小是 K,空间复杂度是O(K),时间复杂度是O(NlogK)。