「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」
题目介绍
给你一个链表,删除链表的倒数第 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]
提示:
- 链表中结点的数目为
sz 1 <= sz <= 300 <= Node.val <= 1001 <= n <= sz
解题思路
思路一:双重遍历
双重遍历是遍历两次链表,第一次遍历完整个链表,可以得出整个链表的长度,那么想要获取倒数第 N 个节点,我们只需要从头开始遍历链表,然后往前走 链表的长度 - n 步就可以走到待删除节点的上一个节点,然后将该节点的 next 指针指向待删除节点的下一个节点就可以删除链表的倒数第 N 个结点了
解题步骤
- 定义一个虚拟头节点
newNode(防止要删除的节点是链表的头节点),将虚拟头节点的next指向真实的头节点 - 定义
p指针从虚拟头节点开始遍历,k用于记录链表的长度 - 如果
p.next不为空,将p指向p.next,然后k++ - 在第 3 步记录完链表长度之后,将
p指针重新指向链表的虚拟头节点newNode,然后p指针往前走k - n步,到达待删除节点的上一个节点 - 将
p指针指向节点的next指针指向p.next.next,即可删除待删除的节点 - 返回虚拟头节点的下一个节点
解题代码
var removeNthFromEnd = function(head, n) {
// 定义一个虚拟头节点
const newNode = new ListNode(-1, head)
// 定义一个指针指向虚拟头节点
let p = newNode, k = 0
// 获取链表的长度
while (p.next) {
p = p.next
k++
}
// p指针指向虚拟头节点
p = newNode
// 从p节点走到删除节点前一个节点的距离
let step = k - n
// 走到删除节点的前一个节点
while (step--) {
p = p.next
}
// 删除倒数第n个节点
p.next = p.next.next
// 返回头节点
return newNode.next
};
思路二:双指针法
可以利用两个指针,第一个指针比第二个指针先走 n 步,然后两个指针同时往前走,当快指针走到链表尾部时,慢指针指向的节点即为待删除节点的上一个节点
解题步骤
- 定义一个虚拟头节点
newNode(防止要删除的节点是链表的头节点),将虚拟头节点的next指向真实的头节点 - 定义快指针
next和慢指针pre同时从虚拟头节点开始 - 快指针
next先往前走n步 - 快慢指针同时往前走,直到快指针
next走到链表尾部 - 将
pre指向的节点的next指针指向pre.next.next - 返回虚拟头节点的下一个节点
解题代码
var removeNthFromEnd = function(head, n) {
// 创建虚拟头节点
const newNode = new ListNode(-1, head)
// 定义两个指针指向虚拟头节点
let pre = next = newNode
// 快指针往前走n个位置
while (n--) {
next = next.next
}
// 快指针和慢指针同时移动,当快指针走到结尾时,慢指针指向删除节点的前一个节点
while (next.next) {
pre = pre.next
next = next.next
}
// 将慢指针的下一个节点指向删除节点的下一个节点
pre.next = pre.next.next
// 返回头节点
return newNode.next
};
至此,我们用两种方法删除了链表的倒数第 N 个结点