zoukankan      html  css  js  c++  java
  • Leetcode题目300.最长上升子序列(动态规划-中等)

    题目描述:

    给定一个无序的整数数组,找到其中最长上升子序列的长度。

    示例:
    
    输入: [10,9,2,5,3,7,101,18]
    输出: 4 
    解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
    说明:
    
    可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
    你算法的时间复杂度应该为 O(n2) 。
    进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

    思路分析:(题解来自:https://leetcode-cn.com/u/liweiwei1419/

    动态规划,时间复杂度为 O(N^2);

    “动态规划”的两个步骤是思考“状态”以及“状态转移方程”。

    有的资料又将“动态规划”分为 3 步:

    base case:思考问题规模最小的时候,是什么情况;
    update function:自下而上思考这个问题,即上面的“状态转移方程”;
    gola:重点强调了输出是什么,很多时候输出并不一定是最后一个状态。
    我觉得这种分法更细致一点,“状态”以及“状态转移方程”也没有问题,但是我觉得还要加上一个,思考一下“输出”是什么,即将第 2 种的第 3 步加上去,在下面的分析中,我还会强调这一点。

    1、定义状态

    首先我们考虑能否将题目的问法定义成状态,即 dp[i] 表示长度为 i 的最长上升子序列的长度,但仔细思考之后,我们发现:由于“子序列”不要求连续,长度为 i - 1 的最长上升子序列,与长度为 i 的“最长上升子序列之间的递推关系并不那么容易得到。

    但我们由「力扣」第 3 题:“无重复字符的最长子串”以及「力扣」第 53 题:“最大子序和”这两个问题的经验,再结合题意,可以知道,“上升”的递推关系是:看子序列最后一个数,如果一个新数,比子序列最后一个数还大,那么就可以放在这个子序列的最后,形成一个更长的子序列。反正一个子序列一定会以一个数字结尾,那我就将状态成以 nums[i] 结尾的“最长上升子序列”的长度,这一点是常见的。

    dp[i]:表示以第 i 个数字为结尾的“最长上升子序列”的长度。即在 [0, ..., i] 的范围内,选择 以数字 nums[i] 结尾 可以获得的最长上升子序列的长度。注意:以第 i 个数字为结尾,即 要求 nums[i] 必须被选取。

    初始化的时候,因为每个元素自己可以认为是一个长度为 1 的子序列,所以可以将 dp 数组的值全部设置为 1。

    定义输出:下面要考虑一下输出,由于状态不是题目中的问法,因此不能将最后一个状态作为输出,这里输出是把 dp[0]、dp[1]、……、dp[n - 1] 全部看一遍,取最大值。

    2、推导“状态转移方程”

    遍历到索引是 i 的数的时候,根据上面“状态”的定义,考虑把 i 之前的所有的数都看一遍,只要当前的数 nums[i] 严格大于之前的某个数,那么 nums[i] 就可以接在这个数后面形成一个更长的上升子序列。因此,dp[i] 就是之前严格小于 nums[i] 的“状态”最大值加 1。

    因此,状态转移方程是:

    dp[i] = max{1 + dp[j] for j < i if nums[j] < nums[i]}

    代码实现:

    class Solution {
           public static int lengthOfLIS(int[] nums) {
    
            int len = nums.length;
            int[] dp = new int[len];
            Arrays.fill(dp, 1);
            //初始化dp数组,每个元素至少都是以它自身为结尾,长度为1的自序列
            int maxLen = 0;
            //从第二个元素开始
            for (int i = 1; i < len; i++) {
                //以当前元素为结尾
                int curVal = nums[i];
                for (int j = 0; j < i; j++) {
                    //当前元素严格大于之前的任何一个片段,则当前元素都可以加在这个区间后面,形成+1长度的自序列
                    if (curVal > nums[j]) {
                        dp[i] = Math.max(dp[i], dp[j] + 1);
                    }
                }
            }
            for (int element : dp) {
                maxLen = Math.max(maxLen, element);
            }
            return maxLen;
        }
    }

    时间复杂度:O(n^2)

    空间复杂度:O(n)

  • 相关阅读:
    docker容器的应用
    KVM虚拟机迁移
    centos6.5虚拟机快照技术
    centos6.5网络虚拟化技术
    centos6.5制作OpenStack云平台Windows7镜像
    centos6.5远程桌面连接(VNCSPice)
    centos6.5kvm虚拟化技术
    centos7安装Jenkins及其卸载(yum和rpm安装)
    CentOS 7安装JDK
    [leetcode]Reverse Nodes in k-Group
  • 原文地址:https://www.cnblogs.com/ysw-go/p/11933852.html
Copyright © 2011-2022 走看看