携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第10天,点击查看活动详情
674. 最长连续递增序列
题目描述
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列。
示例
示例 1:
输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。
示例 2:
输入:nums = [2,2,2,2,2]
输出:1
解释:最长连续递增序列是 [2], 长度为1。
解题思路
题目要求我们找到递增序列,所以判断条件限定为 nums[i] < nums[i+1],才属于递增。
最直接的,初始化一个计数器 count = 0, 我们循环判断当前元素和下一个元素是否满足递增,满足则 count++,不满足则 count++ 后与 max 比较,用 Math.max(max, count) 的值更新 max,随后 count 置0重新计数。
题解
/**
* @param {number[]} nums
* @return {number}
*/
var findLengthOfLCIS = function(nums) {
let max = -Infinity, cnt = 0;
const lens = nums.length;
for(let i=0; i<lens; ++i) {
if(nums[i] < nums[i + 1]) {
cnt++;
} else {
cnt++;
if(cnt > max) max = cnt;
cnt=0;
}
}
return max;
};
128. 最长连续序列
题目描述
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例
示例 1:
输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。
示例 2:
输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
解题思路
直接看题目,又是未排序的,找的还是连续最长序列,也就是等差为1的数列,那好啊,既然你没排序,那我直接 sort 排个序,然后学674题一样,前后比差值进行统计就完事了。
题解
/**
* @param {number[]} nums
* @return {number}
*/
var longestConsecutive = function(nums) {
const lens = nums.length;
if(!lens) return 0;
nums.sort((a,b)=>a-b);
let max = -Infinity, cnt = 0;
for(let i=0; i<lens; ++i) {
if(nums[i] === nums[i + 1]) continue;
if(nums[i] + 1 === nums[i+1]) {
cnt++;
} else {
max = Math.max(max, cnt + 1);
cnt = 0;
}
}
return max;
};
你以为这样就完了?
题目要求我们 设计并实现时间复杂度为 O(n) 的算法解决此问题。
好嘛,隔这等着呢,那怎么样才能实现**时间复杂度为O(n)**的算法来做这道题呢。O(n) 又是什么概念呢?
简单来说这题直接杜绝了你排序的手段。排序是不可能排序的,系统让你过但是面试官不同意啊,只不过面试的时候如果一时想不出来别的解法时,可以提一提,然后问怎么优化时,再尬着吧。
那么不用排序怎么做呢?
解题思路
如果不能用排序的话,那么我们想知道一个数在给定的 nums 数组中有没有上下相邻的数,就需要一个数据结构来保存 nums 中所有的数组,为什么不直接用 nums,而是需要额外的数据结构呢?因为数组检索需要遍历,最好的情况下 O(1) 的时间复杂的,最坏的情况下是 O(n) 的时间复杂度,数据量一大,所花费的开销就更大了。
题目要求递增,像 [1,2,2,3,4,6] 中,递增序列为 [1,2,3,4],也就是说碰到相同的,我们要避开,它不会打断我们对递增的判定。如此分析下来,考虑使用 Set 来保存数据,通过 has 方法来判断相邻数据是否存在,存在就让 count++,且从 Set 集合中将该相邻数字移除。
我们可以通过将 nums 中的每个数字向左递减得到左边相邻的所有数字,通过向右遍历得到右边相邻的所有数字,最后将 count 与 max 进行比较,不断比较下来,就能得到最大递增序列的长度了。
题解
/**
* @param {number[]} nums
* @return {number}
*/
var longestConsecutive = function(nums) {
const lens = nums.length;
if(!lens) return 0;
const staticsSet = new Set();
for(let i=0; i<lens; ++i) {
staticsSet.add(nums[i]);
}
let max = -Infinity, cnt=0, arr = [...staticsSet.values()];
for(let i=0; i<arr.length; ++i) {
if(staticsSet.has(arr[i])) {
cnt = 1;
let temp = arr[i];
while(true) {
temp-=1;
if(staticsSet.has(temp)) {
cnt++;
staticsSet.delete(temp);
} else {
break;
}
}
temp = arr[i];
while(true) {
temp+=1;
if(staticsSet.has(temp)) {
cnt++;
staticsSet.delete(temp);
} else {
break;
}
}
max = Math.max(max, cnt);
cnt = 0;
if(staticsSet.size == 0) break;
}
}
return max;
};
再附一个优化过的代码块:
/**
* @param {number[]} nums
* @return {number}
*/
var longestConsecutive = function(nums) {
if(!nums.length) return 0;
const statisticsSet = new Set();
let max = -Infinity;
for(let n of nums) {
statisticsSet.add(n);
}
for(let n of statisticsSet.values()) {
if(!statisticsSet.has(n-1)) { // 找到连续序列的最小值
let cnt = 1;
while(statisticsSet.has(++n)) {
cnt++;
}
max = Math.max(max, cnt);
}
}
return max;
};
在遍历 Set 集合的时候,通过筛选连续序列的最小值(起始值),来减少递减方向的遍历,从而达到优化手段。