📌  相关文章
📜  在数组中找到一个三元组,使得 arr[i] arr[k] 和 i < j < k(1)

📅  最后修改于: 2023-12-03 14:51:27.800000             🧑  作者: Mango

寻找三元组
问题描述

给定一个包含 n 个整数的数组 arr,要求寻找其中满足 i < j < k 且 arr[i] < arr[k] < arr[j] 的三元组(i,j,k)。

思路分析

暴力枚举所有三元组肯定是可行的,时间复杂度为 O(n^3)。但是本题有更优秀的解法。

考虑对于每个位置 i,如何找到它左边第一个比它小的数 left,以及右边第一个比它大的数 right。如果 left 和 right 都存在,那么只需要检查是否有 left < i < right 就可以判断是否存在所求的三元组。

如何找到 left 和 right 呢?可以使用单调栈的思路,维护一个单调递减的下标栈,遍历整个数组。如果当前值比栈顶元素(即 arr[top])小,则直接将当前下标入栈;否则就不断弹出栈顶元素,直到栈为空或者栈顶元素变得小于当前值。这样,对于任意一个下标 i,其左边第一个比它小的值就是栈中 i 左边的那个元素,而其右边第一个比它大的值就是当前的栈顶元素。

上述思路只能找到所有符合 arr[i] < arr[k] 的三元组,如何进一步判断 arr[k] < arr[j] 呢?可以从右往左遍历数组,同时维护一个变量 max,表示 arr[j] 的最大值(按照上述方法 max 的值也是单调递减的)。对于每个下标 k,如果它满足 arr[k] < max,那么就找到了一个符合条件的三元组。

整体算法的时间复杂度为 O(n)。

代码实现
def find_triplet(arr):
    left = [-1] * len(arr)
    right = [-1] * len(arr)
    stack = []
    for i in range(len(arr)):
        while stack and arr[stack[-1]] >= arr[i]:
            stack.pop()
        if stack:
            left[i] = stack[-1]
        stack.append(i)
    stack = []
    for i in reversed(range(len(arr))):
        while stack and arr[stack[-1]] <= arr[i]:
            stack.pop()
        if stack:
            right[i] = stack[-1]
        stack.append(i)
    max_val = float('-inf')
    for k in range(len(arr)):
        if left[k] != -1 and right[k] != -1 and arr[left[k]] < arr[k] < arr[right[k]] < max_val:
            return [left[k], k, right[k]]
        max_val = max(max_val, arr[k])
    return []