即便这道题是困难题,但是看到这个题目,我就觉得难不到哪里去。至少题目可以看得懂。不像某些题,不仅没思路,甚至题目都看不懂。好了废话不多说,请看题解
- 题目:这道题的标签是#双指针,题目难度为:困难 题目概述:小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 # 返回最终答案