给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
解法1 暴力解法
思路
和之前的链表排序一个思路。先保存下来,再对数组进行排序,最后修改指针。
代码
function mergeKLists(lists: Array<ListNode | null>): ListNode | null {
if (!lists || !lists.length) {
return null;
}
const nodeList = [];
lists.forEach(list => {
let cur = list;
while (cur) {
nodeList.push(cur);
cur = cur.next;
}
});
nodeList.sort((a, b) => a.val - b.val);
for (let i = 0; i < nodeList.length; i++) {
nodeList[i].next = i === nodeList.length - 1 ? null : nodeList[i + 1];
}
return nodeList[0] ?? null;
};
时空复杂度
时间复杂度:重点是排序的开销,所以是 O(n logn)
空间复杂度:需要存下所有节点 O(n)
解法2 最小堆解法
思路
暴力解法是把所有节点的值都放数组里排序,再一个个链接指针。但是没有真正利用链表是有序的这个信息。
就好比面前有几堆牌,每堆牌都是有序的,你需要构建一个有序的牌组。
那是不是可以每次就比较堆上最小的数,一个一个组成新的有序牌组。
而这种方式就是最小堆,这个解法最麻烦的地方就是需要自己构造最小堆。
代码
class MinHeap {
heap: ListNode[];
constructor() {
this.heap = [];
}
private swap(i: number, j: number) {
[this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]];
}
private heapifyUp(index: number) {
while (index > 0) {
const parent = Math.floor((index - 1) / 2);
if (this.heap[parent].val > this.heap[index].val) {
this.swap(parent, index);
index = parent;
} else {
break;
}
}
}
private heapifyDown(index: number) {
const length = this.heap.length;
while (true) {
let smallest = index;
const left = 2 * index + 1;
const right = 2 * index + 2;
if (left < length && this.heap[left].val < this.heap[smallest].val) {
smallest = left;
}
if (right < length && this.heap[right].val < this.heap[smallest].val) {
smallest = right;
}
if (smallest !== index) {
this.swap(index, smallest);
index = smallest;
} else {
break;
}
}
}
insert(node: ListNode) {
this.heap.push(node);
this.heapifyUp(this.heap.length - 1);
}
extractMin(): ListNode | null {
if (this.heap.length === 0) return null;
const min = this.heap[0];
const last = this.heap.pop();
if (this.heap.length > 0 && last) {
this.heap[0] = last;
this.heapifyDown(0);
}
return min;
}
isEmpty(): boolean {
return this.heap.length === 0;
}
}
function mergeKLists(lists: Array<ListNode | null>): ListNode | null {
if (!lists || !lists.length) {
return null;
}
const heap = new MinHeap();
for (const list of lists) {
if (list) heap.insert(list);
}
const dummy = new ListNode(0);
let current = dummy;
while (!heap.isEmpty()) {
const node = heap.extractMin()!;
current.next = node;
current = current.next;
if (node.next) {
heap.insert(node.next);
}
}
return dummy.next;
};
时空复杂度
时间复杂度:每个节点入堆一次,出堆一次,O(n logk),k 是数组的长度,n 是总节点个数
空间复杂度:堆的空间每次都只有 k 个节点,所以是 O(k)