面试热题(最长上升子序列)

给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。

子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。

复制代码
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。

由上图我们可以很容易直到该数组中的最长递增子序列的长度为4,可是计算机可不知道这么算,所以我们要告诉它得这么算,我们仔细找找规律

这种的分析是不是能让你看出一点点规律,如果当前值i比i-1前的最长自序列的最后一个值大时,将当前这个值加入这个递增子序列中,是不是我们就比较容易得到:

java 复制代码
        for (int i = 1; i <nums.length; i++) {
            for (int j = 0; j <i; j++) {
                if(nums[j]<nums[i]){
              dp[i]=Math.max(dp[j]+1,dp[i]); 
                }
                
            }
        }

那么我们动态规划中最难的一步已经写出来了,状态转移方程,接下来就是动规的一般解题步骤,入参判断,维护一个dp数组,对其进行初始化,具体的看下面的源代码:

java 复制代码
 public int lengthOfLIS(int[] nums) {
      if(nums==null||nums.length==0){
          return 0;
      }
      int[] dp=new int[nums.length];
      Arrays.fill(dp,1);
        for (int i = 1; i <nums.length; i++) {
            for (int j = 0; j <i; j++) {
                if(nums[j]<nums[i]){
              dp[i]=Math.max(dp[j]+1,dp[i]); 
                }
                
            }
        }
       return Arrays.stream(dp).max().getAsInt();
    }

在大家再给大家介绍一种解法,面试时两种解法任选一种即可,我认为还是动态规划这种比较容易好记

堆纸牌方法:

这种方法一般人还真想不到,可能那种久经牌场的人说不定可以想到:

类似于蜘蛛纸牌一样的游戏规则,每次找到最左边的适合当前牌的牌堆,如果没有就直接新创一个牌堆

没堆牌出一张组成子序列,牌堆的堆数越多,最长子序列的长度也就越大:

【5,6,7,J】、【5,7,8,J】...

所以我们应该怎么样去模拟这个算法呢?

由于我们要时刻的知道每堆牌的顶牌,所以我们可以维护一个数组去记录每个堆的牌顶

java 复制代码
int[] top=new int[nums.length];
int count=0;//一开始,未进行发牌,牌堆数为0

如果有这么sexy的荷官给你发牌,你做题怎么可能会错?比如邱淑贞姐姐给你发牌,哒哒哒...

因为数组中的索引是从0开始的,但是牌堆数确实从1开始的,所以当我们查找可以放当前牌的最左牌堆的索引等于牌堆数的时候,就应该重新创建一个牌堆,如果不太了解二分搜索最左侧搜索,请看二分搜索篇

java 复制代码
    public int lengthOfLIS(int[] nums) {
      if(nums==null||nums.length==0){
          return 0;
      }
      int[] top=new int[nums.length];
      int count=0;
      //进行发牌
      for(int num:nums){

          int left=0;
          int right=count;
  //这里给大家说明以下,这种二分查找区间的时候区间是左闭右开的
  //因为count代表的是堆数(从1开始),但是right指针代表的是数组的索引(从0开始),指针永远不可能到达堆数的数量
          while(left<right){
              int mid=left+(right-left)/2;
              if(top[mid]>=num){
                  right=mid;//不断的去优先收缩右区间
              }else{
                  left=mid+1;//收缩左区间
              }
          }

          //找到最小的大于等于num的索引大小
          if(left==count){
              count++;
          }
          //更新牌顶元素
          top[left]=num;
      }
      return count;
    }
相关推荐
yuuki2332334 分钟前
【C++】类和对象(上)
c++·后端·算法
dangdang___go10 分钟前
动态内存管理||malloc和free.realloc和calloc
c语言·开发语言·算法·动态内存管理
稚辉君.MCA_P8_Java11 分钟前
Gemini永久会员 快速排序(Quick Sort) 基于分治思想的高效排序算法
java·linux·数据结构·spring·排序算法
数字化脑洞实验室24 分钟前
智能决策与决策优化:从算法到产业的演进逻辑
算法
cpp_250124 分钟前
P5412 [YNOI2019] 排队
数据结构·c++·算法·题解·洛谷
kingmax5421200838 分钟前
图论核心算法(C++):包括存储结构、核心思路、速记口诀以及学习方法, 一站式上机考试学习【附PKU百练,相关练习题单】
c++·算法·图论·信奥赛·上机考试·百练·pku
罗湖老棍子40 分钟前
【例9.15】潜水员(信息学奥赛一本通- P1271)
c++·算法·动态规划·二维费用背包
_OP_CHEN1 小时前
算法基础篇:(二十一)数据结构之单调栈:从原理到实战,玩转高效解题
数据结构·算法·蓝桥杯·单调栈·算法竞赛·acm/icpc
代码游侠1 小时前
学习笔记——数据结构学习
linux·开发语言·数据结构·笔记·学习
q***51892 小时前
【语义分割】12个主流算法架构介绍、数据集推荐、总结、挑战和未来发展
算法·架构