【数据结构和算法】递增的三元子序列

简介: 给你一个整数数组nums,判断这个数组中是否存在长度为3的递增子序列。如果存在这样的三元组下标(i, j, k)且满足i < j < k,使得nums[i] < nums[j] < nums[k],返回true;否则,返回false。

其他系列文章导航

Java基础合集

数据结构与算法合集

设计模式合集

多线程合集

分布式合集

ES合集


文章目录

其他系列文章导航

文章目录

前言

一、题目描述

二、题解

2.1 方法一:贪心 + 二分

2.2 方法二:贪心(优化)

三、代码

3.1 方法一:贪心 + 二分

3.2 方法二:贪心(优化)

四、复杂度分析

4.1 方法一:贪心 + 二分

4.2 方法二:贪心(优化)


前言

这是力扣的334题,难度为中等,解题方案有很多种,本文讲解我认为最奇妙的两种。


一、题目描述

给你一个整数数组 nums ,判断这个数组中是否存在长度为 3 的递增子序列。

如果存在这样的三元组下标 (i, j, k) 且满足 i < j < k ,使得 nums[i] < nums[j] < nums[k] ,返回 true ;否则,返回 false

示例 1:

输入:nums = [1,2,3,4,5]

输出:true

解释:任何 i < j < k 的三元组都满足题意


示例 2:

输入:nums = [5,4,3,2,1]

输出:false

解释:不存在满足题意的三元组

示例 3:

输入:nums = [2,1,5,0,4,6]

输出:true

解释:三元组 (3, 4, 5) 满足题意,因为 nums[3] == 0 < nums[4] == 4 < nums[5] == 6


提示:

    • 1 <= nums.length <= 5 * 105
    • -231 <= nums[i] <= 231 - 1

    进阶:你能实现时间复杂度为 O(n) ,空间复杂度为 O(1) 的解决方案吗?


    二、题解

    题目要我们判断是否存在长度为 3 的上升子序列,问题可以转换为求 nums 的最长上升子序列的长度。

    如果 nums 的最长上升子序列长度大于等于 3,那么原问题答案为 True,否则为 False。

    2.1 方法一:贪心 + 二分

    思路与算法:

    简单来说,就是在遍历每个数 nums[i] 的同时,维护一个具有单调性的 f[ ] 数组,其中 f[len]=x 代表长度为 len 的最长上升子序列最小结尾元素为 x,可以证明 f 数组具有单调性,因此每次可以通过二分找到小于 nums[i] 的最大下标,来作为 nums[i] 的前一个数。

    综上,我们求得最长上升子序列的最大长度,然后和 3 比较即可得出答案。

    2.2 方法二:贪心(优化)

    方法二达到了进阶的要求!

    思路与算法:

    我们可以对 f 数组进行优化:使用有限变量进行替换(将 f 数组的长度压缩为 2),数组含义不变,f[1]=x 代表长度为 1 的上升子序列最小结尾元素为 x,f[2]=y 代表长度为 2 的上升子序列的最小结尾元素为 y。

    从前往后扫描每个 nums[i],每次将 nums[i] 分别与 f[1]] 和 f[2] 进行比较,如果能够满足 nums[i]>f[2],代表 nums[i] 能够接在长度为 2 的上升子序列的后面,直接返回 True,否则尝试使用 nums[i] 来更新 f[1] 和 f[2]。

    这样,我们只使用了有限变量,每次处理 nums[i] 只需要和有限变量进行比较。


    三、代码

    3.1 方法一:贪心 + 二分

    Java版本:

    class Solution {
        public boolean increasingTriplet(int[] nums) {
            int n = nums.length, ans = 1;
            int[] f = new int[n + 1];
            Arrays.fill(f, Integer.MAX_VALUE);
            for (int i = 0; i < n; i++) {
                int t = nums[i];
                int l = 1, r = i + 1;
                while (l < r) {
                    int mid = l + r >> 1;
                    if (f[mid] >= t) {
                        r = mid;
                    } else {
                        l = mid + 1;
                    }
                }
                f[r] = t;
                ans = Math.max(ans, r);
            }
            return ans >= 3;
        }
    }

    image.gif

    C++版本:

    #include <vector>
    #include <climits>
    using namespace std;
    class Solution {
    public:
        bool increasingTriplet(vector<int>& nums) {
            int n = nums.size(), ans = 1;
            vector<int> f(n + 1, INT_MAX);
            for (int i = 0; i < n; i++) {
                int t = nums[i];
                int l = 1, r = i + 1;
                while (l < r) {
                    int mid = l + (r - l) / 2;
                    if (f[mid] >= t) {
                        r = mid;
                    } else {
                        l = mid + 1;
                    }
                }
                f[r] = t;
                ans = max(ans, r);
            }
            return ans >= 3;
        }
    };

    image.gif

    Python版本:

    def increasingTriplet(nums):
        n = len(nums)
        ans = 1
        f = [float('inf')] * (n + 1)
        for i in range(n):
            t = nums[i]
            l, r = 1, i + 1
            while l < r:
                mid = (l + r) // 2
                if f[mid] >= t:
                    r = mid
                else:
                    l = mid + 1
            f[r] = t
            ans = max(ans, r)
        return ans >= 3

    image.gif

    3.2 方法二:贪心(优化)

    Java版本:

    class Solution {
        public boolean increasingTriplet(int[] nums) {
            int n = nums.length;
            long[] f = new long[3];
            f[1] = f[2] = Integer.MAX_VALUE;
            for (int t : nums) {
                if (f[2] < t) {
                    return true;
                } else if (f[1] < t && t < f[2]) {
                    f[2] = t;
                } else if (f[1] > t) {
                    f[1] = t;
                }
            }
            return false;
        }
    }

    image.gif

    C++版本:

    class Solution {
    public:
        bool increasingTriplet(vector<int>& nums) {
            int n = nums.size();
            long long f[3] = {INT_MAX, INT_MAX, INT_MAX};
            for (int t : nums) {
                if (f[2] < t) {
                    return true;
                } else if (f[1] < t && t < f[2]) {
                    f[2] = t;
                } else if (f[1] > t) {
                    f[1] = t;
                }
            }
            return false;
        }
    };

    image.gif

    Python版本:

    class Solution:
        def increasingTriplet(self, nums: List[int]) -> bool:
            n = len(nums)
            f = [float('inf'), float('inf'), float('inf')]
            for t in nums:
                if f[2] < t:
                    return True
                elif f[1] < t < f[2]:
                    f[2] = t
                elif f[1] > t:
                    f[1] = t
            return False

    image.gif


    四、复杂度分析

    4.1 方法一:贪心 + 二分

      • 时间复杂度:O(nlog⁡n)
      • 空间复杂度:O(n)

      4.2 方法二:贪心(优化)

        • 时间复杂度:O(n)
        • 空间复杂度:O(1)
        目录
        相关文章
        |
        12天前
        |
        机器学习/深度学习 算法 存储
        [数据结构]——算法的时间复杂度和空间复杂度
        [数据结构]——算法的时间复杂度和空间复杂度
        |
        19天前
        |
        存储 监控 NoSQL
        Redis处理大量数据主要依赖于其内存存储结构、高效的数据结构和算法,以及一系列的优化策略
        【5月更文挑战第15天】Redis处理大量数据依赖内存存储、高效数据结构和优化策略。选择合适的数据结构、利用批量操作减少网络开销、控制批量大小、使用Redis Cluster进行分布式存储、优化内存使用及监控调优是关键。通过这些方法,Redis能有效处理大量数据并保持高性能。
        39 0
        |
        5天前
        |
        算法
        重拾数据结构和算法——脑图
        重拾数据结构和算法——脑图
        11 0
        |
        10天前
        |
        算法 搜索推荐 Java
        Java数据结构 -- 常见算法分析(查找算法、排序算法)精解详解!!!
        Java数据结构 -- 常见算法分析(查找算法、排序算法)精解详解!!!
        7 0
        |
        11天前
        |
        存储 搜索推荐 算法
        C语言数据结构算法,常用10种排序实战
        插入排序(Insertion Sort) 希尔排序(Shell Sort) 选择排序(Selection Sort) 冒泡排序(Bubble Sort) 归并排序(Merge Sort) 快速排序(Quick Sort) 堆排序(Heap Sort) 基数排序(Radix Sort)
        11 1
        C语言数据结构算法,常用10种排序实战
        |
        17天前
        |
        缓存 算法 Java
        数据结构~缓存淘汰算法--LRU算法(Java的俩种实现方式,万字解析
        数据结构~缓存淘汰算法--LRU算法(Java的俩种实现方式,万字解析
        |
        19天前
        |
        机器学习/深度学习 算法 数据可视化
        Python 数据结构和算法实用指南(四)(4)
        Python 数据结构和算法实用指南(四)
        28 1
        |
        19天前
        |
        机器学习/深度学习 存储 算法
        Python 数据结构和算法实用指南(四)(3)
        Python 数据结构和算法实用指南(四)
        17 1
        |
        19天前
        |
        存储 算法 搜索推荐
        Python 数据结构和算法实用指南(四)(2)
        Python 数据结构和算法实用指南(四)
        12 0
        |
        19天前
        |
        存储 算法 Serverless
        Python 数据结构和算法实用指南(四)(1)
        Python 数据结构和算法实用指南(四)
        17 0