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

相关推荐
前端小白在前进2 小时前
力扣刷题:在排序数组中查找元素的第一个和最后一个位置
数据结构·算法·leetcode
iAkuya4 小时前
(leetcode)力扣100 23反转链表(迭代||递归)
算法·leetcode·链表
FreeBuf_4 小时前
朝鲜黑客组织“传染性面试“瞄准macOS:新型“DriverFixer“窃密工具浮出水面
macos·面试·职场和发展
妮妮喔妮5 小时前
大文件上传面试回答要点
面试·大文件上传
牛客企业服务5 小时前
AI面试实用性解析:不是“能不能用”,而是“怎么用好”
人工智能·面试·职场和发展
a努力。5 小时前
宇树Java面试被问:方法区、元空间的区别和演进
java·后端·面试·宇树科技
a程序小傲6 小时前
蚂蚁Java面试被问:注解的工作原理及如何自定义注解
java·开发语言·python·面试
lcreek6 小时前
LeetCode215. 数组中的第K个最大元素、LeetCode912. 排序数组
python·算法·leetcode
杜子不疼.6 小时前
【LeetCode 852 & 162_二分查找】山脉数组的峰顶索引 & 寻找峰值元素
算法·leetcode·职场和发展