返回

揭秘 LeetCode 第 15 题:三数之和的奥秘

Android

解锁「三数之和」的奥秘:算法与数据结构的三种解法

简介

踏上算法和数据结构的探索之旅,我们总会遇到一些标志性的挑战。其中,第 15 题「三数之和」便是备受推崇的一道题,旨在考察算法设计和数据处理的综合实力。本篇博客将从动态规划、哈希表和双指针三个角度,为你全面剖析「三数之和」的奥秘,助你掌握解决该问题的关键技巧。

动态规划:逐层递进,穷举所有可能

宛如一位勤奋的园丁,动态规划算法采用自底向上的策略,将复杂的问题逐步拆解成一个个小问题。对于「三数之和」,我们可以将它拆分为如下子问题:

  • 如果数组长度不足 3,显然无解。
  • 对于长度为 n 的数组,我们可以先固定第一个元素 a,然后在剩余数组中寻找两个元素 b 和 c,使得 a + b + c = 0。

基于此递推关系,我们构建一张动态规划表,逐层穷举所有满足条件的三元组 (a, b, c)。

代码示例:

def three_sum_dp(nums):
    n = len(nums)
    if n < 3:
        return []

    dp = {}
    for i in range(n - 2):
        for j in range(i + 1, n - 1):
            for k in range(j + 1, n):
                if nums[i] + nums[j] + nums[k] == 0:
                    dp[(nums[i], nums[j], nums[k])] = True

    return list(dp.keys())

哈希表:快速查找,避免重复

哈希表如同一个聪明的管家,它能快速查找和避免重复,为「三数之和」提供了另一种巧妙的解法。

算法步骤:

  • 将数组中所有元素的出现次数存储到哈希表中。
  • 对于每个元素 a,在哈希表中查找 -a-b。如果找到,则存在三元组 (a, -a-b, b)。

代码示例:

def three_sum_hash(nums):
    n = len(nums)
    if n < 3:
        return []

    nums.sort()
    result = []
    for i in range(n - 2):
        if i > 0 and nums[i] == nums[i - 1]:
            continue

        left, right = i + 1, n - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]
            if total == 0:
                result.append((nums[i], nums[left], nums[right]))
                while left < right and nums[left] == nums[left + 1]:
                    left += 1
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1
                left += 1
                right -= 1
            elif total < 0:
                left += 1
            else:
                right -= 1

    return result

双指针:前后夹击,高效收敛

双指针算法如同两位默契的舞伴,从数组的两端向中间收敛,寻找和为 -a 的情况。

算法步骤:

  • 将数组排序。
  • 初始化两个指针 left 和 right 分别指向数组的第一个和最后一个元素。
  • 如果 nums[left] + nums[right] == -a,则找到三元组 (a, nums[left], nums[right])。
  • 如果 nums[left] + nums[right] < -a,则 left 右移,缩小和小于 -a 的范围。
  • 如果 nums[left] + nums[right] > -a,则 right 左移,缩小和大于 -a 的范围。
  • 重复上述步骤,直到 left 和 right 相遇。

代码示例:

def three_sum_two_pointers(nums):
    n = len(nums)
    if n < 3:
        return []

    nums.sort()
    result = []
    for i in range(n - 2):
        if i > 0 and nums[i] == nums[i - 1]:
            continue

        left, right = i + 1, n - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]
            if total == 0:
                result.append((nums[i], nums[left], nums[right]))
                while left < right and nums[left] == nums[left + 1]:
                    left += 1
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1
                left += 1
                right -= 1
            elif total < 0:
                left += 1
            else:
                right -= 1

    return result

结论

「三数之和」是一道经典算法题,它考察了三种不同的数据结构和算法。通过深入理解这三种解法,你不仅掌握了具体问题的解决技巧,更重要的是,培养了算法思维和问题分解的能力。在算法和数据结构的学习和应用中,不断探索、总结和提升,方能真正领悟算法的奥秘,并在解决复杂问题时游刃有余。

常见问题解答

1. 动态规划、哈希表和双指针算法,哪种更好?

这三种算法各有优劣。动态规划算法的时间复杂度最高,但它能够穷举所有可能的解,避免遗漏。哈希表算法的时间复杂度较低,但它需要额外的空间存储哈希表。双指针算法的时间复杂度最低,但它仅适用于排序后的数组。

2. 我应该优先学习哪种算法?

对于初学者,建议优先学习双指针算法,因为它相对简单易懂。掌握了双指针算法后,再深入学习哈希表和动态规划算法。

3. 如何提高算法解决问题的效率?

多加练习是提高算法解决问题效率的最佳方法。此外,还可以通过阅读算法书籍、参加算法竞赛和与其他算法爱好者交流来不断提升自己的算法思维。

4. 我可以在哪些场景中应用「三数之和」算法?

「三数之和」算法可以应用于各种场景,例如:

  • 在数据分析中,查找三个数字的和等于指定值的记录。
  • 在计算机图形学中,计算三角形的面积。
  • 在机器学习中,训练神经网络。

5. 我如何进一步学习算法和数据结构?

网上有许多免费和付费的资源可以帮助你学习算法和数据结构。一些流行的学习平台包括 Coursera、edX、LeetCode 和 HackerRank。