19.删除链表的倒数第 N 个结点

90 阅读1分钟

19. 删除链表的倒数第 N 个结点

题目描述

给你一个链表,删除链表的倒数第 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 <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

 

进阶: 你能尝试使用一趟扫描实现吗?

题解

方法 1

删除倒数第 n 节点等价于删除逆转后链表正数第 n 个节点

  1. 逆置链表
  2. 使用哑节点统一处理头节点和其他节点
  3. 找到待删除节点前驱
  4. 删除节点并返回结果链表
/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func removeNthFromEnd(head *ListNode, n int) *ListNode {
    // 1. 逆置链表
    head = reverse(head)
    // 2. 使用哑节点统一处理头节点和其他节点
    dummy := &ListNode{}
    dummy.Next = head
    // 3. 找到待删除节点前驱
    curNode := dummy
    for i := 0; i < n-1; i++ {
        curNode = curNode.Next
    }
    // 4. 删除节点并返回结果链表
    needDel := curNode.Next
    curNode.Next = needDel.Next
    needDel.Next = nil
    return reverse(dummy.Next)
}   

func reverse(head *ListNode) *ListNode {
    var prev *ListNode
    for head != nil {
        next := head.Next
        head.Next = prev
        prev = head
        head = next
    }
    return prev
}

图示

image.png

方法 2 快慢指针

思路:可以让快指针先移动 n 步,然后慢指针去追快指针,当快指针跑到末尾时,慢指针刚好指向待删除节点的前驱。

代码

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func removeNthFromEnd(head *ListNode, n int) *ListNode {
    var slow, fast, dummy, needDel *ListNode
    dummy = &ListNode{Next: head}
    slow, fast = dummy, dummy
    for i := 0; i < n && fast != nil; i++ {
        fast = fast.Next
    }

    for fast != nil && fast.Next != nil {
        slow = slow.Next
        fast = fast.Next
    }

    needDel = slow.Next
    slow.Next = needDel.Next
    needDel.Next = nil
    return dummy.Next
}

图示

image.png

用 dummy 解决了下面的特殊情况(首节点是待删除节点)

image.png