Leetcode Top 100:最长上升子序列

 

Leetcode Top 100:最长上升子序列_第1张图片

2020.4.12 

又忘记怎么做了,是上次自己没写清楚,这里附上参考题解,作者liweiwei1419:

https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/dong-tai-gui-hua-er-fen-cha-zhao-tan-xin-suan-fa-p/

方法一:暴力法

所有的方法都不是一促而就的,暴力都不会,如何想到更优化的?

这道理暴力可不可以?

肯定可以,为每一个数记录一个以它开头的最长子序列。从第一个数开始遍历一遍数组,碰到大的res就加1,直到最后一个数;

但是这样找的是以第一个数开始完全递增的,确不一定是最长的,因为大的可能在前,比如数组:1,10,4,5,100,,按这个找出来是

1,10,100,其实是14,5,100,也就是説每一个位置都可选可不选,因此复杂度是2^{n}级别的,不可取。

方法二:动态规划

明显上述暴力做法有很多重复的查找

为了从一个较短的上升子序列得到一个较长的上升子序列,我们主要关心这个较短的上升子序列结尾的元素。由于要保证子序列的相对顺序,在程序读到一个新的数的时候,如果比已经得到的子序列的最后一个数还大,那么就可以放在这个子序列的最后,形成一个更长的子序列;

Leetcode Top 100:最长上升子序列_第2张图片

Leetcode Top 100:最长上升子序列_第3张图片

代码

import java.util.*;
class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums == null || nums.length == 0)
            return 0;
        
        int len = nums.length;
        // dp[i]表示以nums[i]结尾的最长上升子序列
        int dp[] = new int[len];
        //初始化
        Arrays.fill(dp, 1);
        int res = 0;
        //填数组
        for(int i = 1; i < len; i++){
            //dp[j]表示以之前数结尾的最长序列,要和nums[i]比较
            for(int j = 0; j < i; ++j){
                dp[i] = dp[j];
                if(nums[j] < nums[i]){
                    dp[i] = Math.max(dp[i], dp[j] + 1);
                }
            }
            res = Math.max(dp[i], res);
        }

        return res;
    }
}

提交报错:

Leetcode Top 100:最长上升子序列_第4张图片

分析:初始化dp都为1,但是循环是从下标1开始的,只有一个数情况,不会走循环,所以res初始化应该为1或者for循环i从0开始

再次提交又发现错误,原来是赋值后就打断原来最长的情况了,删掉这一句:

Leetcode Top 100:最长上升子序列_第5张图片

删掉后运行结果:

Leetcode Top 100:最长上升子序列_第6张图片

效率比较低,下次来看方法三

方法三:贪心加二分查找

明天更新

 

2020.3.15

法一:明显动态规划

法二:贪心加二分查找

 

        tail[i]代表长度为i+1的上升序列最小的末尾

        记住,这个代码只保证末尾最小,也就是长度最长

        最终数组不是最优结果,只求长度

        如果找不到,也就是新数大于tail,则最后循环退出为right

        res是最长的个数

        这里有个trick,right等于res则总是指向下一个数

        因此如果相等的数进来,right会左移  多写几遍

class Solution {
    public int lengthOfLIS(int[] nums) {
        if(nums == null || nums.length == 0)
            return 0;
        int n = nums.length;

        // tail[i]代表长度为i+1的上升序列最小的末尾
        // 记住,这个代码只保证末尾最小,也就是长度最长
        // 最终数组不是最优结果,只求长度
        int tail[] = new int[n];

        //tail数组目前加入数的长度
        int res = 0;
        for(int num : nums){
            int left = 0;
            int right = res;
            //二分,找出在tail中第一个大于等于num的数(这里一定有)
            while(left < right){
                int mid = left + ((right - left) >> 1);
                if(num > tail[mid])  left = mid + 1;
                else
                    right = mid;
            }
            //如果有相等的也是替换
            tail[left] = num;
            //如果找不到,也就是新数大于tail,则最后循环退出为right
            //res是最长的个数
            //这里有个trick,right等于res则总是指向下一个数
            //因此如果相等的数进来,right会左移
            if(res == right) res++;
        }  
        for(int num : tail) System.out.print(num);
        return res;
    }
}


 

你可能感兴趣的:(leetCode,DP系列)