题一:(最长连续递增序列)
给定一个未经排序的整数数组,找到最长且 连续递增的子序列,并返回该序列的长度。
连续递增的子序列 可以由两个下标 l 和 r(l < r)确定,如果对于每个 l <= i < r,都有 nums[i] < nums[i + 1] ,那么子序列 [nums[l], nums[l + 1], ..., nums[r - 1], nums[r]] 就是连续递增子序列。
举例
输入:nums = [1,3,5,4,7]
输出:3
解释:最长连续递增序列是 [1,3,5], 长度为3。
尽管 [1,3,5,7] 也是升序的子序列, 但它不是连续的,因为 5 和 7 在原数组里被 4 隔开。
输入: nums = [2,2,2,2,2]
输出: 1
解释: 最长连续递增序列是 [2], 长度为1。
解题思路
1. 定义两个变量,len、result
len: 初始值为0,记录最长连续递增序列的长度
result: 初始值为1,记录每一次连续递增序列的长度
2. 当连续递增序列中断一次,result就重置为初始值1,同时比较当前 len 与 result 的大小,
值大的赋值给len,直到整个数组循环结束。
代码实现
/**
* 函数描述: 最长连续递增序列
* @param {Array} arr 一维整数数组
* @return {Number} len 最长连续递增序列长度
*/
const findLenthOfLCIS = (arr) => {
let len = 0;
if (arr.length === len) return len;
let result = 1;
for (let i = 0, j = 1; j < arr.length; i++, j++) {
result = arr[j] > arr[i] ? ++result : 1;
len = Math.max(len, result);
}
return len;
}
console.log(findLenthOfLCIS([28, 35, 44, 22, 36, 79, 101, 108, 107, 110])); // 5
console.log(findLenthOfLCIS([28, 35, 44, 22])); // 3
console.log(findLenthOfLCIS([101, 108, 107, 110])); // 2
console.log(findLenthOfLCIS([9, 9, 9, 9])); // 1
题二:(最长连续序列)
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
举例
输入: nums = [100,4,200,1,3,2]
输出: 4
解释: 最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
输入: nums = [0,3,7,2,5,8,4,6,0,1]
输出: 9
解题思路
1. 首先要区分这里的连续与【最长连续递增序列】的连续不是同一个概念;而是指数组 arr = [1, 2, 3, 4, 5];
在(let i = 0; i < arr.length; i++)循环里满足 arr[--i] === arr[++i] || arr[--i] === --arr[i] 元素组成的序列
2. 根据题目发现,入参的数组是一个未经排序的整数数组,大部人的第一想法就是先对数组排序?从而方便在数组里面
比较 arr[--i] === arr[++i] || arr[--i] === --arr[i] 。其实不然,ES6规范引入了Map和Set,其中Set表示为一组key
的集合,与Map不同的是,它不存储value;由于key不能重复,所以在Set里面不存在重复的key。区别于字面量Object的是:
在Set中可以存储任何数据类型的key,不局限于String。
Set对象的应用: const set = new Set([1, 2, 3]) const res1 = set.has(3), res2 = set.has(4);
console.log({res1, res2}); // { res1: true, res2: false }
3. 通过数组 arr = [-1, 1, 2, 3, 5, 6, 7, 8] 可以发现连续序列 1,2,3 与 5,6,7,8 都有一个起点与终点。而它们的
断点分别是(0)123(4) 与 (4)5678(9) ,其中 0 === 1 - 1 、4 === 3 + 1 、 4 === 5 - 1 、 9 === 8 + 1
因此根据规律在(let i = 0; i < arr.length; i++)循环中可得 当(arr[i] - 1)不存在时,
arr[i]可以充当某一段连续序列的起点;当(arr[i] + 1)不存在时,可以看作是某一段连续序列的终点。
代码实现
/**
* 函数描述: 最长连续序列
* @param {Array} arr 一维整数数组
* @return {Number} len 最长连续序列长度
*/
const findLenthOfLCS = (arr) => {
let len = 0;
if (arr.length === len) return len;
/**
以下可使用方式1、方式2,总体来说方式2耗时比较稳定,方式1会受数组元素值影响
arr1 = [1, 2, 3, 4, 5, 6, 7, 9, 10, 11]
arr2 = [1, 22, 22, 22, 22, 22, 22, 22, 23, 24, 25, 26, 27, 28, 29, 30]
解释: 如arr2类似这种数组使用方式2就比较合适。22 这个元素为某段连续序列的起点,且存在7个22,
方式1比方式2的while会多循环 6 * 8(22为起点的连续序列的长度) 次。最后面有测试数据。
**/
let num = 0; // 计算while循环次数
// const arrSet = new Set(arr), arrLen = arr.length; // 方式1
const arrSet = new Set(arr), newArr = Array.from(new Set(arr)), arrLen = newArr.length; // 方式2
for (let i = 0; i < arrLen; i++) {
// const a = arr[i]; // 方式1
const a = newArr[i]; // 方式2
if (!arrSet.has(a - 1)) {
let currentVal = a, step = 1;
while(arrSet.has(currentVal + 1)) {
++currentVal;
++step;
++num; // 计算while循环次数
}
len = Math.max(len, step);
}
}
console.log('num', num);
return len;
};
console.log(findLenthOfLCS([3, 2, 1])); // 3
console.log(findLenthOfLCS([19, 21, 23, 20, 18, 15, 44, 38, 76])); // 4
console.log(findLenthOfLCS([1, 10])); // 1
以下是对【最长连续序列】方式1、方式2耗时测试。(左图对应方式1、右图对应方式2)
举例1
const arrs = [1, 22, 22, 22, 22, 22, 22, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50];
for (let i = 0; i < 1000000; i++) {
arrs.push(22);
}
console.time('start');
findLenthOfLCS(arrs);
console.timeEnd('start');
举例2
const arrs = [1, 22, 22, 22, 22, 22, 22, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50];
for (let i = 0; i < 1000000; i++) {
if (i % 10 === 0) {
arrs.push(i + Math.floor(Math.random() * 10 + 1));
} else {
arrs.push(i);
}
}
console.time('start');
findLenthOfLCS(arrs);
console.timeEnd('start');
举例3
const arrs = [];
for (let i = 0; i < 1000000; i++) {
arrs.push(i);
}
console.time('start');
findLenthOfLCS(arrs);
console.timeEnd('start');
举例4
const arrs = [1, 22, 22, 22, 22, 22, 22, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50];
for (let i = 51; i < 1000000; i++) {
arrs.push(i)
}
console.time('start');
findLenthOfLCS(arrs);
console.timeEnd('start');