给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?
解题思路:双指针和滑动窗口,右指针先走n步,然后左右指针一起走。左指针一开始指向添加的一个哑节点(dummy node),它的next指针指向链表的头节点。这样一来,我们就不需要对头节点进行特殊的判断了。最后左指针指向倒数第 n+1 个节点,将它的 next 指向倒数第 n-1个节点。注意一个 或 两个节点的情况。
// 方法一:双指针+滑窗
func removeNthFromEnd(head *ListNode, n int) *ListNode {
dummyHead := &ListNode{0, head}
// 前后驱指针
left, right := dummyHead, head
for i := 0; i < n; i++ {
right = right.Next
}
for ; right != nil; right = right.Next {
left = left.Next
}
left.Next = left.Next.Next
return dummyHead.Next
}
/*时间复杂度O(L) 空间复杂度O(1)*/
其他两种方式
//方法二:先求长度,再遍历到倒数n+1个, 删除
func getLength(head *ListNode) (length int) {
for ; head != nil; head = head.Next {
length++
}
return
}
func removeNthFromEnd(head *ListNode, n int) *ListNode {
length := getLength(head)
dummy := &ListNode{0, head}
cur := dummy
for i := 0; i < length-n; i++ {
cur = cur.Next
}
cur.Next = cur.Next.Next
return dummy.Next
}
/*
时间复杂度:O(2L-n),其中 L 是链表的长度。
空间复杂度:O(1)。
*/
// 方法三:栈实现
func removeNthFromEnd(head *ListNode, n int) *ListNode {
nodes := []*ListNode{}
dummy := &ListNode{0, head}
for node := dummy; node != nil; node = node.Next {
nodes = append(nodes, node)
}
prev := nodes[len(nodes)-1-n]
prev.Next = prev.Next.Next
return dummy.Next
}
/*
时间复杂度:O(L)
空间复杂度:O(L) 主要为栈的开销。*/