leetbook:中级算法
类型:动态规划
题目名:最长上升子序列
原题URL:https://leetcode-cn.com/leetbook/read/top-interview-questions-medium/xwhvq3/
题目描述
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例 1:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
示例 2:
输入:nums = [0,1,0,3,2,3]
输出:4
示例 3:
输入:nums = [7,7,7,7,7,7,7]
输出:1
限制
1 <= nums.length <= 2500
-104 <= nums[i] <= 104
进阶:
- 你可以设计时间复杂度为
O(n2)
的解决方案吗? - 你能将算法的时间复杂度降低到
O(n log(n))
吗?
解题思路
1.动态规划,dp[i]表示0~i位置的最长上升子序列的个数
2.循环枚举,如果当前位置比循环位置大,就加入到当前位置的最长子序列中
3.每次该位置的最长子序列长度要做一次记录并且与最长的比较
4.可以进行二分优化(二分得比较丑陋请见谅)
解题代码
class Solution {
public int lengthOfLIS(int[] nums) {
if(nums==null||nums.length==0) return 0;
//i,j位置的最长子序列个数
int len = nums.length;
int[] dp = new int[len];
//初始化
for (int i = 0; i < len; i++) {
dp[i] =1;
}
//记录最大值,默认值1
int max = 1;
//定住i的位置,从0-i开始搜索,如果当前位置可以加入到最大子序列,就将其加入
for (int i = 1; i < len; i++) {
//如果当前值比j位置要大,那就往后面加
for (int j = 0; j < i; j++) {
if(nums[j]<nums[i]) {
dp[i] = Math.max(dp[j]+1,dp[i]);
}
}
//找到每一个位置的最大子序列,来进行比较
max = Math.max(max,dp[i]);
}
return max;
}
}
优化代码
public class Solution {
public int lengthOfLIS(int[] nums) {
if(nums==null||nums.length==0) return 0;
int len = nums.length;
//思路,,用一个数组来存储最长子序列
int[] dp = new int[len];
int idx = 0;
dp[0] = nums[0];
int start = 0;
int end = idx;
int mid = 0;
for(int i =1;i<len;i++) {
//如果当前数字大于最长子序列的最大值,那么直接加在末尾
if(nums[i]>dp[idx]) dp[++idx] = nums[i];
else if(nums[i]<dp[0]) dp[0] = nums[i];
else {
start = 0;
end = idx;
//如果没有的话,就在最长子序列中找到他应该在的位置
//因为是升序的,,所以可以使用二分查找
while(start<=end) {
mid = start+(end-start)/2;
if(dp[mid]>=nums[i]) {
if(mid-1<0||dp[mid-1]<nums[i]) {
dp[mid] = nums[i];
break;
}
else end = mid-1;
}else {
start = mid+1;
}
}
}
}
return idx+1;
}
}