难度:⭐
题目描述:
给你一个整数数组 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:
1 | 输入:arr = [3,0,1,1,9,7], a = 7, b = 2, c = 3 |
示例2:
1 | 输入:arr = [1,1,2,2,3], a = 0, b = 0, c = 1 |
提示:
3 <= arr.length <= 100
0 <= arr[i] <= 1000
0 <= a, b, c <= 1000
解题过程:
思路:
暴力解法,三层for循环遍历,每层代表一个三元组中的元素。对每组(i,j,k),判断arr[i]、arr[j]、arr[k]是否满足条件。
这种解法时间复杂度是$O(n^3)$,看题解有没有好方法吧。
c++代码:(执行用时64ms,击败14.93%,内存消耗8.6M,击败5.00%)
1 | class Solution { |
官方题解:
方法一:枚举
思路与算法
用 $O(n^3)$ 的循环依次枚举所有的 $(i, j, k)$,这里 $0 \leq i < j < k < {\rm arr.length}$,对于每组 (i, j, k)(i,j,k),判断 ${\rm arr}[i]$、${\rm arr}[j]$、${\rm arr}[k]$是否满足条件。
最终统计出所有满足条件的三元组的数量。
c++代码:(执行56ms,击败38.19%,内存8.4M,击败9.00%)
1 | class Solution { |
复杂度分析
- 时间复杂度:$O(n^3)$,其中 $n$是数组 $\textit{arr}$ 的长度。
- 空间复杂度:$O(1)$。
方法二:枚举优化
思路与算法
我们考虑 $O(n^2)$ 枚举满足 $|\rm arr[j]-\rm arr[k]|\le b∣$ 的二元组 $(j,k)$,统计这个二元组下有多少 $i$ 满足条件。由题目已知 $i$ 的限制条件为 $|\rm arr[i]-\rm arr[j]|\le a \ \&\&\ |\rm arr[i]-\rm arr[k]|\le c∣$,我们可以拆开绝对值,得到符合条件的值一定是 $[\rm arr[j]-a,\rm arr[j]+a]$ 和 $[\rm arr[k]-c,\rm arr[k]+c]$ 两个区间的交集,我们记为 $[l,r]$。因此,在枚举 $(j,k)$这个二元组的时候,我们只需要快速统计出满足 $i<j$ 且 $\rm arr[i]$的值域范围在 $[l,r]$ 的 $i$ 的个数即可。
很容易想到维护一个 $\rm arr[i]$频次数组的前缀和 $\rm sum$,对于一个二元组 $(j,k)$,我们可以 $O(1)$ 得到答案为 $\rm sum[r]-\rm sum[l-1]$。考虑怎么维护保证当前频次数组存的数的下标符合 $i<j$ 的限制,我们只要从小到大枚举 $j$,每次 $j$移动指针加一的时候,将 $\rm arr[j]$的值更新到 $\rm sum$数组中即可,这样能保证枚举到 $j$的时候 $\rm sum$数组里存的值的下标满足限制。
「将 $\rm arr[j]$ 的值更新到 $\rm sum$数组中」这个操作在本方法中是暴力更新,因为数组的值域上限很小,有能力的读者可以考虑怎么在进一步优化这一部分的复杂度,可以从离散化或者树状数组的角度考虑,这里不再赘述。
c++代码:(执行24ms,击败97.15%,内存9M,击败5.00%)
1 | class Solution { |
复杂度分析
时间复杂度:$O(n^2+nS)$,其中$n$ 是数组 $\textit{arr}$的长度,$S$为数组的值域上限,这里为 $1000$。
空间复杂度:$O(S)$。我们需要 $O(S)$ 的空间维护 $\rm arr[i]$频次数组的前缀和。
总结:
基本上简单题都可以通过暴力解出来,看来是真的😄,官方题解提供了另一种优化的枚举解法,可以学习一波。