Swift 数据结构与算法( 25) 链表 + M_Leetcode19. 删除链表的倒数第 N 个结点(双指针)

65 阅读3分钟

概念

在 LeetCode 中,当题目描述中提到“倒数第 n 个结点”,通常是从 1 开始计数的。也就是说,倒数第 1 个结点是链表的最后一个结点。

例如,对于链表 [1,2,3,4,5],倒数第 1 个结点是 5,倒数第 2 个结点是 4,依此类推。

易错点

核心概念

双指针技巧: 这是一个在链表问题中常见的技巧,特别是与链表长度或位置相关的题目。通过使用两个指针间隔一定的距离移动,我们可以避免对链表的多次遍历。在这个问题中,我们使用两个指针,一个先行指针和一个后行指针,先行指针领先后行指针 n 步。当先行指针到达链表的末尾时,后行指针指向的节点就是我们需要删除的节点的前一个节点。

2. 代码中的错误

错误second 指针始终指向头结点,没有随着 first 指针的移动而移动。 原因:当 first 指针向前移动时,second 指针应该也要开始移动,以保持它们之间的距离。但在您的代码中,second 只在 first 到达链表尾部时开始移动,这导致了错误。

您当时可能是怎么想的:您可能想让 first 指针先行,然后再移动 second 指针。但您可能忘记了,一旦 first 指针移动了 n 步,second 指针就应该开始和 first 指针一起移动。

如何避免

  1. 清晰地描述算法逻辑:在开始编码之前,先在纸上或脑中清晰地描述算法逻辑。
  2. 绘制示意图:对于链表问题,通过绘制示意图来表示节点和指针的关系,这有助于更好地理解和避免错误。
  3. 测试代码:使用多个测试用例,特别是边界条件来测试代码,确保其正确性。

为什么要使用 var first: ListNode? = dummy

 var dummy = ListNode(0)
   dummy.next = head
        
  var first: ListNode? = dummy
  var second: ListNode? = dummy

如果 secondhead 开始,并且我们要删除的是链表的第一个节点,那么当 first 遍历到链表末尾时,second 并不会停留在 head。它会移动到需要删除的节点的前一个节点。但问题在于,当我们需要删除的是链表的第一个节点时,second 本应该停留在一个“虚拟节点”上,这样我们才能删除 head。但由于没有这样的“虚拟节点”,所以我们无法正确地删除 head

因此,为了统一处理所有情况(包括删除链表的第一个节点),我们使用 dummy 作为起始节点。这样,无论我们要删除的节点是链表中的任何一个节点,second 都会正确地指向它的前一个节点。

题目

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

解题思路🙋🏻‍ ♀️

边界思考🤔

代码


class Solution {
    func removeNthFromEnd(_ head: ListNode?, _ n: Int) -> ListNode? {

        if n < 1 {
            return head
        }
        
        var dummy = ListNode(0)
        dummy.next = head
        
        var first: ListNode? = dummy
        var second: ListNode? = dummy
        var count = 0

        while count < n {
            first = first?.next
            count += 1
            // 检查first是否为nil
            if first == nil && count < n {
               return head
            }
        }
        
        while first?.next != nil {
            first = first?.next
            second = second?.next
        }
                
        second?.next = second?.next?.next
        return dummy.next
    
    }
}

时空复杂度分析

O(n)

引用

本系列文章部分概念内容引用 www.hello-algo.com/

解题思路参考了 abuladong 的算法小抄, 代码随想录... 等等

Youtube 博主: huahua 酱, 山景城一姐,