LeetCode - 合并两个有序链表 / 删除链表的倒数第 N 个结点

合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:

复制代码
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

复制代码
输入:l1 = [], l2 = []
输出:[]

示例 3:

复制代码
输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]

  • -100 <= Node.val <= 100

  • l1l2 均按 非递减顺序 排列

解题思路

这段代码实现了合并两个有序链表的功能,采用递归方式实现。

  1. 创建虚拟头节点

    复制代码
    ListNode head = new ListNode(-1);  // 创建一个值为-1的虚拟头节点
  2. 递归合并过程

    • 基线条件:当两个链表都为空时,递归结束

      复制代码
      if (list1 == null && list2 == null) {
          return head;
      }
    • 比较并连接节点

      • 当两个链表都不为空时,比较节点值,取较小者

        复制代码
        if (list1.val > list2.val) {
            head.next = new ListNode(list2.val);  // 创建新节点
            list2 = list2.next;  // 移动指针
        } else {
            head.next = new ListNode(list1.val);
            list1 = list1.next;
        }
      • 当一个链表为空时,直接连接另一个链表的节点

        复制代码
        else if (list1 == null) {
            head.next = new ListNode(list2.val);
            list2 = list2.next;
        } else if (list2 == null) {
            head.next = new ListNode(list1.val);
            list1 = list1.next;
        }
  3. 递归调用

    复制代码
    return doMergeTwoLists(head.next, list1, list2);  // 处理下一个节点
  4. 返回结果

    复制代码
    return head.next;  // 跳过虚拟头节点,返回真正的头节点

    class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
    ListNode head = new ListNode(-1);
    doMergeTwoLists( head, list1, list2);
    return head.next;
    }

    复制代码
     public ListNode doMergeTwoLists(ListNode head, ListNode list1, ListNode list2) {
         if (list1 == null && list2 == null) {
             return head;
         }
    
         if (list1 != null && list2 != null) {
             if (list1.val > list2.val) {
                 head.next = new ListNode(list2.val);
                 list2 = list2.next;
             } else {
                 head.next = new ListNode(list1.val);
                 list1 = list1.next;
             }
         }else if (list1 == null) {
             head.next = new ListNode(list2.val);
             list2 = list2.next;
         }else if (list2 == null) {
             head.next = new ListNode(list1.val);
             list1 = list1.next;
         }
    
         return doMergeTwoLists( head.next,  list1,  list2);
    
     }

    }

删除链表的倒数第 N 个结点

给你一个链表,删除链表的倒数第 n个结点,并且返回链表的头结点。

示例 1:

复制代码
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

复制代码
输入:head = [1], n = 1
输出:[]

示例 3:

复制代码
输入:head = [1,2], n = 1
输出:[1]

提示:

  • 链表中结点的数目为 sz

  • 1 <= sz <= 30

  • 0 <= Node.val <= 100

  • 1 <= n <= sz

栈(Stack)数据结构

解题思路

使用栈(Stack)数据结构来解决"删除链表倒数第N个节点"的问题。栈是一种后进先出(LIFO)的数据结构,可以用来反转链表的顺序,通过将链表节点全部压入栈中,然后弹出N个节点,找到要删除的节点及其前驱节点

  1. 第一次遍历:将链表所有节点依次压入栈

    • 例如链表 1->2->3->4->5,n=2

    • 栈中内容(从底到顶):[1, 2, 3, 4, 5]

  2. 弹出阶段

    • 第一次弹出:5 (n=2 → n=1)

    • 第二次弹出:4 (n=1 → n=0) → 找到要删除的节点4

    • 查看栈是否为空:栈顶现在是3

    • 弹出3作为前驱节点,将3的next指向4的next(即5)

    • 结果链表:1->2->3->5

  3. 特殊情况处理

    • 如果要删除的是头节点(如n=链表长度),栈弹出后为空

    • 直接返回头节点的next作为新头节点

    public ListNode removeNthFromEnd(ListNode head, int n) {
    // 1. 创建栈并遍历链表,将所有节点压入栈中
    Stack<ListNode> stack = new Stack();
    ListNode node = head;
    while (node != null) {
    stack.push(node);
    node = node.next;
    }

    复制代码
     // 2. 弹出栈顶元素,直到找到倒数第N个节点
     while (n > 0) {
         ListNode temp = stack.pop();
         if (--n == 0) {  // 当n减到0时,temp就是我们要删除的节点
             ListNode next = temp.next;
             
             // 3. 处理删除操作
             if (stack.isEmpty()) {
                 // 如果栈为空,说明要删除的是头节点
                 return next;
             } else {
                 // 否则获取前驱节点并修改其next指针
                 ListNode pre = stack.pop();
                 pre.next = next;
             }
             break;
         }
     }
     return head;

    }

快慢指针

解题思路

这个解法使用了快慢双指针技巧来高效地删除链表倒数第N个节点,无需使用额外空间(栈解法需要O(n)空间)。核心思想是:

  1. 快指针先走N+1步:这样快慢指针之间保持N个节点的间隔

  2. 同步移动双指针:当快指针到达链表末尾时,慢指针正好指向要删除节点的前驱节点

  3. 删除目标节点:通过修改慢指针的next指针实现删除

    public ListNode removeNthFromEnd(ListNode head, int n) {
    // 创建虚拟头节点(dummy)
    ListNode dummy = new ListNode(0);
    dummy.next = head;

    复制代码
     // 初始化快慢指针
     ListNode fast = dummy;
     ListNode slow = dummy;
     
     // 快指针先走n+1步
     while (n >= 0) {
         fast = fast.next;
         n--;
     }
     
     // 同步移动双指针直到快指针到达末尾
     while (fast != null) {
         fast = fast.next;
         slow = slow.next;
     }
     
     // 删除目标节点
     slow.next = slow.next.next;
     
     // 返回真正的头节点(可能已被删除)
     return dummy.next;

    }

相关推荐
liweiweili1261 小时前
Tomcat 服务器日志
java·运维·服务器·tomcat
LZQqqqqo2 小时前
C# 中生成随机数的常用方法
java·算法·c#
2301_793086873 小时前
Springboot 04 starter
java·spring boot·后端
seabirdssss6 小时前
错误: 找不到或无法加载主类 原因: java.lang.ClassNotFoundException
java·开发语言
还是鼠鼠6 小时前
tlias智能学习辅助系统--SpringAOP-进阶-通知顺序
java·后端·mysql·spring·mybatis·springboot
哪 吒6 小时前
【2025C卷】华为OD机试九日集训第3期 - 按算法分类,由易到难,提升编程能力和解题技巧
python·算法·华为od·华为od机试·2025c卷
机器学习之心HML6 小时前
PSO-TCN-BiLSTM-MATT粒子群优化算法优化时间卷积神经网络-双向长短期记忆神经网络融合多头注意力机制多特征分类预测/故障诊断Matlab实现
神经网络·算法·cnn
数据与人工智能律师6 小时前
智能合约漏洞导致的损失,法律责任应如何分配
大数据·网络·人工智能·算法·区块链
君莫笑几人回7 小时前
关于记录一下“bug”,在做图片上传的时候出现的小问题
java·开发语言·spring boot