最长递增子序列-DP+打牌
作者:互联网
题目
300. 最长递增子序列
难度中等1533
给你一个整数数组
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 。 提示: 1 <= nums.length <= 2500 -104 <= nums[i] <= 104
子串 VS 子序列
-
子串:一定是连续的
-
子序列可以不用连续,但是要注意元素原来的相对顺序。
(大白话就是:在你前面的还是在你前面。术语叫做:稳定!)
解法一:动态规划
动态规划解法思想:
- 找到状态和选择
- 明确dp数组、函数的定义 (在本题中:dp[i] 表示以nums[i]这个数结尾的最长递增子序列的长度)
- 寻找状态之间的关系 (经常会使用数学归纳法)
/**
* @param {number[]} nums
* @return {number}
*/
var lengthOfLIS = function(nums) {
// base case: 最长递增子序列初始值为1,就是要包括自己
const dp = new Array(nums.length);
dp.fill(1);
for(let i = 0; i < nums.length; ++i) {
// 找到在i前面比nums[i]小的数字
for(let j = 0; j < i; ++j ) {
// 找到比nums[i]小的书,形成新的递增子序列,新的递增子序列在原来的基础上加1
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i],dp[j] + 1);
}
}
}
// apply这函数挺好用的,可以用来改变参数的传递。
return Math.max.apply(Math,dp);
};
解法二:二分搜索
解法核心思想:
给牌分堆:
var lengthOfLIS = function(nums) {
let top = [];
let piles = 0; // 记录牌的堆数
for (let i = 0; i < nums.length; ++i) {
// 当前牌,nums[i] 表示牌的大小
let poker = nums[i];
// 牌堆进行一个二分查找
let left = 0,
right = piles;
while (left < right) {
let mid = parseInt((left + right) / 2);
if (top[mid] > poker) {
right = mid;
} else if (top[mid] < poker) {
left = mid + 1;
} else {
right = mid;
}
}
if (left == piles) piles++;
top[left] = poker;
}
return piles;
};
以上解法思想基本来自于 《labuladong算法小抄》,这是一本很”实在“, 可操作性强的书,十分适合菜鸟入门学习leetcode。
刷leetcode的时候,大家要注意一个问题是,题不只是刷一遍,要完全理解一个问题至少刷个四五遍!而且还有一个问题是,你是来学习别人的解法的,你不会做,很正常!!不会就看题解,看官方题解,看图解题解,看labuladong题解,leetcode国际站题解!反正就是发掘一切能够学习的资料。如果你坐在那里半天耗一个题目完全没有必要,因为你不是来发明解法的!你是来学习解法的,学习别人优秀的解题思想的!
参考
- 《labuladong算法小抄》
- leetcode官方题解
标签:nums,题解,递增,let,DP,打牌,序列,解法,dp 来源: https://www.cnblogs.com/rookie123/p/14668604.html