• (Java) LeetCode 300. Longest Increasing Subsequence —— 最长上升子序列


    Given an unsorted array of integers, find the length of longest increasing subsequence.

    Example:

    Input: [10,9,2,5,3,7,101,18]
    Output: 4 
    Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4. 

    Note:

    • There may be more than one LIS combination, it is only necessary for you to return the length.
    • Your algorithm should run in O(n2) complexity.

    Follow up: Could you improve it to O(n log n) time complexity?


    解法一,O(n2):

    直观想一下,选中一个元素,以这个元素为结尾的子序列前面有多少个比他值小的元素,那么以它为结尾的上升子序列就是多少再加一。即当前状态可以由之前的一个或一些状态推导出来,所以可以用动态规划。建立一个一维数组dp,dp[i]表示以nums[i]结尾的最长上升子序列长度。初始都置为1。对于原数组每个元素,二重循环从头遍历原数组,每当找到一个比当前元素小的值,证明至少可以形成一个dp[j]+1的上升子序列,所以dp[i] = max(dp[i], dp[j] + 1),而dp[j]之前已经求得。

    解法二,O(n log n):

    还是想一下“人工”做这个题是什么过程。按照上面的例子来分析:

    首先看到10,加入备选集,备选集合为{10};

    之后看到了9,没有形成上升序列,那么9不应该加入备选集合。但是因为9小于10,所以如果把10替换成9会增加接下来产生上升序列的机会,且并不影响备选集合元素的个数(因为是替换),所以替换掉,备选集现在有{9};

    遇到2道理同上,替换掉9,备选集变成{2};

    遇到5,这时候形成了上升序列,此时应该是添加到备选集合,变为{2,5};

    遇到3,没有形成上升序列,但还是道理同加入9的情况,如果此时把5替换成3,会增加接下来形成上升序列的机会,且备选集保持上升,并且个数也没变,所以替换掉5,备选集变成{2,3};

    遇到7,同遇到5,添加元素,备选集{2,3,7};

    遇到101,同上,备选集{2,3,7,101};

    遇到18,还是一样,虽然没有形成上升序列,但是如果把101替换掉,那么接下来形成上升序列的机会会增加,并且备选集的上升属性和元素个数都不变,所以替换,备选集变为{2,3,7,18}。

    至此所有元素添加完毕,备选集的元素个数就是最长上升子序列长度。但这里注意,备选集里面的元素并不是最后最长子序列的元素。因为在寻找最长子序列的过程中,目标是尽可能的让以后形成上升序列的机会增加,所以进行了替换。

    “人工”做出来之后,只要用程序实现思考过程就好。总结起来就是:

    如果遇到的元素比备选集合里面的元素都大,那么就添加进去,使得上升序列长度增加;

    如果遇到的元素比备选集合里最后一个元素小,那么代表它无法被添加到备选集。但是为了使后面得到上升序列的机会增加,需要在不破坏集合上升属性和元素总数的情况下,替换掉备选集中的元素,那么就是替换掉大于他的元素中最小的那个,这样才能满足条件。

    这时候,发现备选集一直是保持有序,寻找替换元素的时候就可以用到二分查找,得到O(n log n)的时间复杂度。其中还要注意的是如果元素已经在备选集合中,是不需要任何操作的,因为它并不能增加上升序列的长度,也不会增加之后遇到上升序列的机会,所以直接跳过。


    解法一(Java)

    class Solution {
        public int lengthOfLIS(int[] nums) {
            if (nums == null || nums.length == 0) return 0;
            int[] dp = new int[nums.length];
            int max = 1;
            for (int i = 0; i < nums.length; i++)
                dp[i] = 1;
            for (int i = 0; i < nums.length; i++) {
                for (int j = 0; j <= i; j++) {
                    if (nums[j] < nums[i]) {
                        dp[i] = Math.max(dp[i], 1 + dp[j]);
                        max = max > dp[i] ? max : dp[i];
                    }            
                }
            }
            return max;
    }

    解法二(Java)

    class Solution {
        public int lengthOfLIS(int[] nums) {
            if (nums == null || nums.length == 0) return 0;
            List<Integer> dp = new ArrayList<>();
            dp.add(nums[0]);
            for (int i = 1; i < nums.length; i++) {
                if (dp.contains(nums[i])) continue;
                else if (nums[i] > dp.get(dp.size()-1)) dp.add(nums[i]);
                else if (nums[i] < dp.get(dp.size()-1)) {
                    int l = 0, r = dp.size()-1;
                    while (l < r) {
                        int mid = l + (r - l) / 2;
                        if (dp.get(mid) < nums[i]) l = mid + 1;
                        else r = mid;
                    }
                    dp.set(r, nums[i]);
                }
            }
            return dp.size();
        }
    }
  • 相关阅读:
    这次面试就差不多了,你有什么问题需要问我呢?
    C++为啥要使用new
    C#读取“我的文档”等特殊系统路径及环境变量
    C++11 Lambda表达汇总总结
    c#计算 坐标点与坐标点之间的距离
    eclipse svn同步资源库时忽略某些不需要提交文件类型和文件夹
    通俗理解TCP/IP协议三次握手四次分手流程
    mysql 免安装版 启动服务马上关闭
    MySQL数据库安装与配置详解
    word 插入的图片被嵌套在文字底下
  • 原文地址:https://www.cnblogs.com/tengdai/p/9241455.html
Copyright © 2020-2023  润新知