数组题目:递增的三元子序列

文章目录

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

题目

标题和出处

标题:递增的三元子序列

出处:334. 递增的三元子序列

难度

6 级

题目描述

要求

给你一个整数数组 nums \texttt{nums} nums,如果存在这样的三元组下标 (i,   j,   k) \texttt{(i, j, k)} (i, j, k) 满足 i   <   j   <   k \texttt{i < j < k} i < j < k nums[i]   <   nums[j]   <   nums[k] \texttt{nums[i] < nums[j] < nums[k]} nums[i] < nums[j] < nums[k],返回 true \texttt{true} true;否则,返回 false \texttt{false} false

示例

示例 1:

输入: nums   =   [1,2,3,4,5] \texttt{nums = [1,2,3,4,5]} nums = [1,2,3,4,5]
输出: true \texttt{true} true
解释:任何 i   <   j   <   k \texttt{i < j < k} i < j < k 的三元组都满足题意。

示例 2:

输入: nums   =   [5,4,3,2,1] \texttt{nums = [5,4,3,2,1]} nums = [5,4,3,2,1]
输出: false \texttt{false} false
解释:不存在满足题意的三元组。

示例 3:

输入: nums   =   [2,1,5,0,4,6] \texttt{nums = [2,1,5,0,4,6]} nums = [2,1,5,0,4,6]
输出: true \texttt{true} true
解释:三元组 (3,   4,   5) \texttt{(3, 4, 5)} (3, 4, 5) 满足题意,因为 nums[3]   =   0   <   nums[4]   =   4   <   nums[5]   =   6 \texttt{nums[3] = 0 < nums[4] = 4 < nums[5] = 6} nums[3] = 0 < nums[4] = 4 < nums[5] = 6

数据范围

  • 1 ≤ nums.length ≤ 10 5 \texttt{1} \le \texttt{nums.length} \le \texttt{10}^\texttt{5} 1nums.length105
  • -2 31 ≤ nums[i] ≤ 2 31 − 1 \texttt{-2}^\texttt{31} \le \texttt{nums[i]} \le \texttt{2}^\texttt{31}-\texttt{1} -231nums[i]2311

进阶

你能实现时间复杂度为 O(n) \texttt{O(n)} O(n),空间复杂度为 O(1) \texttt{O(1)} O(1) 的解决方案吗?

解法

思路和算法

这道题中,数组 nums \textit{nums} nums 的长度最大为 5 × 1 0 5 5 \times 10^5 5×105,暴力解法会超出时间限制,必须使用复杂度较低的解法。

由于题目要寻找递增的三元子序列,只有当数组的长度大于等于 3 3 3 时,才存在三元子序列,因此当数组的长度小于 3 3 3 时,直接返回 false \text{false} false 即可。

当数组的长度大于等于 3 3 3 时,只需要遍历数组一次,使用 O ( n ) O(n) O(n) 时间和 O ( 1 ) O(1) O(1) 的空间即可判断是否存在递增的三元子序列。维护两个变量 firstNum \textit{firstNum} firstNum secondNum \textit{secondNum} secondNum 分别存储递增三元子序列的第一个数和第二个数,任何时候都满足 firstNum < secondNum \textit{firstNum}<\textit{secondNum} firstNum<secondNum,如果遍历过程中遇到一个大于 secondNum \textit{secondNum} secondNum 的元素,即找到了一个递增三元子序列。

具体做法是,初始化 firstNum = nums [ 0 ] \textit{firstNum}=\textit{nums}[0] firstNum=nums[0] secondNum = + ∞ \textit{secondNum}=+\infty secondNum=+(代码中使用整型的最大值表示 + ∞ +\infty +)。由于 nums [ 0 ] \textit{nums}[0] nums[0] 已经赋值给 firstNum \textit{firstNum} firstNum,因此从下标 1 1 1 开始遍历数组 nums \textit{nums} nums。对于遍历到的元素 num \textit{num} num,进行如下操作:

  • 如果 num > secondNum \textit{num}>\textit{secondNum} num>secondNum,则 ( firstNum , secondNum , num ) (\textit{firstNum},\textit{secondNum},\textit{num}) (firstNum,secondNum,num) 即为同时满足下标递增和元素值递增的递增三元子序列,返回 true \text{true} true

  • 如果 firstNum < num ≤ secondNum \textit{firstNum}<\textit{num} \le \textit{secondNum} firstNum<numsecondNum,则如果在 num \textit{num} num 的后面存在 num 2 \textit{num}_2 num2 满足 num 2 > num \textit{num}_2>\textit{num} num2>num,则 ( firstNum , num , num 2 ) (\textit{firstNum},\textit{num},\textit{num}_2) (firstNum,num,num2) 即为一个递增三元子序列,由于 num ≤ secondNum \textit{num} \le \textit{secondNum} numsecondNum,因此将 num \textit{num} num 的值赋给 secondNum \textit{secondNum} secondNum,只要在 num \textit{num} num 的后面找到一个元素大于更新后的 secondNum \textit{secondNum} secondNum,即找到一个递增三元子序列;

  • 如果 num ≤ firstNum \textit{num} \le \textit{firstNum} numfirstNum,则将 num \textit{num} num 的值赋给 firstNum \textit{firstNum} firstNum

上述做法中,如果找到一个递增三元子序列,则三个元素值一定是递增顺序,那么下标是否也满足递增顺序?答案是肯定的。理由如下:

  • 当找到 num > secondNum \textit{num}>\textit{secondNum} num>secondNum 时, num \textit{num} num 一定在 secondNum \textit{secondNum} secondNum 的后面,即 num \textit{num} num 的下标一定大于 secondNum \textit{secondNum} secondNum 的下标;

  • secondNum \textit{secondNum} secondNum 被更新时,更新后的 secondNum \textit{secondNum} secondNum 一定在 firstNum \textit{firstNum} firstNum 的后面,即 secondNum \textit{secondNum} secondNum 的下标一定大于 firstNum \textit{firstNum} firstNum 的下标;

  • firstNum \textit{firstNum} firstNum 被更新时,虽然更新后的 firstNum \textit{firstNum} firstNum secondNum \textit{secondNum} secondNum 的后面,但是更新前的 firstNum ′ \textit{firstNum}' firstNum 满足 firstNum ′ < secondNum \textit{firstNum}'<\textit{secondNum} firstNum<secondNum firstNum ′ \textit{firstNum}' firstNum secondNum \textit{secondNum} secondNum 的前面,即 firstNum ′ \textit{firstNum}' firstNum 的下标小于 secondNum \textit{secondNum} secondNum 的下标。

因此,虽然没有记录三个元素的具体下标值,但是可以保证三个元素的下标满足递增顺序。

代码

class Solution {
    public boolean increasingTriplet(int[] nums) {
        int length = nums.length;
        if (length < 3) {
            return false;
        }
        int firstNum = nums[0], secondNum = Integer.MAX_VALUE;
        for (int i = 1; i < length; i++) {
            int num = nums[i];
            if (num > secondNum) {
                return true;
            } else if (num > firstNum) {
                secondNum = num;
            } else {
                firstNum = num;
            }
        }
        return false;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 nums \textit{nums} nums 的长度。遍历数组 nums \textit{nums} nums 一次,遍历过程中维护 firstNum \textit{firstNum} firstNum secondNum \textit{secondNum} secondNum 的值,对于每个位置,维护两个变量的时间复杂度是 O ( 1 ) O(1) O(1),因此总时间复杂度是 O ( n ) O(n) O(n)

  • 空间复杂度: O ( 1 ) O(1) O(1)

你可能感兴趣的:(数据结构和算法,#,数组和字符串,数组)