hot100_删除链表的倒数第n个节点
2026/9/4 21:36:23 网站建设 项目流程

1. 题目

给你一个链表,删除链表的倒数第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]


2. 题解

2.1. 计算

2.1.1. 核心思想

链表只能向后遍历,不能直接访问倒数位置,没有下标。 倒数第n个结点 ⇔正数第总长度 − n+ 1 个结点

例:链表[1,2,3,4,5],长度count=5,删除倒数第 2 个 (4)count‑n = 5‑2 =3→ 正数第 3 个结点 (3),是待删节点的前驱。 让前驱结点的 next,跳过待删结点:cur->next = cur->next->next

2.1.2. 代码

/** * 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) {} * }; */classSolution{public:ListNode*removeNthFromEnd(ListNode*head,intn){ListNode*dummy=newListNode(0,head);ListNode*cur=head;intlen=0;while(cur){len++;cur=cur->next;}cur=dummy;// 走到待删节点的前驱:len-n步for(inti=0;i<len-n;i++){cur=cur->next;}ListNode*del=cur->next;cur->next=cur->next->next;deletedel;ListNode*ans=dummy->next;deletedummy;returnans;}};

2.1.3. 复杂度

时间复杂度:O ( L ) O(L)O(L),L 是链表长度,完整遍历 2 次链表
空间复杂度:O ( 1 ) O(1)O(1),只用几个指针、计数器变量

2.2. 栈

2.2.1. 核心思想

栈:后进先出。 把链表所有节点依次压入栈中,栈底是头结点,栈顶是尾结点。 弹出 n 个节点,弹出的第 1 个就是要删除的倒数第 n 个结点。 此时栈顶剩下的元素,就是待删节点的前驱结点。 然后修改前驱的 next,跳过被删除节点。

边界:如果弹完 n 个之后栈为空,说明要删的是头结点。

2.2.2. 代码

/** * 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) {} * }; */classSolution{public:ListNode*removeNthFromEnd(ListNode*head,intn){stack<ListNode*>st;ListNode*cur=head;while(cur!=nullptr){st.push(cur);cur=cur->next;}ListNode*del=nullptr;for(inti=0;i<n;i++){del=st.top();st.pop();}if(st.empty()){head=head->next;}else{ListNode*pre=st.top();pre->next=pre->next->next;}deletedel;returnhead;}};

2.2.3. 复杂度

时间复杂度:O ( L ) O(L)O(L),L 链表长度。遍历一次链表入栈,再弹出 n 次。
空间复杂度:O ( L ) O(L)O(L)需要栈存储全部链表节点

2.3. 双指针

2.3.1. 核心思想

利用两个指针保持固定间隔 n。 快指针先往前走n 步;之后快慢指针同步一起往后走。 当快指针走到链表末尾 (nullptr) 时,慢指针恰好落在待删除节点的前驱结点

为什么可以这样? 倒数第 n 个节点,距离链表末尾空指针的距离正好是 n。 让快指针先拉开 n 的距离,再同速前进,快指针碰到底,慢指针就定位到目标前驱。

必须搭配dummy 虚拟头结点,规避删除头结点的特殊边界。 如果不用 dummy,删除头节点的情况要额外 if 判断。

2.3.2. 代码

/** * 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) {} * }; */classSolution{public:ListNode*removeNthFromEnd(ListNode*head,intn){ListNode*dummy=newListNode(0,head);ListNode*fast=dummy;ListNode*slow=dummy;for(inti=0;i<n;i++){fast=fast->next;}while(fast->next!=nullptr){fast=fast->next;slow=slow->next;}ListNode*del=slow->next;slow->next=slow->next->next;deletedel;ListNode*res=dummy->next;deletedummy;returnres;}};

2.3.3. 复杂度

时间复杂度:O ( L ) O(L)O(L)只遍历链表一遍。总共移动指针 L 次
空间复杂度:O ( 1 ) O(1)O(1),仅几个指针变量,常数空间。

2.4. 三种算法对比

方法时间空间特点
计数(两次遍历)O(L)O(1)直观,遍历两遍,要处理头结点边界
O(L)O(L)利用后进先出,逻辑简单,额外占用内存
快慢指针O(L)O(1)一次遍历,双指针距离差,最优

3.19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询