面试经典150题[063]:删除链表的倒数第 N 个结点(LeetCode 19)

删除链表的倒数第 N 个结点(LeetCode 19)

题目链接:删除链表的倒数第 N 个结点(LeetCode 19)

难度:中等

1. 题目描述

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

要求:

  • 链表中每个结点的值都是唯一的
  • 1 <= 链表长度 <= 30
  • 1 <= n <= 链表长度
  • 题目保证 n 是有效的(即一定能删除)

示例:

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

输入:head = [1], n = 1
输出:[]

输入:head = [1,2], n = 1
输出:[1]

2. 问题分析

2.1 规律

  • 链表是单向的,无法直接从尾部访问。
  • 倒数第 n 个结点 → 正数第 L - n + 1 个(L 为链表长度),但计算长度需要两遍遍历。
  • 核心问题:如何一次遍历找到倒数第 n 个结点?

2.2 快慢指针(双指针)思路

使用 快慢指针 技巧,一次遍历解决:

  • fast 先走 n 步。
  • 然后 slowfast 同时向前走,直到 fast 到达末尾(fast.next == null)。
  • 此时 slow 恰好位于 倒数第 n+1 个结点 (因为 fastslow 超前 n 步)。
  • 删除 slow.next 即可。

特殊处理

  • 如果删除的是头结点(即 n 等于链表长度),直接返回 head.next
  • 使用 虚拟头结点(dummy head) 可统一处理所有情况,避免单独判断头结点。

3. 代码实现

Python

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: ListNode, n: int) -> ListNode:
        # 创建虚拟头结点,简化删除头结点的逻辑
        dummy = ListNode(0, head)
        fast = slow = dummy
        
        # fast 先走 n 步
        for _ in range(n):
            fast = fast.next
        
        # fast 和 slow 一起走,直到 fast 到末尾
        while fast.next:
            fast = fast.next
            slow = slow.next
        
        # slow.next 就是要删除的结点
        slow.next = slow.next.next
        
        return dummy.next

C++

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        // 虚拟头结点
        ListNode* dummy = new ListNode(0, head);
        ListNode* fast = dummy;
        ListNode* slow = dummy;
        
        // fast 先走 n 步
        for (int i = 0; i < n; ++i) {
            fast = fast->next;
        }
        
        // 一起走,直到 fast 到最后一个结点
        while (fast->next) {
            fast = fast->next;
            slow = slow->next;
        }
        
        // 删除 slow 的下一个结点
        ListNode* temp = slow->next;
        slow->next = slow->next->next;
        delete temp;
        
        ListNode* result = dummy->next;
        delete dummy;  // 可选:释放虚拟头结点
        return result;
    }
};

4. 复杂度分析

  • 时间复杂度 :O(L),其中 L 是链表长度。只遍历一次。
  • 空间复杂度:O(1),只使用了常数额外空间(虚拟头结点可忽略)。

5. 总结

  • 倒数第 n 个快慢指针 是标配
  • 使用 虚拟头结点 统一处理边界(删除头结点)
  • 类似题目:
    • 环形链表 II(快慢指针找入口)
    • 链表中点(LeetCode 876)
  • 可扩展:删除倒数第 n 个后 重建链表返回被删结点值

复习

面试经典150题003删除有序数组中的重复项(LeetCode 26)

面试经典150题033最小覆盖子串(LeetCode 76)

面试经典150题048汇总区间(LeetCode 228)

相关推荐
thesky12345612 小时前
智能体面试准备(九):工作流编排——DAG 与状态机两种范式的取舍与手写实现
面试·agent·状态机·工作流·智能体
hanlin0313 小时前
刷题笔记:力扣第242、349题(哈希表)
笔记·算法·leetcode
Revolution6113 小时前
reactive 对象重新赋值后,表单为什么没有恢复默认值
前端·vue.js·面试
HeiSenBerg13 小时前
Android Handler 机制完全解析:从源码到内存泄漏,一篇就够了
面试
only-qi14 小时前
大模型Agent面试攻略:落地工程痛点、评估体系与Agentic RAG核心精讲
人工智能·算法·面试·职场和发展·langchain·rag
ShineWinsu15 小时前
对于Linux:传输层协议UDP原理的解析
linux·c++·面试·udp·协议·传输层·计算机系统
GitLqr15 小时前
StatefulWidget 里的隐形炸弹:为什么不要在 State 类中使用 context.mounted
flutter·面试·dart
梦想的颜色17 小时前
2026 AI Agent 工程师完整技术图谱|从面试题「什么是本体 Ontology」切入,附精选面试题库
面试·知识图谱·langgraph·aiagent·大模型面试·本体·2026 面试真题
不简说17 小时前
JS 代码技巧 vol.10 — 20 个 V8 引擎原理,解释"为什么这样写快"
前端·javascript·面试
青山木19 小时前
Hot 100 --- 组合总和
java·数据结构·算法·leetcode