单调栈题目:132 模式

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
  • 解法
    • 思路和算法
    • 证明
    • 代码
    • 复杂度分析

题目

标题和出处

标题:132 模式

出处:456. 132 模式

难度

6 级

题目描述

要求

给你一个整数数组 nums \texttt{nums} nums,数组中共有 n \texttt{n} n 个整数。 132 \texttt{132} 132 模式的子序列由三个整数 nums[i] \texttt{nums[i]} nums[i] nums[j] \texttt{nums[j]} nums[j] nums[k] \texttt{nums[k]} nums[k] 组成,并同时满足: i   <   j   <   k \texttt{i < j < k} i < j < k nums[i]   <   nums[k]   <   nums[j] \texttt{nums[i] < nums[k] < nums[j]} nums[i] < nums[k] < nums[j]

如果 nums \texttt{nums} nums 中存在 132 \texttt{132} 132 模式的子序列,返回 true \texttt{true} true;否则,返回 false \texttt{false} false

示例

示例 1:

输入: nums   =   [1,2,3,4] \texttt{nums = [1,2,3,4]} nums = [1,2,3,4]
输出: false \texttt{false} false
解释:序列中不存在 132 \texttt{132} 132 模式的子序列。

示例 2:

输入: nums   =   [3,1,4,2] \texttt{nums = [3,1,4,2]} nums = [3,1,4,2]
输出: true \texttt{true} true
解释:序列中有 1 \texttt{1} 1 132 \texttt{132} 132 模式的子序列: [1,   4,   2] \texttt{[1, 4, 2]} [1, 4, 2]

示例 3:

输入: nums   =   [-1,3,2,0] \texttt{nums = [-1,3,2,0]} nums = [-1,3,2,0]
输出: true \texttt{true} true
解释:序列中有 3 \texttt{3} 3 132 \texttt{132} 132 模式的的子序列: [-1,   3,   2] \texttt{[-1, 3, 2]} [-1, 3, 2] [-1,   3,   0] \texttt{[-1, 3, 0]} [-1, 3, 0] [-1,   2,   0] \texttt{[-1, 2, 0]} [-1, 2, 0]

数据范围

  • n = nums.length \texttt{n} = \texttt{nums.length} n=nums.length
  • 1 ≤ n ≤ 2 × 10 5 \texttt{1} \le \texttt{n} \le \texttt{2} \times \texttt{10}^\texttt{5} 1n2×105
  • -10 9 ≤ nums[i] ≤ 10 9 \texttt{-10}^\texttt{9} \le \texttt{nums[i]} \le \texttt{10}^\texttt{9} -109nums[i]109

解法

思路和算法

这道题要求判断长度为 n n n 的数组 nums \textit{nums} nums 中是否存在下标三元组 ( i , j , k ) (i, j, k) (i,j,k) 满足 i < j < k i < j < k i<j<k nums [ i ] < nums [ k ] < nums [ j ] \textit{nums}[i] < \textit{nums}[k] < \textit{nums}[j] nums[i]<nums[k]<nums[j]

由于三元组的最小下标 i i i 对应最小的元素,因此可以在枚举 i i i 的同时寻找符合要求的下标 j j j k k k

对于下标 j j j k k k,如果满足 j < k j < k j<k nums [ j ] > nums [ k ] \textit{nums}[j] > \textit{nums}[k] nums[j]>nums[k],则只要存在一个下标 i i i 满足 i < j i < j i<j nums [ i ] < nums [ k ] \textit{nums}[i] < \textit{nums}[k] nums[i]<nums[k],即存在满足要求的下标三元组 ( i , j , k ) (i, j, k) (i,j,k)。因此,可以记录数组 nums \textit{nums} nums 的每个前缀的最小值。

创建长度为 n n n 的数组 minArray \textit{minArray} minArray,对于任意 0 ≤ i < n 0 \le i < n 0i<n minArray [ i ] \textit{minArray}[i] minArray[i] 表示数组 nums \textit{nums} nums 从下标 0 0 0 到下标 i i i 的范围内的最小值。数组 minArray \textit{minArray} minArray 中的元素计算方法是:

  • minArray [ 0 ] = nums [ 0 ] \textit{minArray}[0] = \textit{nums}[0] minArray[0]=nums[0]

  • 对于 1 ≤ i < n 1 \le i < n 1i<n minArray [ i ] = min ⁡ ( minArray [ i − 1 ] , nums [ i ] ) \textit{minArray}[i] = \min(\textit{minArray}[i - 1], \textit{nums}[i]) minArray[i]=min(minArray[i1],nums[i])

根据数组 minArray \textit{minArray} minArray 的定义可知,对于任意 1 ≤ i < n 1 \le i < n 1i<n minArray [ i − 1 ] ≥ minArray [ i ] \textit{minArray}[i - 1] \ge \textit{minArray}[i] minArray[i1]minArray[i],因此 minArray \textit{minArray} minArray 的元素具有非严格单调性,从左到右遍历时单调递减,从右到左遍历时单调递增。

得到数组 minArray \textit{minArray} minArray 之后,从右到左遍历数组 nums \textit{nums} nums,寻找满足条件的下标三元组。遍历过程中维护单调栈,单调栈存储数组 nums \textit{nums} nums 的元素,满足从栈底到栈顶的元素单调递减(非严格)。

记当前下标为 j j j,需要寻找小于 j j j 的下标 i i i 和大于 j j j 的下标 k k k,满足 nums [ i ] < nums [ k ] < nums [ j ] \textit{nums}[i] < \textit{nums}[k] < \textit{nums}[j] nums[i]<nums[k]<nums[j]。具体做法如下:

  1. 如果栈不为空且栈顶元素小于或等于 minArray [ j ] \textit{minArray}[j] minArray[j],则将栈顶元素出栈,重复该操作直到栈为空或栈顶元素大于 minArray [ j ] \textit{minArray}[j] minArray[j]

  2. 如果栈不为空且栈顶元素小于 nums [ j ] \textit{nums}[j] nums[j],则返回 true \text{true} true,否则将 nums [ j ] \textit{nums}[j] nums[j] 入栈。

遍历结束时,如果没有发现满足要求的下标三元组,返回 false \text{false} false

证明

对于上述解法的正确性,需要从两个方面证明,一是只有当找到符合要求的下标三元组时才会返回 true \text{true} true,二是只要存在符合要求的下标三元组就一定会返回 true \text{true} true

由于对单调栈操作时是从右到左遍历数组 nums \textit{nums} nums,因此当遍历到下标 j j j 时,单调栈内的元素一定是 nums [ j ] \textit{nums}[j] nums[j] 右边的元素,即单调栈内的元素在数组中的下标一定大于 j j j。当单调栈不为空时,假设单调栈的栈顶元素在数组中对应下标 k k k,即栈顶元素是 nums [ k ] \textit{nums}[k] nums[k],则必有 j < k j < k j<k。又由于当遍历到下标 j j j 时,如果单调栈不为空,则单调栈的栈顶元素一定大于 minArray [ j ] \textit{minArray}[j] minArray[j](否则栈顶元素出栈),因此当单调栈不为空时,一定存在下标 i i i,使得 i ≤ j < k i \le j < k ij<k nums [ i ] = minArray [ j ] < nums [ k ] \textit{nums}[i] = \textit{minArray}[j] < \textit{nums}[k] nums[i]=minArray[j]<nums[k]。如果 nums [ j ] \textit{nums}[j] nums[j] 大于栈顶元素,则 nums [ j ] > nums [ k ] \textit{nums}[j] > \textit{nums}[k] nums[j]>nums[k],根据传递性可知 nums [ j ] > nums [ k ] > nums [ i ] \textit{nums}[j] > \textit{nums}[k] > \textit{nums}[i] nums[j]>nums[k]>nums[i],因此 i ≠ j i \ne j i=j,必有 i < j i < j i<j,此时找到一个下标三元组 ( i , j , k ) (i, j, k) (i,j,k),满足 i < j < k i < j < k i<j<k nums [ i ] < nums [ k ] < nums [ j ] \textit{nums}[i] < \textit{nums}[k] < \textit{nums}[j] nums[i]<nums[k]<nums[j],该下标三元组满足 132 132 132 模式,返回 true \text{true} true

由于当遍历到下标 j j j 时,单调栈内的元素一定大于 minArray [ j ] \textit{minArray}[j] minArray[j],且当 nums [ j ] \textit{nums}[j] nums[j] 大于栈顶元素时即返回 true \text{true} true,因此只有当 nums [ j ] \textit{nums}[j] nums[j] 小于等于栈顶元素时才会将 nums [ j ] \textit{nums}[j] nums[j] 入栈,可知单调栈满足从栈底到栈顶的元素单调递减(非严格)。

根据数组 minArray \textit{minArray} minArray 的定义可知,对于任意 1 ≤ i < n 1 \le i < n 1i<n minArray [ i − 1 ] ≥ minArray [ i ] \textit{minArray}[i - 1] \ge \textit{minArray}[i] minArray[i1]minArray[i],因此在从右到左遍历数组 nums \textit{nums} nums 的过程中,下标 j j j 递减,访问到的 minArray [ j ] \textit{minArray}[j] minArray[j] 的值递增(非严格)。由于单调栈内的元素必须大于 minArray [ j ] \textit{minArray}[j] minArray[j],因此在从右到左遍历过程中,单调栈的栈顶元素允许的最小值在递增,如果单调栈不为空,则栈顶元素 nums [ k ] \textit{nums}[k] nums[k] 一定是数组 nums \textit{nums} nums 位于下标 j j j 右边的元素中的大于 minArray [ j ] \textit{minArray}[j] minArray[j] 的最小元素,只要 nums [ j ] > nums [ k ] \textit{nums}[j] > \textit{nums}[k] nums[j]>nums[k] 即找到满足要求的下标三元组。如果此时单调栈为空,则不存在满足 j < k j < k j<k minArray [ j ] < nums [ k ] \textit{minArray}[j] < \textit{nums}[k] minArray[j]<nums[k] 的下标 k k k,因此不存在满足要求的下标三元组;如果此时 nums [ j ] \textit{nums}[j] nums[j] 小于等于单调栈的栈顶元素,则虽然存在下标 i i i k k k 满足 i < j < k i < j < k i<j<k nums [ i ] < nums [ k ] \textit{nums}[i] < \textit{nums}[k] nums[i]<nums[k],但是当前下标 j j j 不满足 nums [ j ] > nums [ k ] \textit{nums}[j] > \textit{nums}[k] nums[j]>nums[k],因此不存在满足要求的下标三元组。根据上述分析可知,对于当前下标 j j j,如果存在满足要求的下标三元组则不会遗漏,如果没有找到将当前下标 j j j 作为中间下标的下标三元组则说明当前下标 j j j 无法作为任何一个下标三元组中的 j j j

代码

class Solution {
    public boolean find132pattern(int[] nums) {
        int n = nums.length;
        int[] minArray = new int[n];
        minArray[0] = nums[0];
        for (int i = 1; i < n; i++) {
            minArray[i] = Math.min(minArray[i - 1], nums[i]);
        }
        Deque<Integer> stack = new ArrayDeque<Integer>();
        for (int j = n - 1; j >= 0; j--) {
            int num = nums[j];
            while (!stack.isEmpty() && stack.peek() <= minArray[j]) {
                stack.pop();
            }
            if (!stack.isEmpty() && stack.peek() < num) {
                return true;
            }
            stack.push(num);
        }
        return false;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。需要从左到右遍历数组 nums \textit{nums} nums 计算数组 minArray \textit{minArray} minArray 的值,然后从右到左遍历 nums \textit{nums} nums 寻找满足要求的下标三元组,由于每个元素最多入栈和出栈各一次,因此时间复杂度是 O ( n ) O(n) O(n)

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。空间复杂度主要取决于数组 minArray \textit{minArray} minArray 和栈空间,数组 minArray \textit{minArray} minArray 的长度是 n n n,栈内元素个数不会超过 n n n

你可能感兴趣的:(数据结构和算法,#,栈和队列,栈,单调栈)