三数之和问题题解 | 豆包MarsCode AI刷题

127 阅读4分钟

即便这道题是困难题,但是看到这个题目,我就觉得难不到哪里去。至少题目可以看得懂。不像某些题,不仅没思路,甚至题目都看不懂。好了废话不多说,请看题解

  • 题目:这道题的标签是#双指针,题目难度为:困难 题目概述:小U有一个整数数组 arr,他希望找到其中三个元素 i, j, k 满足条件 i < j < k 且 arr[i] + arr[j] + arr[k] == target。由于可能存在大量的元组,结果需要对 10^9 + 7 取模。
  • 题目分析:首先看到的就是要求找到三个下标,满足i<j<k,说白了,就是三个指针不能指向同一个元素。然后告诉你要求arr[i] + arr[j] + arr[k] == target
  • 思路:一开始我的思路就是循环遍历三次,ijk不能一样,值相加为target,如果判断为真,那么就ans+=1;如果这样能过的话这道题就不配是困难题了。肯定要想办法优化。
  • 优化点1:降低循环次数,本题是标题为双指针。肯定要想着用双指针的方法优化,第一步肯定是先给arr排个序,然后再循环遍历一次arr,最后用双指针左右遍历,找到每一个符合条件的元组。
  • 优化点2:去重,有例子可知,arr中可能存在一样的元素,如果i遍历一样的就没有意义了,同理left and right 指针遍历一样的也没有意义了。所以要避免这种事情发生,这里我用到了python的counter方法。(counter方法就是统计一个数组中每一个元素的数量,然后以字典的形式返回)

好了题目差不多分析完了,接下来是代码细节了

  • 我在编写代码的时候犯了一个错误,一开始我就用counter函数将arr统计了一遍,最直观的就是例2出现了重复的情况。在第一层循环我们就一一枚举了i,如果一开始就统计一遍,那么前面枚举的i也会算进去,所以说要在第一层循环内进行counter,并且counter i后面的那些
  • 然后在双指针查找到了left与right后要分两种情况,第一个就是当left与right指针指向的数组值相等时,count为该元素的个数排列组合选两个,第二种情况就是当left与right指针指向的数组值不相等时,count为counter[arr[left]]*counter[arr[right]] 好了细节也说完了,最后就是代码实现。注释非常详细
from collections import Counter

def solution(arr: list, t: int) -> int:
    MOD = 10**9 + 7  # 定义模数,用于防止结果溢出
    arr.sort()  # 对数组进行排序,方便后续的双指针操作
    ans = 0  # 初始化答案为0
    n = len(arr)  # 获取数组的长度

    for i in range(n - 2):  # 遍历数组的前 n-2 个元素
        counter = Counter(arr[i+1:])  # 计算从当前元素之后的所有元素的出现次数
        left, right = i + 1, n - 1  # 初始化双指针,left 指向当前元素之后的第一个元素,right 指向数组的最后一个元素

        while left < right:  # 当左指针小于右指针时,继续循环
            current_sum = arr[i] + arr[left] + arr[right]  # 计算当前三元组的和
            # print(arr[i], arr[left], arr[right])  # 打印当前三元组(调试用)

            if current_sum == t:  # 如果当前三元组的和等于目标值 t
                if arr[left] == arr[right]:  # 如果 left 和 right 指向相同的元素
                    # 计算组合数,即从 left 到 right 之间选择两个相同元素的组合数
                    count = (counter[arr[left]] - 1) * counter[arr[left]] // 2
                    ans = (ans + count) % MOD  # 更新答案,并取模
                    break  # 跳出循环,因为已经找到了所有可能的组合
                else:  # 如果 left 和 right 指向不同的元素
                    # 计算 left 和 right 指向不同元素的情况
                    left_count = counter[arr[left]]
                    right_count = counter[arr[right]]
                    ans = (ans + left_count * right_count) % MOD  # 更新答案,并取模
                    left += left_count  # 移动左指针,跳过所有相同的 left 元素
                    right -= right_count  # 移动右指针,跳过所有相同的 right 元素
            elif current_sum < t:  # 如果当前三元组的和小于目标值 t
                left += counter[arr[left]]  # 移动左指针,跳过所有相同的 left 元素
            else:  # 如果当前三元组的和大于目标值 t
                right -= counter[arr[right]]  # 移动右指针,跳过所有相同的 right 元素

    return ans  # 返回最终答案