算法列表

23.合并k个升序链表 困难

布莱克2026-02-27 15:26链表分治

问题:

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:

输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
  1->4->5,
  1->3->4,
  2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6

示例 2:

输入:lists = []
输出:[]

示例 3:

输入:lists = [[]]
输出:[]

回答:

有两个方案,1是分而治之,将链表分为两半,递归合并;2是引入最小堆进行实现

分治法更易理解,并且代码量简洁,最小堆的话还需要自己实现一个堆

方案一:

/**
 * @param {ListNode[]} lists
 * @return {ListNode}
 */
var mergeKLists = function(lists) {
    if (lists.length === 0) return null;
    var divide = function(left, right) {
        //只有一个链表
        if (left == right) {
            return lists[left]
        }
        if (left + 1 == right) {
            return mergeTwo(lists[left], lists[right])
        }
        let mid = Math.floor((left + right) / 2);
        let leftList = divide(left, mid);
        let rightList = divide(mid + 1, right);
        return mergeTwo(leftList, rightList)
    }
    return divide(0, lists.length - 1)
}
var mergeTwo = function(l1, l2) {
    let dummy = new ListNode();
    let current = dummy;
    while(l1 && l2) {
        if (l1.val < l2.val) {
            current.next = l1;
            l1 = l1.next;
        } else {
            current.next = l2;
            l2 = l2.next;
        }
        current = current.next;
    }
    current.next = l1 || l2;
    return dummy.next;
}

方案二:

class MiniHeap {
    constructor() {
        this.heap = [];
    };
    isEmpty() {
        return this.heap.length === 0
    }
    //推入节点
    push(node) {
        this.heap.push(node);
        //找到当前节点
        let current = this.heap.length - 1;
        while (current > 0) {
            //找到父节点
            let parent = Math.floor((current - 1) / 2);
            if (this.heap[current].val < this.heap[parent].val) {
                //节点值小于父节点值,上浮
                [this.heap[current], this.heap[parent]] = [this.heap[parent], this.heap[current]];
                current = parent;
            } else {
                //终止上浮
                break;
            }
        }
    }
    //删除最小值
    pop() {
        if (this.heap.length === 0) {
            return null;
        }
        if (this.heap.length === 1) return this.heap.pop();
        let minNode = this.heap[0];
        //把最后一个节点移到堆顶
        this.heap[0] = this.heap.pop();
        let current = 0;
        while (true) {
            let small = current;
            let left = small * 2 + 1;
            let right = small * 2 + 2;
            if (left < this.heap.length && this.heap[small].val > this.heap[left].val) {
                small = left;
            }
            if (right < this.heap.length && this.heap[small].val > this.heap[right].val) {
                small = right;
            }
            //堆顶已经为最小值,不必再下沉
            if (small == current) {
                break;
            }
            [this.heap[current], this.heap[small]] = [this.heap[small], this.heap[current]];
            //继续向下检查
            current = small;
        }
        return minNode;
    }
}
/**
 * @param {ListNode[]} lists
 * @return {ListNode}
 */
var mergeKLists = function(lists) {
    if (lists.length === 0) {
        return null;
    }
    //引用堆的数据结构
    let minHeap = new MiniHeap();
    for (let i = 0; i < lists.length; i++) {
        if (lists[i] != null) {
            minHeap.push(lists[i])
        }
    }
    let dummy = new ListNode(0);
    let current = dummy;
    while (!minHeap.isEmpty()) {
        let node = minHeap.pop();
        current.next = node;
        current = current.next;
        if (node.next) {
            minHeap.push(node.next)
        }
    }
    return dummy.next;
};


assistant