一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第9天,点击查看活动详情
1. 计算总长度
将反向转成正向问题。找到链表的总长度,然后 总长度 - n 就是正向要删除的链表节点的位置。
-
因为有可能删除头节点,所以要注意使用哨兵节点
-
删除操作:
- 能找到前一个节点
pre:pre.next = pre.next.next。(本题中使用)
-
如果链表没有提供
head,只给了当前节点cur,就无法从头开始找到前一个pre。这种情况可以通过同时修改cur.val和cur.next实现:
cur.val = cur.next.val cur.next = cur.next.next - 能找到前一个节点
function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
const dummy = new ListNode(-1, head)
let cur = head
let count = 0
// 计算总长度
while(cur) {
count++
cur = cur.next
}
// 计算正向需要删除的位置
let forwardNum = count - n
cur = dummy
// cur从哨兵节点开始,forwardNum正好可以找到要删除节点的 pre节点
while(forwardNum--) {
cur = cur.next
}
// 删除节点
cur.next = cur.next.next
return dummy.next
};
2. 双指针
这里也可以使用双指针实现:
- 1.先设定一个指针,从头开始走
n步,找到第n + 1个节点 - 2.在设置第二个指针,从
哨兵节点开始 - 3.同时移动两个指针:当第一个指针变成
null时,第二个指针正好在要删除指针的前一个位置。 - 4.删除
代码如下:
function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
const dummy = new ListNode(-1, head)
let cur = head
let count = n
while(count--) {
cur = cur.next
}
let other = dummy
while(cur) {
cur = cur.next
other = other.next
}
other.next = other.next.next
return dummy.next
};
复杂度
两种方式:
- 时间复杂度: 最多扫描两次,
O(N),N为链表长度; - 空间复杂度: 常量级缓存:
O(1)。