「这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战」
首先,来个开胃菜。给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
nums = [1,3,5,4,7] // 最长连续递增序列是 [1,3,5], 长度为3。
-
我们首先分析连续递增
i> 0 && nums[i-1] <= nums[i]// 开始位置要大于0 当前比上一位要大或相等构成连续递增 -
然后记录这个时候的长度
i-start + 1, 我们要保存临时长度ans, 和之前的长度对比取最大Math.max(ans, i-start+1), 遍历结束后就是我们的最大长度。
//完 整代码
let start = 0
let ans = 0
for(let i = 0; i< nums.length; i++){
if(i>0 && nums[i]<= nums[i-1]){
start = i
}
ans = Math.max(ans, i-start +1)
}
return ans
现在我们进入leetcode300题的解析。
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
-
最长严格递增只能是比前面的大,相等的不算
-
我们使用动态规划解题
- 首先初始化一个
dp数组每个元素为1, - 外层循环用一个临时变量
max,取Math.max(dp[i], max) - 然后从索引i-1的位置往前比较
nums[i] > nums[j]计算长度保存到dp[i] = Math.max(dp[i],dp[j] + 1)当前位置
//完 整代码
//判断边界
const n = nums.length
if(n <= 1){
return n
}
let max = 0
const dp = new Array(n).fill(1) // 初始化
for(let i = 1; i < n; i++){
for(let j = i-1; j>=0; j--){
// 严格递增
if(nums[i] > nums[j]){
// 获取dp[i]的最大值 dp[j] + 1 长度包含nums[i]当前位置所以+1
dp[i] = Math.max(dp[i], dp[j] + 1)
}
}
max = Math.max(d[i], max)
}
return max
上述解法中,当前元素都和之前的元素做了对比,如果做个记录,就不需要多余的比较
- 优化解法,定义一个
dp[i]记录一个长度为i最长递增子序列尾部元素的最小值 dp[1]初始化nums[0]- 如果
dp里的元素都比nums[i],将其插入到最后 - 否则二分查找dp,用
num[i]覆盖掉比它大的元素
const n = nums.length;
let max = 1
if(n <=1 )return n
const dp = [null, nums[0]]
for(let i = 1; i< n; i++){
if(dp[max] < nums[i]){
dp[++max] = nums[i]
}
// 二分查找
let left = 1,
right = max,
mid,
pos = 0
while(left <= right){
mid = (right+left )>>1
if(dp[mid] < nums[i]){
//在左边
left = mid+1
pos = mid
}else{
right = mid-1
}
}
// 替换掉比当前元素大的那个
dp[pos+1] = nums[i]
}
return max
总结:使用
dp记录一个长度为i最长递增子序列尾部元素的最小值 ,可以减少对比次数,优化了时间复杂度 O(nlogn)