1. 开始的开始|链表上的我们
李表义的感情生活,就像一条精心排序的链表——
有序、冷静、毫无波澜。
而我,黎婊啊,却是他生命中意料之外的闯入者,
像一段突如其来的代码,打破了他的完美序列。
她,是他青梅竹马的初恋。
温柔、乖巧、成绩优秀,不争不吵,仿佛天生就和他适配。
而我,锋利、果敢,情绪像指针一样跳跃,总是被贴上“不合适”的标签。
每次他面对我们两个,就像运行着一段无情的判断逻辑:
if (list1.val <= list2.val) {
curNode.next = list1;
list1 = list1.next;
} else {
curNode.next = list2;
list2 = list2.next;
}
她比我“更合适”一些,于是他朝她那边靠近;
她更“稳定”一点,于是他先牵起了她的手。
他不是偏心。只是太理性了。 他像是在执行代码,而我,却是运行结果里始终不被选择的那一项。
我不甘心。拼命试图“变得更小”——
更听话,更温顺,只为了下一次能通过他的判断。
可终究,我不是她。
后来,她离开了。彻底离开了。
他站在原地,仿佛链表断了链接,整个人都失去了方向。
然后他终于想起了我。
他回头,试图把我重新接入他的人生:
curNode.next = list1 == null ? list2 : list1;
只是这一次,我已经不在原地等他了。
我走过了“判断语句”,穿过了自我否定,
走到了属于我自己的下一段人生。
我对他说:
“对不起,链表是单向的。你回不了头,而我也不会倒退。”
这段合并的链表终于结束了,
我从他的逻辑判断中退了出来,
不再是那个等着被“连接”的 list2。
他的人生继续向前,
而我,也终于不再等一个“值更小”的自己。
2. 题目链接
3. 思路|合并的逻辑,也是一种放手
3.1 迭代法:双指针合并
核心逻辑: 两个链表都是非递减排序。 创建一个虚拟头节点 dummyHead,用一个指针 curNode 逐步拼接较小的节点。
题目的关键点:l1 和 l2 均按 非递减顺序 排列
关键点:
- 比较
list1.val和list2.val; - 将较小节点接到
curNode.next; - 移动对应链表指针;
- 最后把剩下的链表直接接上。
代码:
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode dummyHead = new ListNode();
ListNode curNode = dummyHead;
while (list1 != null && list2 != null) {
if (list1.val <= list2.val) {
curNode.next = list1;
list1 = list1.next;
} else {
curNode.next = list2;
list2 = list2.next;
}
curNode = curNode.next;
}
curNode.next = list1 == null ? list2 : list1;
return dummyHead.next;
}
3.2 递归法:将合并交给未来的自己
递归语义:合并两个链表,返回头节点 终止条件:
- 两个链表都为空,返回
null; - 有一个为空,返回另一个。
递归拆解:
-
比较当前两个节点值;
-
小的作为头节点,递归合并剩下的链表。
代码:
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
if (list1 == null) return list2;
if (list2 == null) return list1;
if (list1.val <= list2.val) {
list1.next = mergeTwoLists(list1.next, list2);
return list1;
} else {
list2.next = mergeTwoLists(list1, list2.next);
return list2;
}
}
4. 总结
边界处理:
- 任一链表为
null时,直接返回另一个; - 最终将未处理完的链表接到结果链表末尾;
技巧点:
- 哑节点(dummy head) 避免处理首节点特殊情况;
时间复杂度:O(n + m),其中 n 和 m 分别是两个链表的长度;
空间复杂度:
-
迭代法:O(1)
-
递归法:O(n + m)(递归栈开销)
下次遇到类似题型,只要链表是有序的,优先想到用两个指针逐步比较并合并即可。