【LeetCode刷题】删除链表的倒数第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

解法思路

  1. 虚拟头节点 :创建dummy节点指向原链表头,避免删除头节点时的特殊处理(如输入head=[1], n=1的情况)。
  2. 双指针初始化 :fast和slow都从dummy开始。
  3. 快指针先行 :让fast先向前走n步,拉开与slow的距离。
  4. 同步移动指针 :fast和slow同时前进,直到fast到达链表末尾(fast.next为None)。此时slow的下一个节点,就是链表的倒数第 N 个节点。
  5. 删除节点 :将slow.next指向slow.next.next,完成节点删除。

核心优势

  • 时间复杂度 O (L):仅遍历链表一次(L 为链表长度),比 "先统计长度再删除" 的两次遍历更高效。
  • 边界全覆盖:通过虚拟头节点,完美处理 "删除头节点""链表长度为 1" 等特殊情况。

Python代码

python 复制代码
# 导入必要的类型注解模块
from typing import Optional


# 完整定义单链表节点类
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

    # 可选:添加打印链表的辅助方法(方便测试)
    def __str__(self):
        result = []
        current = self
        while current:
            result.append(str(current.val))
            current = current.next
        return " -> ".join(result)


# 核心解题类
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        # 虚拟头节点:避免删除头节点时的边界处理
        dummy = ListNode(0)
        dummy.next = head
        fast = dummy
        slow = dummy

        # 步骤1:快指针先向前走n步
        for _ in range(n):
            fast = fast.next

        # 步骤2:快慢指针同时前进,直到快指针到达链表末尾
        while fast.next:
            fast = fast.next
            slow = slow.next

        # 步骤3:删除慢指针的下一个节点(倒数第n个节点)
        slow.next = slow.next.next

        # 返回新的头节点(虚拟头节点的下一个)
        return dummy.next


# 辅助函数:将列表转换为链表(方便测试)
def list_to_linkedlist(arr):
    if not arr:
        return None
    head = ListNode(arr[0])
    current = head
    for val in arr[1:]:
        current.next = ListNode(val)
        current = current.next
    return head


# 测试用例
if __name__ == "__main__":
    # 测试用例1:常规情况
    arr1 = [1, 2, 3, 4, 5]
    n1 = 2
    head1 = list_to_linkedlist(arr1)
    print("原链表1:", head1)
    solution = Solution()
    new_head1 = solution.removeNthFromEnd(head1, n1)
    print(f"删除倒数第{n1}个节点后:", new_head1)  # 预期输出:1 -> 2 -> 3 -> 5

    # 测试用例2:删除头节点
    arr2 = [1, 2]
    n2 = 2
    head2 = list_to_linkedlist(arr2)
    print("\n原链表2:", head2)
    new_head2 = solution.removeNthFromEnd(head2, n2)
    print(f"删除倒数第{n2}个节点后:", new_head2)  # 预期输出:2

    # 测试用例3:链表只有一个节点
    arr3 = [1]
    n3 = 1
    head3 = list_to_linkedlist(arr3)
    print("\n原链表3:", head3)
    new_head3 = solution.removeNthFromEnd(head3, n3)
    print(f"删除倒数第{n3}个节点后:", new_head3)  # 预期输出:None

LeetCode提交代码

python 复制代码
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        # 虚拟头节点:避免删除头节点时的边界处理
        dummy = ListNode(0)
        dummy.next = head
        fast = dummy
        slow = dummy
        
        # 步骤1:快指针先向前走n步
        for _ in range(n):
            fast = fast.next
        
        # 步骤2:快慢指针同时前进,直到快指针到达链表末尾
        while fast.next:
            fast = fast.next
            slow = slow.next
        
        # 步骤3:删除慢指针的下一个节点
        slow.next = slow.next.next
        
        # 返回新的头节点(虚拟头节点的下一个)
        return dummy.next

程序运行结果展示

原链表1: 1 -> 2 -> 3 -> 4 -> 5

删除倒数第2个节点后: 1 -> 2 -> 3 -> 5

原链表2: 1 -> 2

删除倒数第2个节点后: 2

原链表3: 1

删除倒数第1个节点后: None

总结

本文介绍了一种高效删除链表倒数第N个节点的双指针解法。通过创建虚拟头节点处理边界情况,使用快慢指针实现一次遍历:快指针先走N步,然后两指针同步移动直至链表末尾,此时慢指针的下一个节点即为待删除节点。该方法时间复杂度O(L)(L为链表长度),优于两次遍历的解法,且能完美处理删除头节点等特殊情况。文中提供了Python实现代码,包含链表转换、测试用例及运行结果验证,确保算法正确性和鲁棒性。该解法已通过LeetCode测试,适用于所有合法输入情况。

相关推荐
代码山河5 小时前
Python就业方向全解析:后端、爬虫、数据分析、AI你选哪个?
python·学习·架构·教程·面向对象·项目
在世修行6 小时前
智学文枢:题目流水线
python·多线程·状态机·pyside
梯度下降者6 小时前
零基础入门Python 爬虫
爬虫·python·数据分析·入门指南·webcrawler
UIU1148 小时前
P1233 [ICPC 2001 Taejon R] 木棍加工
数据结构·c++·算法·动态规划
“AI国潮设计-小江”8 小时前
【AIGC实战】Python+Stable Diffusion打造“英歌舞财神”IP,解锁“马到成功”等爆款情绪营销文案设计
开发语言·人工智能·python·prompt·aigc
论文复现现场8 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
ZhengEnCi8 小时前
用 AI 写代码为什么拼到最后还是拼人:算力瓶颈与领域专家提示词的本质差异
算法·架构
benchmark_cc9 小时前
用 Python 拉 A 股全市场实时快照:Notebook 请求、结果校验与易错点
python·数据分析·pandas·量化交易·股票数据·quantdash
伞伞悦读9 小时前
【第70期】Adam二阶矩不是方差:偏差校正、动量与AdamW完整实验
python
秋天的怀念_David9 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法