「这是我参与11月更文挑战的第 15 天,活动详情查看:2021最后一次更文挑战」
刷算法题,从来不是为了记题,而是练习把实际的问题抽象成具体的数据结构或算法模型,然后利用对应的数据结构或算法模型来进行解题。个人觉得,带着这种思维刷题,不仅能解决面试问题,也能更多的学会在日常工作中思考,如何将实际的场景抽象成相应的算法模型,从而提高代码的质量和性能
合并K个已排序的链表
题目描述
给你一个链表数组,每个链表都已经按升序排列
请你将所有链表合并到一个升序链表中,返回合并后的链表
示例
示例 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.length0 <= k <= 10^40 <= lists[i].length <= 50010^4 <= lists[i][j] <= 10^4lists[i]按 升序 排列lists[i].length的总和不超过10^4
解题
解法一:顺序合并
思路
合并k个有序链表,很容易让我们想到合并两个有序链表。合并两个有序链表很简单,但是我们肯定是希望在O(n)的时间复杂度和O(1)的空间复杂度下实现。这里先大致回顾一个两个有序链表的合并思路
合并两个有序链表
- 定义一个虚拟的头结点dummyHead,它不用于存储任何东西,主要是为了方便实现合并
- 然后定义一个prev指针,它指向待插入位置的前一个位置
- 最后定义currNode1和currNode2,分别指向两个链表的待插入节点
- 当currNode1和currNode2都不为空的时候,取两者中值最小的那个,插入到prev的后边。当currNode1和currNode2有一个为空的时候,将另一个链表的剩余部分全部插入到prev后边
知道了合并两个有序链表的思路,合并k个有序链表就很简单了,两个两个的合并
代码
//合并两个有序链表
func MergeTwoList(head1 *ListNode, head2 *ListNode) *ListNode {
if head1 == nil {
return head2
} else if head2 == nil {
return head1
}
dummyHead := &ListNode{}
prev := dummyHead
currNode1, currNode2 := head1, head2
for currNode1 != nil && currNode2 != nil {
if currNode1.Val <= currNode2.Val {
prev.Next = currNode1
currNode1 = currNode1.Next
} else {
prev.Next = currNode2
currNode2 = currNode2.Next
}
prev = prev.Next
}
if currNode1 == nil {
prev.Next = currNode2
}
if currNode2 == nil {
prev.Next = currNode1
}
return dummyHead.Next
}
//顺序合并 - 合并K个有序链表
func mergeKLists(lists []*ListNode) *ListNode {
baseList := &ListNode{math.MinInt32, nil}
for i := 0; i < len(lists); i++ {
baseList = MergeTwoList(baseList, lists[i])
}
return baseList.Next
}
解法二:分治思想
思路
前边接触到分治思想,就是归并排序算法中,它是将待排序的数字,先拆分到足够小的区间,然后再合并有序的区间
本题也可以利用相似的思想,将待合并的链表进行拆分,拆分到足够小之后,进行两两合并
//分治思想,实现k个有序链表的合并
func MergeKLists2(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) >> 2
return MergeTwoList(merge(lists, l, mid), merge(lists, mid+1, r))
}
//合并两个有序链表
func MergeTwoList(head1 *ListNode, head2 *ListNode) *ListNode {
if head1 == nil {
return head2
} else if head2 == nil {
return head1
}
dummyHead := &ListNode{}
prev := dummyHead
currNode1, currNode2 := head1, head2
for currNode1 != nil && currNode2 != nil {
if currNode1.Val < currNode2.Val {
prev.Next = currNode1
currNode1 = currNode1.Next
} else {
prev.Next = currNode2
currNode2 = currNode2.Next
}
prev = prev.Next
}
if currNode1 == nil {
prev.Next = currNode2
}
if currNode2 == nil {
prev.Next = currNode1
}
return dummyHead.Next
}