300.最长递增子序列 🌟
题目
LeetCode 中等
题目
给你一个整数数组 nums ,找到其中最长严格递增子序列的长度。
子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。
例如,[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序列。
示例:
输入:nums = [10,9,2,5,3,7,101,18]
输出:4
解释:最长递增子序列是 [2,3,7,101],因此长度为 4 。
输入:nums = [0,1,0,3,2,3]
输出:4
输入:nums = [7,7,7,7,7,7,7]
输出:1
题解
方法:动态规划
DP
O(n2) O(n)
ts
function lengthOfLIS(nums: number[]): number {
const len = nums.length
if (len === 0) return 0
if (len === 1) return 1
let maxLen = 1
const dp = Array(len).fill(1)
for (let i = 1; i < len; i++) {
for (let j = 0; j < i; j++) {
console.log(nums[i], nums[j])
//与前面元素比较
if (nums[i] > nums[j]) {
//碰到小的,dp[j]+1
dp[i] = Math.max(dp[j] + 1, dp[i])
maxLen = Math.max(maxLen, dp[i])
}
}
console.log('------')
}
console.log('dp:', dp)
return maxLen
}
// const array = [10, 9, 2, 5, 3, 7, 101, 1, 4]
// const array = [10, 9, 2, 5, 3, 4]
const array = [3, 5, 6, 2, 5, 4]
console.log('🌰', lengthOfLIS(array))
方法:贪心 + 二分查找
贪心
二分查找
O(nlogn) O(n)
DANGER
❗️ 得到的 tail 并不一定是最后正确的序列,只能保证 length 正确。
ts
function lengthOfLISBinarySearch(nums: number[]): number {
const len = nums.length
if (len <= 1) {
return len
}
const tail = [nums[0]]
for (let i = 0; i < len; i++) {
const num = nums[i]
console.log('-------')
console.log(num)
if (num > tail[tail.length - 1]) {
//当nums中的元素比tail中的最后一个大时,直接push
tail.push(num)
} else {
let li = 0 //左下标
let ri = tail.length - 1 //右下标
while (li < ri) {
let mi = (li + ri) >> 1 //中间下标
console.log('li', li, 'ri', ri, 'mi', mi, '| tailNum', tail[mi])
if (tail[mi] < num) {
li = mi + 1
} else {
ri = mi
}
}
tail[li] = num //将 num 放置到合适的位置,此时前面的元素都比 num 小
}
console.log(`第${i + 1}轮:`, JSON.stringify(tail))
}
return tail.length
}
const array2 = [3, 5, 6, 2, 5, 4, 19, 5, 6, 7, 12]
console.log('🌰', lengthOfLISBinarySearch(array2))