返回

在 LeetCode 上征服#83:告别重复,拥抱有序!

见解分享

驯服链表中的重复元素:LeetCode #83 算法之旅

认识我们的对手:LeetCode #83

在数字世界的浩瀚海洋中,重复就像一位不速之客,潜伏在我们精心构建的数据结构中,破坏着代码的整洁性和效率。对于任何一位严肃的程序员来说,驯服这些重复元素都是一项必备技能。LeetCode 上的#83 题就是这样一个考验,它要求我们从一个排序链表中删除所有重复元素。准备好踏上算法之旅,在这个过程中,我们将磨练我们的技能,让我们的代码更上一层楼!

算法策略:两步法

为了解决这个问题,我们将采用一种两步法:

  1. 遍历链表: 使用两个指针,currprev,遍历链表。
  2. 检查重复: 如果 curr 节点与 prev 节点相等,则删除 curr 节点。

代码实现:

func deleteDuplicates(_ head: ListNode?) -> ListNode? {
    var curr = head
    var prev: ListNode? = nil

    while curr != nil {
        if prev == nil || prev!.val != curr!.val {
            prev = curr
        } else {
            prev?.next = curr?.next
        }
        curr = curr?.next
    }

    return head
}

算法解析

我们的代码从头结点开始遍历链表。两个指针,currprev,一路同行。如果 curr 节点与 prev 节点相等,则说明遇到了重复元素,我们通过将 prevnext 指向 currnext 来删除 curr 节点。否则,prev 指向 currcurr 移动到下一个节点。这种方法确保了链表中的每个元素都只出现一次。

性能分析

算法的时间复杂度为 O(n),其中 n 是链表中的节点数。遍历链表需要 O(n) 时间。对于每个节点,我们最多进行一次恒定时间操作,因此总的复杂度为 O(n)。

实例示例

让我们用一个示例链表 [1, 1, 2, 3, 3] 来演示算法。

  • 遍历链表时,curr 指向第一个 1 节点,prevnil
  • 由于 prevnil,所以我们保留 curr 节点。
  • 现在,curr 指向第二个 1 节点,prev 指向第一个 1 节点。
  • 由于 currprev 相等,我们删除 curr 节点。prevnext 直接指向第三个节点。
  • 继续遍历,我们删除第二个 3 节点,最终得到一个不重复元素的链表:[1, 2, 3]。

总结

通过解决 LeetCode #83 题,我们加强了我们在链表操作和算法解决问题方面的技能。我们的两步法算法有效地删除了重复元素,同时保持了链表的顺序。无论是初学者还是经验丰富的程序员,LeetCode 都是磨练算法技巧和加深对数据结构理解的宝贵平台。让我们继续征服 LeetCode 的挑战,一步步提升我们的编程实力!

常见问题解答

  1. 算法是否有空间复杂度?
    算法的空间复杂度为 O(1),因为我们只使用有限数量的指针和变量。

  2. 算法是否适用于未排序的链表?
    不,算法要求链表已经排序,否则无法有效地检测重复元素。

  3. 如何处理链表中的循环?
    算法无法处理链表中的循环,因为它可能会陷入无限循环。

  4. 算法是否可以在原地修改链表?
    是的,算法可以在原地修改链表,无需创建新的链表。

  5. 如何扩展算法以删除所有元素,而不只是重复元素?
    可以通过在算法的开头添加一个条件来实现,该条件检查 curr 节点是否为 nil。如果是,则删除链表的所有元素。