题解 | #最长上升子序列(一)#

最长上升子序列(一)

http://www.nowcoder.com/practice/5164f38b67f846fb8699e9352695cd2f

题意整理

  • 给定一个长度为n的数组。
  • 求它的最长上升子序列的长度。子序列是指按原来数组顺序取的若干个数字组成的序列,上升子序列是指序列中元素严格单调递增。

方法一(动态规划)

1.解题思路

  • 状态定义:dp[i]dp[i]表示以下标i结尾的最长上升子序列的长度。
  • 状态初始化:以任意下标结尾的上升子序列长度不小于1,故初始化为1。
  • 状态转移:遍历数组中所有的数,再遍历当前数之前的所有数,只要前面某个数小于当前数,则要么长度在之前基础上加1,要么保持不变,取两者中的较大者。即dp[i]=Math.max(dp[i],dp[j]+1)dp[i]=Math.max(dp[i],dp[j]+1)

2.代码实现

import java.util.*;

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型一维数组 给定的数组
     * @return int整型
     */
    public int LIS (int[] arr) {
        int n=arr.length;
        //特殊请款判断
        if(n==0) return 0;
        //dp[i]表示以下标i结尾的最长上升子序列长度
        int[] dp=new int[n];
        for(int i=0;i<n;i++){
            //初始化为1
            dp[i]=1;
            for(int j=0;j<i;j++){
                if(arr[i]>arr[j]){
                    //只要前面某个数小于当前数,则要么长度在之前基础上加1,要么保持不变,取较大者
                    dp[i]=Math.max(dp[i],dp[j]+1);
                }
            }
        }
        //返回所有可能中的最大值
        return Arrays.stream(dp).max().getAsInt();
    }
}

3.复杂度分析

  • 时间复杂度:两层循环,最多执行n(n1)/2n*(n-1)/2次,所以时间复杂度是O(n2)O(n^2)
  • 空间复杂度:需要额外大小为n的dp数组,所以空间复杂度为O(n)O(n)

方法二(二分优化)

1.解题思路

  • 维护一个单调递增的tail数组。
  • 遍历arr数组中所有的数,如果tail数组为空或tail数组末尾值小于arr[i]arr[i],直接加在后面。否则,二分法找到当前数在tail数组中的位置,替换掉原来位置的数。
  • end总是指向tail数组最后一个元素的位置,所以end+1即为最终tail数组的长度,也就是最长上升子序列的长度。

图解展示: alt

2.代码实现

import java.util.*;

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 给定数组的最长严格上升子序列的长度。
     * @param arr int整型一维数组 给定的数组
     * @return int整型
     */
    public int LIS (int[] arr) {
        int n=arr.length;
        //特殊情况判断
        if(n==0) return 0;
        //维护一个单调递增的数组
        int[] tail=new int[n];
        //指向tail数组的最后一位
        int end=-1;
        for(int i=0;i<n;i++){
            //如果数组为空或数组末尾值小于arr[i],直接加在后面
            if(i==0||tail[end]<arr[i]){
                tail[++end]=arr[i];
            }
            //否则找到tail数组中第一个大于等于arr[i]的数的下标,替换为arr[i]
            else{
                int index=binarySearch(tail,end,arr[i]);
                tail[index]=arr[i];
            }
        }
        return end+1;
    }

    //二分法找到tail数组中第一个大于等于arr[i]的数的下标
    private int binarySearch(int[] tail,int end,int target){
        int l=0;
        int r=end;
        while(l<r){
            int mid=l+(r-l)/2;
            if(tail[mid]>=target){
                r=mid;
            }
            else{
                l=mid+1;
            }
        }
        return l;
    }
}

3.复杂度分析

  • 时间复杂度:需要遍历数组中所有的数,对于特定的数(小于等于有序数组中的最大值),需要通过二分法找到其在有序数组中的位置,最坏情况下所有的数都需要二分查找确定位置,所以时间复杂度是O(nlog2n)O(n*log_2n)
  • 空间复杂度:需要额外维护一个大小为n的有序数组,所以空间复杂度为O(n)O(n)
xqxls的题解 文章被收录于专栏

牛客题解

全部评论
这个二分查找法想不到
点赞 回复 分享
发布于 2023-07-29 21:02 北京
这个二分查找,虽然会找到子序列的长度,但是子序列的内容是不一样的,比如6,7,1,8,9,10
点赞 回复 分享
发布于 2023-06-18 23:08 湖北
很详细,很nice
点赞 回复 分享
发布于 2022-03-09 15:17

相关推荐

不愿透露姓名的神秘牛友
05-29 15:00
教授A:“你为什么要讲这么久,是要压缩我们对你的评议时间吗?你们别以为这样就能够让我们对你们少点意见。”&nbsp;“从你的发言和论文格式就能知道你的性格啊。”…….&nbsp;感觉被狠狠霸凌了。
码农索隆:“教授您好,首先我想回应您提出的两点疑问。” “关于我讲解时间较长的问题:这绝非为了压缩各位老师的评议时间。这份毕业设计是我过去几个月倾注了全部心血的作品,从构思、实验、调试到撰写,每一个环节都反复打磨。我深知时间宝贵,所以选择详细讲解,是希望能更完整、清晰地展示它的核心创新点、实现过程和验证结果,确保老师们能充分理解它的价值和我的努力。我完全理解并重视评审环节的意义,也做好了充分准备来听取各位老师的专业意见和批评。几个月的研究都坚持下来了,我怎么可能害怕老师们的点评呢?今天站在这里,正是抱着虚心学习、诚恳求教的态度而来。” “如果我的展示确实超时,影响了后续流程,烦请老师们随时示意,我会立刻调整。我非常期待并预留了充足的时间,希望能听到老师们宝贵的建议和深入的讨论。” “其次,关于您提到‘从发言和论文格式就能知道我的性格’。教授,我对此感到非常困惑和不安。学术研究和答辩的核心,难道不应该是作品本身的质量、逻辑的严谨性、数据的可靠性和结论的合理性吗?论文格式有明确的规范要求,我尽最大努力遵循了这些规范。如果格式上存在疏忽或不足,这属于技术性、规范性的问题,恳请老师们具体指出,我一定认真修改。但将格式问题或个人表达风格(如讲解时长)直接上升为对个人性格的评判,甚至以此作为质疑我学术态度和动机的依据,这让我感到非常不公平,也偏离了学术评议应有的客观和严谨原则。” “我尊重每一位评审老师的专业权威,也衷心希望能得到老师们对我的工作内容本身的专业指导和批评指正。任何基于研究本身的意见,无论多么尖锐,我都会认真聆听、反思并改进。但我恳请老师们,能将评议的焦点放在我的研究本身,而不是对我个人进行主观的推断或评价。谢谢各位老师。”
点赞 评论 收藏
分享
求面试求offer啊啊啊啊:把华北改为华南再试一试,应该就没啥问题了。改完可能都不用投,别人主动联系了。
点赞 评论 收藏
分享
评论
13
3
分享

创作者周榜

更多
牛客网
牛客企业服务