解法一:计算链表长度
得到链表的长度 L。随后我们再从头节点开始对链表进行一次遍历,当遍历到第 Len−n 个节点时,它就是我们需要删除的节点。
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeNthFromEnd(head *ListNode, n int) *ListNode {
// 统计链表的长度
len := 0
cur := head
for cur != nil{
len++
cur = cur.Next
}
// 考虑到有可能被删除的是头节点,因此前面申请一个哨兵节点
dummy := &ListNode{
Val: -1,
Next: head,
}
cur = dummy
// 走到倒数第n个节点的前面一个位置,需要走len-n-1步
for i := 0; i<len-n; i++{
cur = cur.Next
}
// 删除节点
cur.Next = cur.Next.Next
return dummy.Next
}
注意,最后不能直接 return head,必须返回 dummy.Next,因为head 变量在一开始被赋值为链表的头节点,后续代码并没有修改 head 本身,而是通过 dummy 进行操作,如果 head 被删除(即 n == 链表长度),直接 return head 就会返回一个已删除的节点,导致错误。
假设链表是 [1,2,3,4,5],n = 5(删除第 1 个节点,即 1)。
- 原始 head:
1 -> 2 -> 3 -> 4 -> 5 - 添加 dummy:
(-1) -> 1 -> 2 -> 3 -> 4 -> 5 - 执行
cur.Next = cur.Next.Next后:(-1) -> 2 -> 3 -> 4 -> 5 dummy.Next变为2,但head仍然是1。
如果 return head,返回的仍然是 1,但 1 已经被删除,程序会报错。所以必须返回 dummy.Next,即 新的头节点 2。
解法二:快慢指针
只扫描一次链表就可以得到答案
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeNthFromEnd(head *ListNode, k int) *ListNode {
dummy := &ListNode{
Val: -1,
Next: head,
}
// 倒数第k个节点,即正数第len-k个节点
// 为了删除该节点,需要找到其前驱节点,倒数第k+1个节点,即正数第len-k-1个节点
slow, fast := dummy, dummy
// 快指针先走k+1步
for i:=0; i<k+1; i++{
if fast == nil{ // 链表长度不足
return nil
}
fast = fast.Next
}
// 快慢指针一同前进,当快指针到达结尾之外时,慢指针走了len-(k+1)步,刚好指向倒数第k+1个节点
for fast != nil{
slow = slow.Next
fast = fast.Next
}
// 此时慢指针指向倒数第k+1个节点,删除下一个节点(即删除倒数第k个节点)
slow.Next = slow.Next.Next
return dummy.Next
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
另一种写法
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func removeNthFromEnd(head *ListNode, n int) *ListNode {
// 虚拟头结点
dummy := &ListNode{-1, head}
// 删除倒数第 n 个,要先找倒数第 n + 1 个节点
x := findFromEnd(dummy, n+1)
// 删掉倒数第 n 个节点
x.Next = x.Next.Next
return dummy.Next
}
// 返回链表的倒数第 k 个节点
func findFromEnd(head *ListNode, k int) *ListNode {
p1, p2 := head, head
// p1 先走 k 步
for i := 0; i < k; i++ {
p1 = p1.Next
}
// p1 和 p2 同时走 n - k 步
for p1 != nil {
p2 = p2.Next
p1 = p1.Next
}
// p2 现在指向第 n - k 个节点
return p2
}