Longest Increasing Subsequence in DSA Javascript - Time & Space Complexity
We want to understand how the time needed to find the longest increasing subsequence changes as the input list grows.
How does the number of steps grow when the list gets bigger?
Analyze the time complexity of the following code snippet.
function lengthOfLIS(nums) {
const n = nums.length;
const dp = new Array(n).fill(1);
for (let i = 1; i < n; i++) {
for (let j = 0; j < i; j++) {
if (nums[j] < nums[i]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
}
return Math.max(...dp);
}
This code finds the length of the longest increasing subsequence by checking all pairs of elements.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Nested loops comparing pairs of elements.
- How many times: Outer loop runs n times, inner loop runs up to i times, so roughly n x n = n² times.
As the list size grows, the number of comparisons grows much faster because each new element is compared with all before it.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 100 comparisons |
| 100 | About 10,000 comparisons |
| 1000 | About 1,000,000 comparisons |
Pattern observation: Operations grow roughly by the square of the input size.
Time Complexity: O(n²)
This means if the input list doubles in size, the time to run the code roughly quadruples.
[X] Wrong: "The code only looks at each element once, so it must be O(n)."
[OK] Correct: Each element is compared with many others inside nested loops, so the total steps grow much faster than just the number of elements.
Understanding this time complexity helps you explain why some solutions are slower and shows your skill in analyzing nested loops, a common interview topic.
"What if we used a binary search to find the position to update instead of checking all previous elements? How would the time complexity change?"
Practice
Solution
Step 1: Understand the definition of LIS and compare options
The Longest Increasing Subsequence is a subsequence where every next number is strictly greater than the previous one. The longest subsequence where each number is larger than the previous one matches the definition exactly, others describe different patterns.Final Answer:
The longest subsequence where each number is larger than the previous one -> Option DQuick Check:
LIS = strictly increasing subsequence [OK]
- Confusing increasing with decreasing subsequence
- Thinking LIS means contiguous elements only
- Assuming equal numbers are allowed in LIS
arr?Solution
Step 1: Understand LIS initialization and check syntax
Each element starts with LIS length 1 because each element alone is a subsequence. const lis = new Array(arr.length).fill(1); correctly creates an array of length arr.length filled with 1s.Final Answer:
const lis = new Array(arr.length).fill(1); -> Option CQuick Check:
Initialize LIS array with 1s [OK]
- Filling with 0 instead of 1
- Using fill(null) or fill(undefined) which are incorrect for LIS
- Not matching array length to input array
[10, 22, 9, 33, 21, 50, 41, 60]?Solution
Step 1: Identify increasing subsequences and confirm no longer exists
Possible LIS includes [10, 22, 33, 50, 60] which has length 5. Checking other subsequences shows none longer than 5.Final Answer:
5 -> Option BQuick Check:
LIS length for given array = 5 [OK]
- Counting non-increasing sequences
- Confusing subsequence with subarray
- Missing longer subsequences by skipping elements
function lengthOfLIS(arr) {
const lis = new Array(arr.length).fill(1);
for (let i = 1; i < arr.length; i++) {
for (let j = 0; j < i; j++) {
if (arr[i] > arr[j]) {
lis[i] = Math.max(lis[i], lis[j] + 1);
}
}
}
return lis[arr.length];
}
What is the error in this code?Solution
Step 1: Check return statement and correct return logic
Returning lis[arr.length] accesses out-of-bounds index (undefined). Should return the maximum value in lis array, e.g., Math.max(...lis).Final Answer:
Returns lis[arr.length] which is undefined, should return max value in lis array -> Option AQuick Check:
Return max LIS length, not out-of-bounds element [OK]
- Returning wrong index from lis array
- Misunderstanding loop ranges
- Reversing comparison operators
[3, 10, 2, 1, 20], which of the following code snippets correctly finds the length of the Longest Increasing Subsequence using binary search optimization?Solution
Step 1: Understand binary search in tails array and check boundaries/conditions
We find the position to replace or append the current number to keep tails sorted. function lengthOfLIS(arr) { const tails = []; for (const num of arr) { 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; } tails[left] = num; } return tails.length; } uses correct boundaries and conditions: left < right, mid floor, tails[mid] < num moves left up.Final Answer:
function lengthOfLIS(arr) { const tails = []; for (const num of arr) { 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; } tails[left] = num; } return tails.length; } -> Option AQuick Check:
Binary search with left < right and tails[mid] < num [OK]
- Using wrong binary search boundaries (<= instead of <)
- Incorrect mid calculation (ceil instead of floor)
- Wrong comparison operators in binary search
Ready to prove it?
Pass the test and this pattern moves unknown
