📅  最后修改于: 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 []