【每日算法】合并两个有序链表 LeetCode
方法设计思路
- 目标:将两个有序链表合并为一个新的有序链表。
- 核心思想:通过双指针遍历两个链表,每次选择较小的节点连接到新链表,直到其中一个链表遍历完毕,然后将剩余链表直接连接到新链表尾部。
- 关键点:
- 使用虚拟头节点 (
head
) 简化边界条件处理。 - 通过
cur
指针动态构建新链表。 - 直接利用原链表的节点,避免额外的空间开销。
- 使用虚拟头节点 (
实现细节
/*** Definition for singly-linked list.* public class ListNode {* public int val;* public ListNode next;* public ListNode(int val=0, ListNode next=null) {* this.val = val;* this.next = next;* }* }*/public ListNode MergeTwoLists(ListNode list1, ListNode list2)
{// 1. 创建虚拟头节点,简化初始为空的情况ListNode head = new ListNode();ListNode cur = head;// 2. 遍历两个链表,直到其中一个为空while (list1 != null && list2 != null){// 3. 比较当前节点的值,选择较小的节点连接到新链表if (list1.val < list2.val){cur.next = list1;list1 = list1.next; // 移动 list1 指针}else{cur.next = list2;list2 = list2.next; // 移动 list2 指针}cur = cur.next; // 移动新链表的指针}// 4. 将剩余的非空链表直接连接到新链表尾部cur.next = list1 == null ? list2 : list1;// 5. 返回合并后的链表(跳过虚拟头节点)return head.next;
}
步骤解析
初始化:
- 创建一个虚拟头节点
head
,其next
指针指向新链表的实际头节点。 cur
指针用于动态构建新链表。
- 创建一个虚拟头节点
遍历与比较:
- 比较
list1
和list2
当前节点的值,将较小的节点连接到cur.next
。 - 移动较小节点所在链表的指针(
list1
或list2
)。 - 移动
cur
指针到新链表的尾部。
- 比较
处理剩余节点:当其中一个链表遍历完毕时,直接将另一个链表的剩余部分连接到
cur.next
。返回结果:返回
head.next
,即新链表的实际头节点。
边界条件处理
一个链表为空:如果
list1
为空,直接返回list2
(反之亦然)。在代码中通过cur.next = list1 == null ? list2 : list1
实现。两个链表均为空:返回
head.next
(即null
),符合预期。虚拟头节点的作用:避免单独处理初始时
cur
为空的情况,简化代码逻辑。
复杂度分析
指标 | 值 | 说明 |
---|---|---|
时间复杂度 | O(n + m) | 需要遍历两个链表的所有节点,其中 n 和 m 分别为链表的长度。 |
空间复杂度 | O(1) | 仅使用常数级别的额外空间(虚拟头节点和指针变量)。 |
适用场景
有序链表的合并:适用于两个已经按升序排列的链表。可以扩展为合并多个有序链表(需调整逻辑)。
资源敏感场景:由于空间复杂度为 O(1),适合内存受限的环境。
稳定性要求:保持原有链表的相对顺序,适合需要稳定排序的场景。
方法优势
- 高效性:时间和空间复杂度均为最优。
- 简洁性:代码逻辑清晰,易于理解和维护。
- 通用性:可轻松扩展为其他类似问题(如合并多个链表)。