面试经典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)

相关推荐
VT.馒头7 分钟前
【力扣】2721. 并行执行异步函数
前端·javascript·算法·leetcode·typescript
马猴烧酒.3 小时前
【面试八股|Java集合】Java集合常考面试题详解
java·开发语言·python·面试·八股
不穿格子的程序员4 小时前
从零开始写算法——普通数组篇:缺失的第一个正数
算法·leetcode·哈希算法
闻哥6 小时前
从测试坏味道到优雅实践:打造高质量单元测试
java·面试·单元测试·log4j·springboot
VT.馒头6 小时前
【力扣】2722. 根据 ID 合并两个数组
javascript·算法·leetcode·职场和发展·typescript
执着2597 小时前
力扣hot100 - 108、将有序数组转换为二叉搜索树
算法·leetcode·职场和发展
兩尛7 小时前
160. 相交链表/c++
数据结构·链表
2302_813806227 小时前
【嵌入式修炼:数据结构篇】——单向链表的排序
数据结构·链表·排序算法
52Hz1188 小时前
力扣230.二叉搜索树中第k小的元素、199.二叉树的右视图、114.二叉树展开为链表
python·算法·leetcode
苦藤新鸡8 小时前
56.组合总数
数据结构·算法·leetcode