给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 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 = [[]]
输出:[]
提示:
- k == lists.length
- 0 <= k <= 10^4
- 0 <= lists[i].length <= 500
- -10^4 <= lists[i][j] <= 10^4
- lists[i] 按 升序 排列
- lists[i].length 的总和不超过 10^4
解题思路
1.新链表的下一个节点一定是k个链表头中的最小节点
2.考虑选择使用最小堆
解题步骤
1.构建一个最小堆,并依次把链表头插入堆中
2.弹出堆顶接到输出链表,并将堆顶所在链表的新链表头插入堆中
3.等堆元素全部弹出后,合并工作就完成了
时空分析
时间复杂度:O(nlogk)
空间复杂度:O(k)
代码
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
class MinHeap {
constructor() {
this.heap = [];
}
// 交换节点位置
swap(i1, i2) {
[this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]];
}
// 获得父节点
getParentIndex(i) {
return (i - 1) >> 1;
}
// 获得左节点
getleftIndex(i) {
return 2 * i + 1;
}
// 获得右节点
getrightIndex(i) {
return 2 * i + 2;
}
// 上移
shiftUp(index) {
if (index === 0) return;
const parentIndex = this.getParentIndex(index);
if (this.heap[parentIndex] && this.heap[parentIndex].val > this.heap[index].val) {
this.swap(parentIndex, index);
this.shiftUp(parentIndex);
}
}
// 下移
shiftDown(index) {
const leftIndex = this.getleftIndex(index);
const rightIndex = this.getrightIndex(index);
if (this.heap[leftIndex] && this.heap[leftIndex].val < this.heap[index].val) {
this.swap(leftIndex, index);
this.shiftDown(leftIndex);
}
if (this.heap[rightIndex] && this.heap[rightIndex].val < this.heap[index].val) {
this.swap(rightIndex, index);
this.shiftDown(rightIndex);
}
}
// 插入
insert(value) {
this.heap.push(value);
this.shiftUp(this.heap.length - 1);
}
// 删除堆顶
pop() {
if(this.size() === 1) return this.heap.shift()
const top = this.heap[0]
// pop()方法删除数组最后一个元素并返回,赋值给堆顶
this.heap[0] = this.heap.pop();
// 对堆顶重新排序
this.shiftDown(0);
return top
}
// 获取堆顶
peek() {
return this.heap[0];
}
// 获取堆的大小
size() {
return this.heap.length;
}
}
/**
* @param {ListNode[]} lists
* @return {ListNode}
*/
var mergeKLists = function(lists) {
const res = new ListNode(0)
let p = res
const h = new MinHeap()
// 插入k个升序链表的头部节点
lists.forEach(l => {
if(l) h.insert(l)
})
// 不断的地比较最小堆中k个节点的大小
while(h.size()) {
const n = h.pop()
p.next = n
p = p.next
if(n.next) h.insert(n.next)
}
return res.next
};