function lengthOfLIS(nums: number[]): number {
const dp: number[] = Array(nums.length).fill(1);
for (let i = 1; i < nums.length; i++) {
for (let j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
}
return Math.max(...dp);
}
console.log(lengthOfLIS([10, 9, 2, 5, 3, 7, 101, 18]));The longest increasing subsequence in the array [10, 9, 2, 5, 3, 7, 101, 18] has length 4. One such subsequence is [2, 3, 7, 101]. The dp array stores the length of LIS ending at each index, and the maximum value in dp is the answer.
After computing the dp array for LIS lengths, a stack is often used to trace back the indices of elements forming the LIS by moving from the maximum dp value backward.
function lengthOfLIS(nums: number[]): number {
const dp: number[] = Array(nums.length).fill(1);
for (let i = 1; i < nums.length; i++) {
for (let j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
}
return Math.max(...dp);
}
console.log(lengthOfLIS([3, 4, 2, 8, 10]));The outer loop uses i <= nums.length which causes nums[i] and dp[i] to access an index equal to nums.length, which is out of bounds and causes a runtime error.
function lengthOfLIS(nums: number[]): number {
const tails: number[] = [];
for (const num of nums) {
let left = 0, right = tails.length;
while (left < right) {
const mid = Math.floor((left + right) / 2);
if (tails[mid] < num) left = mid + 1;
else right = mid;
}
if (right < tails.length) tails[right] = num;
else tails.push(num);
}
return tails.length;
}
console.log(lengthOfLIS([0, 8, 4, 12, 2, 10, 6, 14, 1, 9]));The binary search method efficiently finds the length of LIS. For the input, the LIS length is 5, for example [0, 2, 6, 9, 14].
The classic DP approach uses two nested loops resulting in O(n^2) time. The binary search optimized approach uses a tails array and binary search, reducing time to O(n log n).