持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情
一、题目描述:
给你一个整数数组 arr ,以及 a、b 、c 三个整数。请你统计其中好三元组的数量。
如果三元组 (arr[i], arr[j], arr[k]) 满足下列全部条件,则认为它是一个 好三元组 。
- 0 <= i < j < k < arr.length
- |arr[i] - arr[j]| <= a
- |arr[j] - arr[k]| <= b
- |arr[i] - arr[k]| <= c
其中 |x| 表示 x 的绝对值。
返回 好三元组的数量 。
示例 1:
输入:arr = [3,0,1,1,9,7], a = 7, b = 2, c = 3
输出:4
解释:一共有 4 个好三元组:[(3,0,1), (3,0,1), (3,1,1), (0,1,1)] 。
示例 2:
输入:arr = [1,1,2,2,3], a = 0, b = 0, c = 1
输出:0
解释:不存在满足所有条件的三元组。
提示:
- 3 <= arr.length <= 100
- 0 <= arr[i] <= 1000
- 0 <= a, b, c <= 1000
二、思路分析:
暴力法 没啥好说的
太简单了,完全没啥好说的,时间复杂度大概在O(n^3)O(n3)。
看了看题解区没找到精巧的解法,学习了提交记录排行中速度排前几名的思路。
j和k的遍历是无法避免的,而当这两者确定后,i的选择有些许搞头
如果提前将每个位置之前所有位置的数进行排序,那么arr[j]和arr[k]都确定后
根据a和c算出上下界后,可以通过二分查找从已排序的数列内确定满足要求的arr[i]
排序的时间复杂度,对n个位置遍历显然低于,二分查找
也低于原有的k次判断,所以时间效率上有提升
但是当数组很长时,第一步存的排序结果会占用大量空间。
三、AC 代码:
from bisect import bisect_left, bisect_right
class Solution:
def countGoodTriplets(self, arr: List[int], a: int, b: int, c: int) -> int:
res = 0
sorti = [sorted(arr[:i]) for i in range(len(arr))]
for k in range(2, len(arr)):
ak = arr[k]
for j in range(1, k):
aj = arr[j]
if abs(ak - aj) > b:
continue
prevj = sorti[j]
L, R = max(aj - a, ak - c), min(aj + a, ak + c)
if L > R:
continue
iL = bisect.bisect_left(prevj, L)
iR = bisect.bisect_right(prevj, R, iL)
res += iR-iL
return res
四、总结:
一次典型的优化提速:空间换时间