题目描述
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
解题思路
算法
多路归并,小顶堆
思路
我们用小顶堆来维护存储最小值的链表头节点
先将所有的链表头节点 insert
(填充) 堆中
这边需要注意一点,我们维护的是小顶堆,填充的是链表节点,对比用的对比函数比较的是节点值
然后不断地 extract
(弹出)头节点,将他的合并到新链表(ret
)
在合并之后,我们需要看弹出的头节点 cur
是否存在 cur.next
,也就是还有没有后边继元素,如果有的话需要 insert
堆中,然后只要堆中还有元素,这个过程就要一直继续下去
最终返回新链表的头节点 ret.next
代码
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode[]} lists
* @return {ListNode}
*/
// 链表,堆,归并排序
function ListNode(val, next) {
this.val = val === undefined ? 0 : val
this.next = next === undefined ? null : next
}
const l1 = new ListNode(1, new ListNode(4, new ListNode(5)))
const l2 = new ListNode(1, new ListNode(3, new ListNode(4)))
const l3 = new ListNode(2, new ListNode(6))
var mergeKLists = function (lists) {
// 优先队列存储当前所有 k 个链表当前位置
const h = new Heap((a, b) => {
if (!b) return false
return a.val > b.val
})
// 将 k 个链表的 head 依次押入队列
for (x of lists) {
if (!x) continue
h.insert(x)
}
// 从 h 不断弹出节点,并且放到合并链表的末尾
let ret = new ListNode(),
p = ret
while (!h.isEmpty()) {
const cur = h.extract()
p.next = cur
p = p.next
// 如果存在 cur.next,即当前弹出的节点有 next 节点,我们需要把它放入 h
if (cur.next) {
h.insert(cur.next)
}
}
return ret.next
}
class Heap {
constructor(compareFn) {
this.compareFn = compareFn
this.heap = []
}
swap(parent, index) {
const arr = this.heap
;[arr[parent], arr[index]] = [arr[index], arr[parent]]
}
getLeftIndex(index) {
return index * 2 + 1
}
getRightIndex(index) {
return index * 2 + 2
}
getParentIndex(index) {
return Math.floor((index - 1) / 2)
}
size() {
return this.heap.length
}
isEmpty() {
return this.size() === 0
}
insert(value) {
const index = this.heap.length
this.heap.push(value)
this.siftUp(index)
}
siftUp(index) {
let parent = this.getParentIndex(index)
while (
index > 0 &&
this.compareFn(this.heap[parent], this.heap[index])
) {
this.swap(parent, index)
index = parent
parent = this.getParentIndex(index)
}
}
extract() {
if (this.isEmpty()) return
if (this.size() === 1) return this.heap.pop()
const removedItem = this.heap[0]
this.heap[0] = this.heap.pop()
this.siftDown(0)
return removedItem
}
siftDown(index) {
let element = index
const left = this.getLeftIndex(index)
const right = this.getRightIndex(index)
if (
index < this.size() &&
this.compareFn(this.heap[element], this.heap[left])
) {
element = left
}
if (
index < this.size() &&
this.compareFn(this.heap[element], this.heap[right])
) {
element = right
}
if (element !== index) {
this.swap(element, index)
this.siftDown(element)
}
}
top() {
return this.heap[0]
}
}