前言
本文是LC第19题:删除链表的倒数第 N 个结点
题目描述
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
限制:
链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
进阶:你能尝试使用一趟扫描实现吗?
示例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]
解题思路
-
非递归解决
先求出链表的长度length,然后就可以找到要删除链表的前一个结点,让他的前一个结点指向要删除结点的下一个结点即可。以示例1为例画个图看一下
首先让 head 指针指向链表的第一个元素。
然后找到要删除节点的前一个节点。
最后让 pre->next 指针指向要删除节点的下一个节点。 -
双指针解法
上面是先计算链表的长度,其实不计算链表的长度也是可以,我们可以使用两个指针,一个指针fast先走n步,然后另一个指针slow从头结点开始,找到要删除结点的前一个结点,这样就可以完成结点的删除了。 -
递归解法
我们知道获取链表的长度除了上面介绍的一种方式以外,还可以写成递归的方式,比如
//求链表的长度
private int length(ListNode head) {
if (head == null)
return 0;
return length(head.next) + 1;
}
上面计算链表长度的递归其实可以把它看做是从后往前计算,当计算的长度是n的时候就表示遍历到了倒数第n个节点了,这里只要求出倒数第n+1个节点,问题就迎刃而解了
代码
非递归解法——两次遍历
此解法,核心在于找到待删除节点——倒数第N个节点。
如果我们要删除的是第N个节点,那么可以写出的代码是
while (n--) { p = p->next;}
此时 p 节点指向的就是待删除节点。(p节点初始指向 dummynode)
转换一下题目,将倒数第N个节点转换为第M个节点,进行删除。那么M的值是多少?如果知道链表的长度L,那么M = L - N + 1
。举个例子:链表 {1,2, 3, 4, 5}, L = 5, N =2, 要删除就是节点4, 而根据公式算 M = 4, 也就是要删除第 4 个节点,节点值为 4。
知道了要删除的节点所在位置(左->右遍历),那么很容易写出如下的代码:
int m = l - n + 1;
while (m--) {
p = p->next;
}
那么代码链表长度 L 的值如何得来?答案是遍历一遍链表。
因此,这种解法我们需要遍历 2 遍链表,第一遍确定链表长度 L,第二遍找到待删除节点,进行删除。
这版代码简单,读者可以自行编写。
时间复杂度仍然是 O(n)
非递归解法——双指针版
可以将链表分为两部分,假设 L = M + N
, 链表分为 M 个节点和 N 个节点。
我们要删除的是倒数第 N 个节点,也就是第 M + 1 个节点,删除第 M + 1个节点,只需要知道第 M 个节点就可以。
如下,将链表分为两部分,后N个节点是没用的,所以我们可以“抹去后N个节点”,然后让链表指针从第一个节点,移动到最后一个节点。
这里我们将第 M 个节点(第3个节点)的下一个节点看做空,然后将一个指针看做初始指向第 M+1 个节点(待删节点),然后这个指针先向后移动 N 个节点,然后一直向后移动直到遇到第 M 个节点的下一个节点(空节点)。
这个时候,我们是不是就找到了第M个节点?而且回顾下这个过程:真正实际的步骤只有:
指针先向后移动N步,然后向后移动直到节点的next指针为空
在这个描述中,N是已知的,向后移动直到next指针为空则是虚构出来的条件。
因为事实上第 M 个节点的 next 指针指向的节点非空,我们只是为了方便理解如何找到第 M 个节点做出的一种辅助方式。
故,想让一个指针完成这个操作显然是不行的。我们需要再加一个指针,过程就变成了
fast 指针先向后移动 N 步, slow 指针不动
slow 指针和 fast 指针同时向后移动,直到节点的 next 为 NULL
而后,slow 指针指向的节点就是第 M 个节点。
代码如下
/**
* 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) {
if(head == nullptr) return nullptr;
ListNode* dummyNode = new ListNode(-1);
dummyNode->next = head;
ListNode* p = dummyNode;
while(head)
{
if(n-- <= 0 && head) p = p->next;
head = head->next;
}
p->next = p->next->next;
head = dummyNode->next;
delete dummyNode;
return head;
}
};