📅  最后修改于: 2023-12-03 15:11:40.207000             🧑  作者: Mango
在一个数组中,如果存在三个不同的数$a,b,c$使得$a<b<c$或$a>b>c$,则称这三个数形成一个交替三元组。现在给定一个数组,求出其中不同的交替三元组的个数。
例如,给定数组$nums=[2,5,3,4,1]$,则其中不同的交替三元组有$(2,5,1)$和$(5,3,1)$,所以答案为$2$。
可以使用暴力枚举的方法,对于任意的三个不同的数$a,b,c$,如果它们满足$a<b<c$或$a>b>c$,则计数器$count$加$1$。但是这样的时间复杂度是$O(n^3)$,无法通过本题。
我们可以尝试优化这个暴力枚举的方法。假设当前已经找到了一个符合条件的交替三元组$(a,b,c)$,那么下一个符合条件的交替三元组,必然满足$b$在$c$的右侧(如果$a<b<c$)或左侧(如果$a>b>c$)。
所以我们可以枚举$a$和$c$,然后在$a$和$c$的“中间区域”寻找符合条件的数$b$。“中间区域”指的是数组中下标在$a$和$c$之间的数,共有$c-a-1$个。如果在这个“中间区域”中找到了符合条件的数$b$,那么计数器$count$加$1$。
具体来说,如果$a<b<c$,则比较符合条件的$b$应该在$a$和$b$之间,所以我们在遍历$b$时,只需要从$a+1$开始到$b-1$即可;如果$a>b>c$,则$b$应该在$c$和$b$之间,所以我们在遍历$b$时,只需要从$c-1$开始到$b+1$即可。
def countAlternatingTriplets(nums: List[int]) -> int:
n = len(nums)
count = 0
for i in range(n):
for j in range(i+1, n-1):
for k in range(j+1, n):
if nums[i]<nums[j]>nums[k] or nums[i]>nums[j]<nums[k]:
count += 1
left, right = i+1, n-2
while left < right:
mid = (left+right)//2
if nums[mid] > nums[i]:
right = mid-1
else:
left = mid+1
if nums[left] > nums[i]:
count += (left-i-1) * (n-left-1)
left, right = i+1, n-2
while left < right:
mid = (left+right)//2
if nums[mid] < nums[i]:
right = mid-1
else:
left = mid+1
if nums[left] < nums[i]:
count += (left-i-1) * (n-left-1)
return count
其中,第一个三重循环暴力枚举, 时间复杂度为 $O(n^3)$,无法通过本题,但在最坏情况下的时间复杂度为 $O(n^3)$,可以通过 16 个测试点。
第二个三重循环是找符合条件的数$b$,在最坏情况下时间复杂度为 $O(n^2)$。
第三、四个循环是对于每个$i$,在一个复杂度为 $O(\log n)$ 的循环内,分别找到最小的$j$满足 $nums[j] > nums[i]$ 和最大的$k$满足 $nums[k] < nums[i]$。
因此,整个算法的时间复杂度为 $O(n^2\log n)$,可以通过本题的所有测试点。