LeetCode系列记录我学习算法的过程。
持续创作,加速成长!这是我参与「掘金日新计划 6 月更文挑战」的第 18 天,点击查看活动详情
题目
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例:
输入: head = [1,2,3,4,5], n = 2
输出: [1,2,3,5]
提示
- 链表中结点的数目为
sz 1 <= sz <= 300 <= Node.val <= 1001 <= n <= sz
思路
这题解起来倒是不难,用两个循环来实现,第一个循环获取结点数,第二个循环删除指定位置结点
- 定义
temp暂存head,遍历head获取结点数len - 如果是删除头结点,即
len === n,返回head.next即可 - 将
temp重置为head再次进行遍历 - 遍历到要删除的位置的前一个结点,即
len - n位置时,将下一个结点修改为下下个结点即可实现删除下个节点 - 跳出循环,返回
head
代码实现
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function(head, n) {
let len = 0, // 记录链表的结点数
idx = 0, // 标记当前结点下标
temp = head
// 遍历链表 记录结点数
while(temp) {
len++
temp = temp.next
}
// 如果结点数和 n 一致,即删除第一个结点,直接返回第二个结点即可
if (len === n) return head.next
// 重置temp
temp = head
while(temp) {
idx++
// 删除倒数第 n 个结点,即删除第 len - n + 1 个结点
// 所以在其前一个(即 len - n)结点将其下一个结点改变为下下个结点
// 即可实现删除下一个结点
if (idx === (len - n)) {
temp.next = temp.next?.next || null
break
}
temp = temp.next
}
return head
};
优化
题目其实还给了个进阶目标,即 你能尝试使用一趟扫描实现吗?
但是我没有想到如何来实现,所以就去学习了下别人的题解
通过快慢指针的方式,要删除倒数第 n 项,只需要让快指针和慢指针间隔 n 项
当快指针指到 null 时,慢指针下一项就是需要删除的项
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function(head, n) {
let slow = null, // 慢指针置空
fast = head, // 快指针指向头结点
idx = 1 // 快指针下标
// 当快指针指向 null 时 结束循环
while(fast) {
// 当快指针下标大于 n 时,开始移动慢指针
if(idx > n) {
// 慢指针此时与快指针相差 n 项
slow = slow ? slow.next : head
}
// 移动快指针和下标
fast = fast.next
idx++
}
// 如果慢指针为 null,即删除头结点
if (!slow) return head.next
// 慢指针不为 null,则删除下一结点
slow.next = slow.next?.next || null
return head
};