• LeetCode题解 300 最长上升子序列


    1. 题目描述

    给定一个数字序列,求其最长上升子序列

    1.1. 测试用例

    测试用例
    int[] nums = {4,2,4,5,3,7};
    
    预期结果
    4, 序列是{2,4,5,7}
    

    1.2. 函数签名

    public int lengthOfLIS(int[] nums){
    
    }
    

    2. 题解

    2.1. 动态规划解法

    时间复杂度为O(N^2)

    2.1.1. 分析

    • 确定状态:dp[i]以nums[i]结尾的最长子序列的长度
    • 转移方程: $dp[i] = max { 1, dp[j] + 1 } quad 0 le j < i 且 nums[i] ge nums[j]$
      • 以nums[i]结尾的LIS,序列中nums[i]的上一个数字可能是nums[i]之前的任何一个比它小的数, 假设是上一个数字nums[j], 则有j < i 且 nums[i] $le$ ge nums[j], 此时的序列的长度为dp[j] + 1
      • 也有可能nums[i]之前的数字没有比它小的,那么以它结尾的LIS长度就是1
    • 返回值。注意要返回dp数组中的最大值,而不是dp[n]

    2.1.2. Java实现

    public int longestIncreasingSubsequence(int[] nums) {
        if(nums == null || nums.length == 0){
            return 0;
        }
        //dp[i] : 以nums[i]结尾的LIS的长度
        int n = nums.length;
        int[] dp = new int[n] ;
        //初始化
        Arrays.fill(dp, 1);
        for(int i = 0; i < n; i++){
            //nums[i]之前的每一个数字
            for(int j = 0; j < i; j++){
                if(nums[i] > nums[j]){
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
        }
        //dp中的最大值
        int res = dp[0];
        for (int i = 1; i < n; i++) {
            res = Math.max(res, dp[i]);
        }
        return res;
    }
    

    2.2. 二分解法

    时间复杂度为O(N*logN),单独去理解比较难,在动态规划的解法上进一步思考会更好理解

    2.2.1. 分析

    数组{4, 2, 4, 5, 2,7}, 用DP解法可以得到,dp = {1, 1, 2, 3, 2,7}。假设计算dp[3], DP解法在得到dp[3]时,需要顺序查找3之前的dp值中可用的(序列尾元素小于nums[3]的)最大值,其实可以使用二分查找。但想使用二分查找前,先要证明一些结论。

    通过dp数组我们知道dp[3]前面的dp有:

    • dp[0],dp[1]对应长度为1的序列{4}、{2},它们中最小的尾元素是2
    • dp[2]对应长度为2的序列{*, 4}, 它们中最小的尾元素是4

    结论1 : 数值相等的那些dp只需要查找最小的尾元素是否可用

    • 分析:如果尾元素大的可用,尾元素小的一定可用,但是反过来不一定,所以最后的采用的一定会是dp相同的尾元素最小的其中一个

    结论2 : dp值递增时,它们中最小的尾元素肯定也是递增的

    • 反证法,dp = len1的最小尾元素是t1,dp = len2的最小尾元素是t2, len1 < len2。如果t1 > t2 , 因为长为len2的序列为...t2, 其中肯定有长为len1的序列 ...t3...t2, t3<=t2, 和长为len1的序列的最小尾元素t1 > t2矛盾

    有了这两个结论,可以用tail[j]记录dp值为j的序列们的最小尾元素,dp[i]的值可以通过对tail进行二分查找可用的尾元素值来得到

    2.2.2. Java实现

    public int longestIncreasingSubsequence(int[] nums) {
        int n = nums.length;
        //minTail[i] 所有长度为i的子序列中最小的尾元素的值
        int[] minTail = new int[n + 1];
        minTail[0] = Integer.MIN_VALUE;
        //最后一个记录的位置
        int maxLen = 0;
        for (int i = 0; i < n; i++) {
            int prePlace = 0;
            int start = 0, end = maxLen, mid;
            while (start <= end) {
                mid = start + (end - start) / 2;
                //nums[i]会放到最后一个比它小的堆的右边
                if (nums[i] > minTail[mid]) {
                    prePlace = mid;
                    start = mid + 1;
                } else {
                    end = mid - 1;
                }
            }
            //更新nums[i]放的堆的最小值
            minTail[prePlace + 1] = nums[i];
            //所有的堆的最小值都比它小,另起一堆
            if (prePlace + 1 > maxLen) {
                maxLen = prePlace + 1;
            }
        }
        return maxLen;
    }
    
  • 相关阅读:
    zabbix3.4报警队列过多清理
    k8s安装nexus并导入第三方jar包
    kubeadmin安装最新版本的kubenets
    aws相关知识
    mysql忘记root密码做法
    mysql实现读写分离(proxy)与高可用(MGR)
    jvm调优
    zabbix监控php-fpm的性能
    脚本
    opencv demo
  • 原文地址:https://www.cnblogs.com/uoa7/p/13096317.html
Copyright © 2020-2023  润新知