子序列 是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
128. 最长连续序列
[100,4,200,1,3,2] => [1, 2, 3, 4]
我自己没做出来,看下官方解:
官方解,确实好一点,尤其是使用了 set 的数据结构,让取值变得简单多了。还有就是其遍历的手段挺有意思的:利用set结构找连续值:找数集中是否有连续的数,直接通过set查找即可。
var longestConsecutive = function(nums) {if (nums.length <= 1) {return nums.length;}let max = 0;let nums_set = new Set(nums); // 利用set结构找连续值for (let num of nums_set) {let cur = num;// 这一点特别鸡贼,避免了重复运算:只从起始点运算 -- 这也是记忆化搜索的替代方案。if (nums_set.has(cur - 1)) { // 不是局部最大的就跳过,如 [300, 5, 4, 3, 10],其中 4,3, 300, 10 就不满足条件,而 5 满足continue;}let size = 1;while (nums_set.has(cur + 1)) {cur++;size++;}if (max < size) {max = size;}}return max;};longestConsecutive([100,4,200,1,3,2]);
674. 最长连续递增序列
[1,3,5,4,7] => [1,3,5]
思路:一个一个遍历元素,记录当前元素为cur,然后判断其后面相邻元素 post,是否比它大:
1)如果大,则增加记录值,然后移动指针
2)如果不大于,则重置状态,去探查下一个序列
一次遍历,双 while 循环:
var findLengthOfLCIS = function(nums) {let n = nums.length;if (n <= 1) {return n;}let max = 0;let i = 0;while (i < n) { // 因为 i 指针,不一定按照顺序:进入下一轮 while 循环,i 指针可能隔好几个数let size = 1let cur = nums[i];let post = nums[i + 1];while (cur < post) {i++;size++cur = nums[i];post = nums[i + 1];}i++; // 容易丢:i++ 是循环向下走的关键,必须保证 while 循环能有路if (max < size) {max = size;}}return max;};
结果:(也还可以吧)
既然,面试不仅靠算法,还要考 coding 能力,那我这道题不换思路,换 for 循环去做:
var findLengthOfLCIS = function(nums) {let n = nums.length;if (n <= 1) {return n;}let max = 0;let size = 1;for (let i = 0; i < n - 1; i++) {let cur = nums[i];let post = nums[i + 1];if (cur < post) {size++;} else { // 上一轮已经记过了size = 1; // 直接清除就好了}if (max < size) { // 每轮都去记录max = size;}}return max;};
300. 最长递增子序列
[10,9,2,5,3,7,101,18] => [2,3,7,101]
这个题目和前面最大的不同就是:不再连续!
不能使用 在《最长连续序列里》 的 set 数据结构,也不能使用 while 循环或for循环。
这段说的非常有理:
逻辑很土,但是有效:
var lengthOfLIS = function(nums) {let n = nums.length;if (n <= 1) {return n;}let dp = (new Array(n)).fill(1); // 单个数自己成递增数列for (let i = 1; i < n; i++) {for (let j = 0; j < i; j++) {if (nums[j] < nums[i]) {dp[i] = Math.max(dp[i], dp[j] + 1); // 去掉max,dp[i]默认是1,所以不断与1比,不需要额外的max来记录最大值了}}}return Math.max(...dp);};
