[LeetCode][golang] 19. 删除链表的倒数第 N 个结点

283 阅读3分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目描述:

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

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

image.png

输入: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. 获取链表结点数(循环)。

  2. 计算目标结点位置。

  3. 循环链表。

    1. 目标结点为头结点,则指向头结点的 Next 。

    2. 目标结点非头结点,则将前一个结点的 Next 设置为当前结点的 Next 。

    3. 当前结点保存为前一个结点(用于下次循环) 。

    4. 当前结点的 Next(下一个结点)再作为当前结点(用于下次循环) 。

  4. 返回头结点

单次循环

使用结点切片作为辅助。

  1. 定义结点指针切片,存放各结点指针。

  2. 循环链表,获取结点数并将各结点指针保存到定义的结点指针切片中。

  3. 计算目标结点位置。

  4. 目标结点为头结点,则头结点指向头结点的 Next(下一个结点)。

    目标结点为最后一个结点,则将切片中目标结点的前一个结点的 Next(下一个节点)设置为 nil。
    其它情况,则将切片中目标结点的前一个结点的 Next(下一个结点)设置为目标结点的 Next(下一个结点)。

  5. 返回头结点。

AC 代码:

golang :

两次循环:

// 删除链表的倒数第 N 个结点
// 两次循环
func removeNthFromEnd(head *ListNode, n int) *ListNode {
   node := head

   // 获取链表节点数
   length := 1
   for ; node.Next != nil; length = length + 1 {
      node = node.Next
   }

   // 目标节点位置
   iTarget := length - n

   // 前一个节点
   var prev *ListNode
   // 当前节点
   current := head
   // 循环到 目标节点位置 为止
   for i := 0; i <= iTarget; i++ {
      if iTarget == 0 {
         // 目标节点为头节点,则指向头节点的 Next(下一个节点)
         head = current.Next
      } else if i == iTarget {
         // 已是目标节点,则将前一个节点的 Next(下一个节点)设置为当前节点的 Next(下一个节点)
         prev.Next = current.Next
      }

      // 当前节点赋给前一个节点(用于下次循环)
      prev = current
      // 当前节点的 Next(下一个节点)再作为当前节点(用于下次循环)
      current = current.Next
   }

   return head
}

单次循环:

// 删除链表的倒数第 N 个结点
// 单次循环
func removeNthFromEnd(head *ListNode, n int) *ListNode {
   // 节点指针切片,存放各节点指针
   var slice []*ListNode

   // 获取链表节点数并将各节点的指针保存到切片中。
   node := head
   length := 1
   for ; node.Next != nil; length = length + 1 {
      slice = append(slice, node)
      node = node.Next
   }

   // 目标节点位置
   iTarget := length - n

   if iTarget == 0 {
      // 目标节点为头节点,则头节点指向头节点的 Next(下一个节点)
      head = head.Next
   } else if iTarget == length-1 {
      // 目标节点为最后一个节点,则目标节点的前一个节点的 Next(下一个节点)设置为 nil
      slice[iTarget-1].Next = nil
   } else {
      // 目标节点的前一个节点的 Next(下一个节点)设置为目标节点的 Next(下一个节点)
      slice[iTarget-1].Next = slice[iTarget].Next
   }

   return head
}

总结:

  1. 两次循环,需要将当前的结点保存起来作为下次循环的前一个结点。
    需要单独处理头结点的情况。

  2. 单次循环,需要借助切片来存放各节点的指针。
    需要单独处理头结点和末尾结点的情况。