将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
注: 本文章使用的代码是Java
LeetCode题链接:合并两个有序链表
官方示例图:

java
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
}
题解
本题给了两个升序链表,list1和list2。需要我们将题目串起来
画图
那么我们就自己假设出链表模拟模拟。我现在随便画出2个升序链表

模拟过程
我们先不写代码,模拟模拟把两个链表串起来需要怎么做。 很显然,我们会判断 1和2哪个大哪个小,然后发现1 比 2小。所以我们的目光就聚集在1上。
1要往下一个指,随后我们就比较 2和8哪个大哪个小。发现2比8小。那么1指向2所以变成了:
好,现在我们的目光聚集在了2上,我们的2要指向下一个节点。所以我们比较 5 和 8 的大小,发现 5小于8。那么2指向5,所以又变成了:
我们的目光聚集到了5身上,5要指向下一个,那么比较8和7,发现7比8小。那么5指向7,所以又变成了:
现在目光聚集在7身上,7要指向下一个,8和9比较,8比9小,7指向8。变成:
现在目光聚集在8身上,8要指向下一个,24和9比较,9比24小,8指向9。变成:
目光聚集在9上,9要指向下一个,24和10比较,10比较小,9指向10。变成:
目光聚集在10,10要指向下一个,12和24比较,12小,10指向12.变成:
到这里目光聚集在12上,然后list2已经遍历完了,所以我们会让12指向24即可,完成本题。变成:
最后上面这个链表就是我们的答案。
分析总结
我们回过头看看刚才的模拟过程,我们做了什么?提取关键字
- 目光聚集
- 比大小
- 指向
然后就是循环循环.......
最后有一个链表 如:list2遍历完了,我们就指向list的24节点即可。
我们尝试转换成代码逻辑。
转成代码
我们可以先弄出主要的过程,再回过头注意细节。根据刚才的过程:
- 目光聚集
- 比大小
- 指向
- 目光重新聚集
- 循环
目光聚集
我们一开始需要把目光放到一个节点上,然后这个节点是怎么出来的呢,不妨回过头看看刚才的模拟过程。 一开始比较两个链表的头节点,谁小目光就在谁身上。
这时产生了代码
目光 = 值小的
即:
ListNode sight = list1.val >= list2.val ? list2 : list1;
比大小和指向
目光聚集锁定后,就要比较大小了,比完就指向下一个。
比较大小: 谁和谁比,比什么。目光聚集在1的时候,是2和8比,比较这两个节点的val

而且是不同目光聚集,比的节点也不一样。于是这需要变量代表这两个比较的节点。而且这两个节点,一个在list1中,一个在list2中。那么有
一个节点 = 目光聚集.next
另一个节点 = 刚才没有被目光聚集那个节点
Java
ListNode cur1 = sight.next;
ListNode cur2 = sight == list1 ? list2 : list1;
并且在慢慢遍历链表。于是代码变成: 指向 = list1中节点 和 list2中节点 值小的那个
指向执行完之后,被指的那个节点需要向下移动一个(慢慢遍历)。 即:
Java
if(cur1.val <= cur2.val){
sight.next = cur1;
cur1 = cur1.next;
} else{
sight.next = cur2;
cur2 = cur2.next;
}
sight = sight.next; // 重新聚集目光
循环和打破循环后
回到刚才模拟过程,打破循环的是什么情况呢?对,就是list2遍历完了,他下面没有了。于是就打破循环了。
也就是 list2 == null
那不一定是每次都是list2先遍历完(只不过我们的例子是list2先遍历完),下次也有可能是list1。所以应该是 list1 或者list2 遍历完,没有了就打破循环。
代码: while( list1 !=null || list2 != null)
循环打破之后还没完,我们干了什么事情呢?
当时我们把12(目光聚集sight)的下一个(next)直接指向了24(比较大小的节点cur1)。
发现:list2遍历完了就是 指向cur1。 因为我们这个例子是list2先遍历完。 所以如果是 list1先遍历完 那指向cur2,list2先遍历完指向cur1.
代码:sight.next = cur1 == null ? cur2 : cur1;
返回值
最后我们是需要返回一个链表的。这个链表我们不妨叫 ans。 那么最后返回肯定是 return ans;
我们返回的就是这个 值为1的Node是吧。因为1小,所以我们把1当成链表头。 那我们有
ans = list1.val >= list2.val ? list2 : list1
;
最终代码
我们按照声明变量,代码逻辑,判断循环即可生成最终代码。
Java
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if(list1 == null || list2 == null){
return list1 == null ? list2 : list1;
}
ListNode ans = list1.val >= list2.val ? list2 : list1;
ListNode cur1 = ans.next;
ListNode cur2 = ans == list1 ? list2 :list1;
ListNode pre = ans;
while(cur1 != null && cur2 != null){
if(cur1.val >= cur2.val){
pre.next = cur2;
cur2 = cur2.next;
}else{
pre.next = cur1;
cur1 = cur1.next;
}
pre = pre.next;
}
pre.next = cur1 == null ? cur2 : cur1;
return ans;
}
时空复杂度
时间复杂度:O(m + n)
其中m和n分别是两个链表的长度。这是因为在最坏的情况下,我们需要遍历两个链表的所有节点。每次循环,我们都会将一个节点从输入链表移动到输出链表,直到两个输入链表都为空。因此,总的操作次数是m + n,所以时间复杂度是O(m + n)。
空间复杂度:O(1)
因为它只使用了几个指针,并没有使用额外的空间来存储数据。