内容介绍
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 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
完整代码
public ListNode mergeTwoLists(ListNode a, ListNode b) {
if (a == null || b == null) {
return a != null ? a : b;
}
ListNode head = new ListNode(0);
ListNode tail = head, aPtr = a, 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;
}
思路详解
-
边界条件处理:
- 首先检查链表
a
和b
是否为空。如果其中一个为空,则直接返回另一个链表。这是因为空链表与任何链表合并后,结果都是非空链表本身。
- 首先检查链表
-
初始化:
- 创建一个虚拟头节点
head
,其值为 0。这个虚拟头节点的作用是简化合并过程中的边界处理,使得合并后的链表可以方便地返回。 - 初始化三个指针:
tail
指向虚拟头节点,aPtr
指向链表a
的头部,bPtr
指向链表b
的头部。
- 创建一个虚拟头节点
-
合并过程:
- 使用
while
循环,当aPtr
和bPtr
均不为空时,进入循环。 - 在循环内部,比较
aPtr
和bPtr
所指向的节点值:- 如果
aPtr.val < bPtr.val
,则将tail.next
指向aPtr
,并将aPtr
移动到下一个节点。 - 否则,将
tail.next
指向bPtr
,并将bPtr
移动到下一个节点。
- 如果
- 每次比较后,将
tail
指向新的尾节点。
- 使用
-
处理剩余节点:
- 当
while
循环结束时,可能有一个链表已经遍历完毕,而另一个链表还有剩余节点。此时,直接将tail.next
指向未遍历完的链表(aPtr
或bPtr
)。
- 当
-
返回结果:
- 由于
head
是虚拟头节点,合并后的链表实际从head.next
开始。因此,返回head.next
作为合并后的链表。
- 由于
知识点精炼
-
链表定义:
- 链表是由节点(Node)组成的线性结构,每个节点包含数据域(val)和指向下一个节点的指针(next)。
-
有序链表:
- 有序链表是指链表中的节点按照一定的顺序排列,如升序或降序。
-
合并思路:
- 初始化一个虚拟头节点,简化边界处理。
- 比较两个链表的当前节点值,将较小的节点接入结果链表。
- 更新指针,继续比较,直至一个链表为空。
- 将非空链表的剩余部分直接接入结果链表。
-
边界条件:
- 处理链表为空的情况,直接返回非空链表或空链表。
-
时间复杂度:
- 合并过程的时间复杂度为 O(n),其中 n 为两个链表长度之和。
-
空间复杂度:
- 合并过程中,除了返回的新链表外,只使用了常数级别的额外空间,空间复杂度为 O(1)。
-
稳定性:
- 合并后的链表保持原有链表的有序性。