📌  相关文章
📜  给定数组中不同的交替三元组索引的计数(1)

📅  最后修改于: 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)$,可以通过本题的所有测试点。