剑指 Offer II 061. 和最小的 k 个数对

77 阅读1分钟

剑指 Offer II 061. 和最小的 k 个数对

  1. 插入: 从完全二叉树末尾插入元素,通过 上浮 找到对应的位置
var kSmallestPairs = function (nums1, nums2, k) {
    const n = nums1.length;
    const m = nums2.length;
    // 求和最小的k 个数对 : 最大堆
    // 由于数组升序,那么前k 大值 肯定在 nums1 和 nums2 的前k个数据中
    let sum;
    let maxHeap = new MaxHeap(k);
    // 添加一个数组 [sum,num1,num2]
    for (let i = 0; i < k && i < n; i++) {
        for (let j = 0; j < k && j < m; j++) {
            sum = nums1[i] + nums2[j];
            // 插入 arr 通过 sum 进行堆排序
            maxHeap.insert([sum, nums1[i], nums2[j]]);
        }
    }
    const result = [];
    for (let i = 1; i <= maxHeap.size; i++) {
        result.push([maxHeap.data[i][1], maxHeap.data[i][2]])
    }
    return result;
};

class MaxHeap {
    constructor(k) {
        this.maxSize = k;
        this.size = 0
        this.data = [null]
    }
    // 1. 插入: 从完全二叉树末尾插入元素,通过 上浮 找到对应的位置
    insert(arr) {
        var i = ++this.size;
        // 如果要插入的值大于父节点,就持续上沉
        while (i > 1 && this.data[i >> 1][0] < arr[0]) {
            var j = i >> 1
            this.data[i] = this.data[j];
            i = j
        }
        this.data[i] = arr;
        if (this.size > this.maxSize) this.delete();
    }
    // 2.删除 : 将堆尾 元素 temp 插入到 堆顶
    // 对于 temp 下沉有两种选择:
    // 1. 只有左节点,我们跟左节点进行比交,如果左节点更大,则替换\
    // 2. 同时具备左节点和右节点, 我们选择更大的进行替换
    delete() {
        // 堆空
        if (this.size < 1) return false;
        // saveItem 保存一下被删除的堆顶元素,最后将要返回出去
        const saveItem = this.data[1];
        // 获取堆尾元素 -- 将它置为 堆顶 
        const temp = this.data.pop();
        this.size--;
        let parent = 1; // parent 代表 temp 当前所处的位置, 因为 temp 替换到了堆顶,所以当前它是 1 
        // 将新的对顶元素下沉 , 如果 parent*2 说明 parent 还有左子节点, 判断是否需要替换
        while (parent * 2 <= this.size) {
            // 左子节点 = parent * 2 , 右子节点 = parent * 2 + 1
            let child = parent * 2;
            // 判断一下 右儿子的 存在 
            // 1. 没右儿子,将 temp 与 当前元素进行比较
            // 2. 同时存在 左右儿子, 取两者间最大的进行比较
            if (child != this.size && this.data[child][0] < this.data[child + 1][0]) child++; // 左 ———> 右
            if (temp[0] >= this.data[child][0]) break; // 如果 temp 比左右子节点中的 最大值 还要大,则位置合适,循环中止
            else this.data[parent] = this.data[child]; // 否则 将 temp 的子节点 与 temp 替换位置
            // 并进行下一层循环
            parent = child
        }
        this.data[parent] = temp; // 找到对应位置后,循环中断,将 temp 的值存储到 当前位置
        return saveItem;
    }
}
var nums1 = [1, 7, 11], nums2 = [2, 4, 6], k = 7
console.log(kSmallestPairs(nums1, nums2, k))