手摸手提桶跑路——LeetCode递增序列系列

166 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 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 中的每个数字向左递减得到左边相邻的所有数字,通过向右遍历得到右边相邻的所有数字,最后将 countmax 进行比较,不断比较下来,就能得到最大递增序列的长度了。

题解

/** 
* @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 集合的时候,通过筛选连续序列的最小值(起始值),来减少递减方向的遍历,从而达到优化手段。