合并K个升序链表
- https://leetcode.cn/problems/merge-k-sorted-lists/
描述
- 给你一个链表数组,每个链表都已经按升序排列
- 请你将所有链表合并到一个升序链表中,返回合并后的链表
示例 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 <= 10410^4104
- 0 <= lists[i].length <= 500
- -10410^4104 <= lists[i][j] <= 10410^4104
- lists[i] 按 升序 排列
- lists[i].length 的总和不超过 10410^4104
Typescript 版算法实现
1 )方案1:使用堆
/**
* Definition for singly-linked list.
* class ListNode {
* val: number
* next: ListNode | null
* constructor(val?: number, next?: ListNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
* }
*/
class MinHeap {
heap: Array<ListNode | null> = [];
// 交换节点位置
swap(i, j) {
if (i === j) return;
[this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]];
}
// 获得父节点
getParentIndex(i) {
return (i - 1) >> 1;
}
// 获取左子节点
getLeftIndex(i) {
return (i << 1) + 1; // 极客写法
}
// 获取右子节点
getRightIndex(i) {
return (i << 1) + 2;
}
// 上移操作封装 是个递归
shiftUp(i) {
// 如果到了堆顶元素,index是0,则不要再上移了
if(!i) return;
// 获得父节点下标: pi
const pi = this.getParentIndex(i);
// 开始做比较
if(this.heap[pi]?.val > this.heap[i].val) {
// 实现交换
this.swap(pi, i);
// 继续尝试上移操作
this.shiftUp(pi);
}
}
// 下移操作
shiftDown(i) {
// 如果到了堆尾元素,则不要再下移了
if(i >= this.heap.length - 1) return;
const li = this.getLeftIndex(i); // 左孩子索引
const ri = this.getRightIndex(i); // 右孩子索引
// 左孩子节点的值 < 当前节点的值 注意:左右对比中,this.heap[i]不能用变量替代,因为内部有swap和shiftDown,使用变量会导致bug
if(this.heap[li]?.val < this.heap[i].val) {
this.swap(li, i);
this.shiftDown(li);
}
// 同样,对右孩子节点的值 < 当前节点的值
if(this.heap[ri]?.val < this.heap[i].val) {
this.swap(ri, i);
this.shiftDown(ri);
}
}
// 插入
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];
// 变相删除堆顶, 堆尾元素移动到堆顶
this.heap[0] = this.heap.pop(); // 删除数组的最后一个元素并返回,返回值赋值给堆顶元素
// 执行堆顶下移操作,维持堆的有序性
this.shiftDown(0);
return top;
}
// 获取堆顶
peak() {
return this.heap[0];
}
// 获取堆的大小
size() {
return this.heap.length;
}
}
function mergeKLists(lists: Array<ListNode | null>): ListNode | null {
const res = new ListNode(0);
let p = res;
const h = new MinHeap();
// 通过输入来构建堆结构
// 三个链表,堆中存入的是三个链表的头
lists.forEach(l => {
l && h.insert(l); // l是个链表,其实用的是链表头表示,也就是链表的第一个元素
})
// while循环,每一轮pk的都是堆内的元素
// 谁出队,就把谁的下一个入堆,逐个比较
// 最终堆空了,比较全部结束
while(h.size()) {
const n = h.pop(); // 弹出堆顶元素,最小值
p.next = n; // 将堆顶元素挂载在新链表上
p = p.next; // 链表指针移位,为下次连接做准备
n.next && h.insert(n.next) // 将弹出的堆顶元素的下一个元素入堆,进行重新构建堆结构
}
return res.next; // 返回的是next, 因为其第一个节点是我们new出来,用于连接的,所以不包含第一个节点
}
- 时间复杂度 O(nlogk)
- forEach O(k), k是链表数量
- while循环遍历了所有链表的所有节点 O(n),n是所有链表节点之和
- 并且堆操作是O(logk), 两者结合:O(nlogk)
- 整体:O(nlogk)
- 空间复杂度 O(k)
- 就是堆的大小
- 堆能高效、快速找出最大值和最小值,时间复杂度O(1),堆顶是最大值或最小值
- 找出第K个最大(小)元素
- 构建一个容量为k的堆,让每个元素都插入这个堆
- 保持容量始终为k, 最终堆顶就是最大元素或最小元素
- 这个解法不算精妙,但是是两个数据结构(堆和链表)融合解题的典型
2 )方案2: 使用优先队列(堆)合并,优化方案1
/**
* Definition for singly-linked list.
* class ListNode {
* val: number
* next: ListNode | null
* constructor(val?: number, next?: ListNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
* }
*/
class Status {
val: number;
ptr: ListNode;
constructor(val: number, ptr: ListNode) {
this.val = val;
this.ptr = ptr;
}
}
class PQ<T> {
private heap: T[];
constructor() {
this.heap = [];
}
get length(): number {
return this.heap.length;
}
private compare(a: T, b: T): number {
if (a instanceof Status && b instanceof Status) {
return a.val - b.val;
}
throw new Error('Invalid comparison types');
}
push(item: T): void {
this.heap.push(item);
this._siftUp(this.heap.length - 1);
}
pop(): T | undefined {
if (this.heap.length === 0) return undefined;
const lastElement = this.heap.pop();
if (this.heap.length === 0) return lastElement;
const returnItem = this.heap[0];
this.heap[0] = lastElement!;
this._siftDown(0);
return returnItem;
}
private _siftUp(index: number): void {
while (index > 0) {
const parentIndex = Math.floor((index - 1) / 2);
if (this.compare(this.heap[parentIndex], this.heap[index]) > 0) {
[this.heap[parentIndex], this.heap[index]] = [this.heap[index], this.heap[parentIndex]];
index = parentIndex;
} else {
break;
}
}
}
private _siftDown(index: number): void {
const size = this.heap.length;
while (true) {
let smallest = index;
const leftChild = 2 * index + 1;
const rightChild = 2 * index + 2;
if (leftChild < size && this.compare(this.heap[leftChild], this.heap[smallest]) < 0) {
smallest = leftChild;
}
if (rightChild < size && this.compare(this.heap[rightChild], this.heap[smallest]) < 0) {
smallest = rightChild;
}
if (smallest !== index) {
[this.heap[index], this.heap[smallest]] = [this.heap[smallest], this.heap[index]];
index = smallest;
} else {
break;
}
}
}
}
function mergeKLists(lists: Array<ListNode | null>): ListNode | null {
const queue = new PQ<Status>();
for (const node of lists) {
if (node !== null) {
queue.push(new Status(node.val, node));
}
}
const head = new ListNode(0);
let tail = head;
while (queue.length > 0) {
const f = queue.pop();
if (f) {
tail.next = f.ptr;
tail = tail.next;
if (f.ptr.next !== null) {
queue.push(new Status(f.ptr.next.val, f.ptr.next));
}
}
}
return head.next;
}
3 )方案3: 顺序合并
/**
* Definition for singly-linked list.
* class ListNode {
* val: number
* next: ListNode | null
* constructor(val?: number, next?: ListNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
* }
*/
function mergeKLists(lists: Array<ListNode | null>): ListNode | null {
let ans: ListNode | null = null;
for (let i = 0; i < lists.length; ++i) {
ans = mergeTwoLists(ans, lists[i]);
}
return ans;
}
function mergeTwoLists(a: ListNode | null, b: ListNode | null): ListNode | null {
if (a === null || b === null) {
return a !== null ? a : b;
}
const head = new ListNode(0);
let tail = head;
let aPtr = a;
let bPtr = b;
while (aPtr !== null && bPtr !== null) {
if (aPtr.val < bPtr.val) {
tail.next = aPtr;
aPtr = aPtr.next;
} else {
tail.next = bPtr;
bPtr = bPtr.next;
}
tail = tail.next;
}
tail.next = aPtr !== null ? aPtr : bPtr;
return head.next;
}
4 )方案4: 分治合并
/**
* Definition for singly-linked list.
* class ListNode {
* val: number
* next: ListNode | null
* constructor(val?: number, next?: ListNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
* }
*/
function mergeKLists(lists: Array<ListNode | null>): ListNode | null {
return merge(lists, 0, lists.length - 1);
}
function merge(lists: Array<ListNode | null>, l: number, r: number): ListNode | null {
if (l == r) {
return lists[l];
}
if (l > r) {
return null;
}
const mid = (l + r) >> 1;
return mergeTwoLists(merge(lists, l, mid), merge(lists, mid + 1, r));
}
function mergeTwoLists(a: ListNode | null, b: ListNode | null): ListNode | null {
if (a === null || b === null) {
return a !== null ? a : b;
}
const head = new ListNode(0);
let tail = head;
let aPtr = a;
let bPtr = b;
while (aPtr !== null && bPtr !== null) {
if (aPtr.val < bPtr.val) {
tail.next = aPtr;
aPtr = aPtr.next;
} else {
tail.next = bPtr;
bPtr = bPtr.next;
}
tail = tail.next;
}
tail.next = aPtr !== null ? aPtr : bPtr;
return head.next;
}
Python3 版算法实现
1 ) 方案1:使用堆
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class MinHeap:
def __init__(self):
self.heap = []
def swap(self, i, j):
self.heap[i], self.heap[j] = self.heap[j], self.heap[i]
def get_parent_index(self, i):
return (i - 1) // 2
def get_left_index(self, i):
return (i << 1) + 1
def get_right_index(self, i):
return (i << 1) + 2
def shift_up(self, i):
if i == 0:
return
pi = self.get_parent_index(i)
if self.heap[pi].val > self.heap[i].val:
self.swap(pi, i)
self.shift_up(pi)
def shift_down(self, i):
if i >= len(self.heap) - 1:
return
li = self.get_left_index(i)
ri = self.get_right_index(i)
if li < len(self.heap) and self.heap[li].val < self.heap[i].val:
self.swap(li, i)
self.shift_down(li)
if ri < len(self.heap) and self.heap[ri].val < self.heap[i].val:
self.swap(ri, i)
self.shift_down(ri)
def insert(self, value):
self.heap.append(value)
self.shift_up(len(self.heap) - 1)
def pop(self):
if len(self.heap) == 1:
return self.heap.pop()
top = self.heap[0]
self.heap[0] = self.heap.pop()
self.shift_down(0)
return top
def peak(self):
return self.heap[0]
def size(self):
return len(self.heap)
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
res = ListNode(0)
p = res
h = MinHeap()
for l in lists:
if l:
h.insert(l)
while h.size() > 0:
n = h.pop()
p.next = n
p = p.next
if n.next:
h.insert(n.next)
return res.next
2 )方案2:使用优先队列(堆)合并,优化方案1
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Status:
def __init__(self, val, ptr):
self.val = val
self.ptr = ptr
def __lt__(self, other):
return self.val < other.val
class PriorityQueue:
def __init__(self):
self.heap = []
def push(self, item):
self.heap.append(item)
self._sift_up(len(self.heap) - 1)
def pop(self):
if not self.heap:
raise IndexError("pop from an empty priority queue")
last_element = self.heap.pop()
if self.heap:
return_item = self.heap[0]
self.heap[0] = last_element
self._sift_down(0)
return return_item
return last_element
def _sift_up(self, index):
while index > 0:
parent_index = (index - 1) // 2
if self.heap[parent_index] > self.heap[index]:
self.heap[parent_index], self.heap[index] = self.heap[index], self.heap[parent_index]
index = parent_index
else:
break
def _sift_down(self, index):
size = len(self.heap)
while True:
smallest = index
left_child = 2 * index + 1
right_child = 2 * index + 2
if left_child < size and self.heap[left_child] < self.heap[smallest]:
smallest = left_child
if right_child < size and self.heap[right_child] < self.heap[smallest]:
smallest = right_child
if smallest != index:
self.heap[index], self.heap[smallest] = self.heap[smallest], self.heap[index]
index = smallest
else:
break
def is_empty(self):
return len(self.heap) == 0
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
queue = PriorityQueue()
for node in lists:
if node:
queue.push(Status(node.val, node))
head = ListNode(0)
tail = head
while not queue.is_empty():
f = queue.pop()
tail.next = f.ptr
tail = tail.next
if f.ptr.next:
queue.push(Status(f.ptr.next.val, f.ptr.next))
return head.next
3 )方案3:顺序合并
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
ans = None
for l in lists:
ans = self.mergeTwoLists(ans, l)
return ans
def mergeTwoLists(self, a, b):
if a is None or b is None:
return a if a is not None else b
head = ListNode(0)
tail = head
aPtr, bPtr = a, b
while aPtr is not None and bPtr is not None:
if aPtr.val < bPtr.val:
tail.next = aPtr
aPtr = aPtr.next
else:
tail.next = bPtr
bPtr = bPtr.next
tail = tail.next
tail.next = aPtr if aPtr is not None else bPtr
return head.next
4 )方案4:分治合并
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
return self.merge(lists, 0, len(lists) - 1)
def merge(self, lists, l, r):
if l == r:
return lists[l]
if l > r:
return None
mid = (l + r) // 2
return self.mergeTwoLists(self.merge(lists, l, mid), self.merge(lists, mid + 1, r))
def mergeTwoLists(self, a, b):
if a is None or b is None:
return a if a is not None else b
head = ListNode(0)
tail = head
aPtr, bPtr = a, b
while aPtr is not None and bPtr is not None:
if aPtr.val < bPtr.val:
tail.next = aPtr
aPtr = aPtr.next
else:
tail.next = bPtr
bPtr = bPtr.next
tail = tail.next
tail.next = aPtr if aPtr is not None else bPtr
return head.next
Golang 版算法实现
1 ) 方案1:使用堆
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
// MinHeap 定义最小堆
type MinHeap struct {
heap []*ListNode
}
// swap 交换堆中两个元素的位置
func (h *MinHeap) swap(i, j int) {
h.heap[i], h.heap[j] = h.heap[j], h.heap[i]
}
// getParentIndex 获取父节点的索引
func (h *MinHeap) getParentIndex(i int) int {
return (i - 1) / 2
}
// getLeftIndex 获取左子节点的索引
func (h *MinHeap) getLeftIndex(i int) int {
return 2*i + 1
}
// getRightIndex 获取右子节点的索引
func (h *MinHeap) getRightIndex(i int) int {
return 2*i + 2
}
// shiftUp 向上调整堆
func (h *MinHeap) shiftUp(i int) {
if i == 0 {
return
}
pi := h.getParentIndex(i)
if h.heap[pi].Val > h.heap[i].Val {
h.swap(pi, i)
h.shiftUp(pi)
}
}
// shiftDown 向下调整堆
func (h *MinHeap) shiftDown(i int) {
size := len(h.heap)
if i >= size-1 {
return
}
li := h.getLeftIndex(i)
ri := h.getRightIndex(i)
minIndex := i
if li < size && h.heap[li].Val < h.heap[minIndex].Val {
minIndex = li
}
if ri < size && h.heap[ri].Val < h.heap[minIndex].Val {
minIndex = ri
}
if minIndex != i {
h.swap(minIndex, i)
h.shiftDown(minIndex)
}
}
// Insert 插入新元素到堆中
func (h *MinHeap) Insert(value *ListNode) {
h.heap = append(h.heap, value)
h.shiftUp(len(h.heap) - 1)
}
// Pop 弹出堆顶元素
func (h *MinHeap) Pop() *ListNode {
if len(h.heap) == 1 {
popped := h.heap[0]
h.heap = h.heap[:0]
return popped
}
top := h.heap[0]
h.heap[0] = h.heap[len(h.heap)-1]
h.heap = h.heap[:len(h.heap)-1]
h.shiftDown(0)
return top
}
// Peek 获取堆顶元素但不弹出
func (h *MinHeap) Peek() *ListNode {
if len(h.heap) == 0 {
return nil
}
return h.heap[0]
}
// Size 获取堆的大小
func (h *MinHeap) Size() int {
return len(h.heap)
}
// mergeKLists 合并 K 个升序链表
func mergeKLists(lists []*ListNode) *ListNode {
res := &ListNode{Val: 0}
p := res
h := &MinHeap{}
// 将所有链表的头节点插入堆中
for _, l := range lists {
if l != nil {
h.Insert(l)
}
}
// 从堆中弹出最小元素,并将其下一个节点(如果存在)插入堆中
for h.Size() > 0 {
n := h.Pop()
p.Next = n
p = p.Next
if n.Next != nil {
h.Insert(n.Next)
}
}
return res.Next
}
2 )方案2:使用优先队列(堆)合并,优化方案1
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
// Status represents the status of a node in the priority queue.
type Status struct {
val int
ptr *ListNode
}
// PriorityQueue is a min-heap of Status.
type PriorityQueue []*Status
func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool { return pq[i].val < pq[j].val }
func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] }
func (pq *PriorityQueue) Push(x interface{}) {
*pq = append(*pq, x.(*Status))
}
func (pq *PriorityQueue) Pop() interface{} {
old := *pq
n := len(old)
item := old[n-1]
*pq = old[0 : n-1]
return item
}
func (pq *PriorityQueue) Fix(i int) {
n := pq.Len()
for {
j := i
// Check left child
if l := 2*i + 1; l < n && pq.Less(l, j) {
j = l
}
// Check right child
if r := 2*i + 2; r < n && pq.Less(r, j) {
j = r
}
if j == i {
break
}
pq.Swap(i, j)
i = j
}
}
func (pq *PriorityQueue) Up(i int) {
for {
parent := (i - 1) / 2
if parent == i || !pq.Less(i, parent) {
break
}
pq.Swap(i, parent)
i = parent
}
}
func (pq *PriorityQueue) PushFix(x interface{}) {
*pq = append(*pq, x.(*Status))
n := pq.Len()
pq.Up(n - 1)
}
func (pq *PriorityQueue) PopFix() interface{} {
old := *pq
n := len(old)
item := old[0]
old[0] = old[n-1]
*pq = old[0 : n-1]
pq.Fix(0)
return item
}
func mergeKLists(lists []*ListNode) *ListNode {
queue := &PriorityQueue{}
for _, node := range lists {
if node != nil {
queue.PushFix(&Status{val: node.Val, ptr: node})
}
}
head := &ListNode{Val: 0}
tail := head
for queue.Len() > 0 {
f := queue.PopFix().(*Status)
tail.Next = f.ptr
tail = tail.Next
if f.ptr.Next != nil {
queue.PushFix(&Status{val: f.ptr.Next.Val, ptr: f.ptr.Next})
}
}
return head.Next
}
3 ) 方案3:顺序合并
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func mergeKLists(lists []*ListNode) *ListNode {
ans := (*ListNode)(nil)
for _, l := range lists {
ans = mergeTwoLists(ans, l)
}
return ans
}
func mergeTwoLists(a, b *ListNode) *ListNode {
if a == nil || b == nil {
if a != nil {
return a
}
return b
}
head := &ListNode{Val: 0}
tail := head
aPtr, bPtr := a, b
for aPtr != nil && bPtr != nil {
if aPtr.Val < bPtr.Val {
tail.Next = aPtr
aPtr = aPtr.Next
} else {
tail.Next = bPtr
bPtr = bPtr.Next
}
tail = tail.Next
}
if aPtr != nil {
tail.Next = aPtr
} else {
tail.Next = bPtr
}
return head.Next
}
4 ) 方案4:分治合并
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func mergeKLists(lists []*ListNode) *ListNode {
return merge(lists, 0, len(lists)-1)
}
func merge(lists []*ListNode, l, r int) *ListNode {
if l == r {
return lists[l]
}
if l > r {
return nil
}
mid := (l + r) >> 1
return mergeTwoLists(merge(lists, l, mid), merge(lists, mid+1, r))
}
func mergeTwoLists(a, b *ListNode) *ListNode {
if a == nil || b == nil {
if a != nil {
return a
}
return b
}
head := &ListNode{Val: 0}
tail := head
aPtr, bPtr := a, b
for aPtr != nil && bPtr != nil {
if aPtr.Val < bPtr.Val {
tail.Next = aPtr
aPtr = aPtr.Next
} else {
tail.Next = bPtr
bPtr = bPtr.Next
}
tail = tail.Next
}
if aPtr != nil {
tail.Next = aPtr
} else {
tail.Next = bPtr
}
return head.Next
}
7948



被折叠的 条评论
为什么被折叠?



