010 【入门】链表入门题目-合并两个有序链表

合并两个有序链表 | [算法]-[中级]-[链表]

▶ JDK8+ | ⏱️ O(m+n)

核心代码实现

package class010;

// 将两个升序链表合并为一个新的升序链表并返回
// 新链表是通过拼接给定的两个链表的所有节点组成的
// 测试链接 : https://leetcode.cn/problems/merge-two-sorted-lists/
public class MergeTwoLists {

    // 链表节点定义
    public static class ListNode {
        public int val;
        public ListNode next;

        public ListNode(int val) {
            this.val = val;
        }

        public ListNode(int val, ListNode next) {
            this.val = val;
            this.next = next;
        }
    }

    class Solution {

        /**
         * 合并两个有序链表的迭代解法
         * 时间复杂度: O(m+n),其中m和n分别是两个链表的长度
         * 空间复杂度: O(1),只需要常数级别的指针空间
         * 
         * @param head1 第一个链表的头节点
         * @param head2 第二个链表的头节点
         * @return 合并后链表的头节点
         */
        public static ListNode mergeTwoLists(ListNode head1, ListNode head2) {
            // 边界条件:如果任一链表为空,直接返回另一个链表
            if (head1 == null || head2 == null) {
                return head1 == null ? head2 : head1;
            }
            
            // 选择头节点值较小的链表作为新链表的头
            ListNode head = head1.val <= head2.val ? head1 : head2;
            // cur1指向已选择头部链表的下一个节点
            ListNode cur1 = head.next;
            // cur2指向另一个链表的头节点
            ListNode cur2 = head == head1 ? head2 : head1;
            // pre指针用于追踪新链表的当前末尾节点
            ListNode pre = head;
            
            // 遍历两个链表,直到其中一个到达末尾
            while (cur1 != null && cur2 != null) {
                if (cur1.val <= cur2.val) {
                    pre.next = cur1; // 连接cur1节点
                    cur1 = cur1.next; // 移动cur1指针
                } else {
                    pre.next = cur2; // 连接cur2节点
                    cur2 = cur2.next; // 移动cur2指针
                }
                pre = pre.next; // 移动pre指针到新末尾
            }
            
            // 将剩余的未处理节点连接到新链表末尾
            pre.next = cur1 != null ? cur1 : cur2;
            
            return head;
        }

    }
}

关联知识图谱

1. 算法思路可视化
比较两链表头节点值
初始化三指针
选择较小值节点
一个链表到达末尾
返回合并后链表
边界检查
选择头节点
迭代合并
连接剩余
2. 三指针策略图解
pre  → [ ] → [ ] → ...
       ↑
      head

cur1 → [ ] → [ ] → ...

cur2 → [ ] → [ ] → ...
3. 递归解法对比
// [递归解法] 更简洁但空间复杂度为O(m+n)
public ListNode mergeTwoListsRecursive(ListNode l1, ListNode l2) {
    // 基本情况:如果一个链表为空,返回另一个链表
    if (l1 == null) return l2;
    if (l2 == null) return l1;
    
    // 递归合并:选择较小的头节点,并递归合并剩余部分
    if (l1.val < l2.val) {
        l1.next = mergeTwoListsRecursive(l1.next, l2);
        return l1;
    } else {
        l2.next = mergeTwoListsRecursive(l1, l2.next);
        return l2;
    }
}

行动指南

1. 优化与变体
  • [优化] 使用哨兵节点简化代码:
// [推荐] 使用哨兵节点简化边界处理
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    ListNode dummy = new ListNode(-1); // 哨兵节点
    ListNode current = dummy;
    
    while (l1 != null && l2 != null) {
        if (l1.val <= l2.val) {
            current.next = l1;
            l1 = l1.next;
        } else {
            current.next = l2;
            l2 = l2.next;
        }
        current = current.next;
    }
    
    // 连接剩余部分
    current.next = l1 != null ? l1 : l2;
    
    return dummy.next;
}
2. 安全与性能考量
  • [警告] 在多线程环境中操作链表需要额外同步机制
  • [注意] 对于超长链表,递归解法可能导致栈溢出,迭代解法更安全
  • [技巧] 使用哨兵节点可以简化代码,但会增加少量空间开销
3. 相关题目与应用

相关题目:

  • [LeetCode 23] 合并K个升序链表
  • [LeetCode 148] 排序链表
  • [面试经典] 链表归并排序实现

效率工具:

  • 调试技巧: 使用条件断点检查链表循环引用 node == node.next

▶ 扩展问题:如何在O(1)空间复杂度下合并K个有序链表?

你可能感兴趣的:(算法专栏,链表,数据结构)