Longest Increasing Subsequence – LeetCode Solution with Detailed Explanation
Problem Statement
Given an integer array nums, return the length of the longest increasing subsequence.
A subsequence is a sequence derived from another sequence by deleting some or no elements without changing the order of the remaining elements.
Example 1:
Input:
nums = [10,9,2,5,3,7,101,18]
Output:
4
Explanation:
The longest increasing subsequence is [2,3,7,101], and its length is 4.
Example 2:
Input:
nums = [0,1,0,3,2,3]
Output:
4
Explanation:
The longest increasing subsequence is [0,1,2,3], and its length is 4.
Example 3:
Input:
nums = [7,7,7,7,7,7,7]
Output:
1
Explanation:
All elements are the same, so the longest subsequence has only one element.
Optimal Approach – Dynamic Programming (DP) with Binary Search
We use Dynamic Programming with Binary Search to solve this problem in O(n log n) time.
Key Observations:
- We maintain an array
sub, wheresub[i]represents the smallest element that could be the end of an increasing subsequence of lengthi+1. - For each
nums[i]:- Use Binary Search (
lower_bound) to find wherenums[i]fits insub. - If it extends
sub, appendnums[i]. - Otherwise, replace an element in
subto keep it minimal.
- Use Binary Search (
- The length of
subgives the length of the longest increasing subsequence.
LeetCode-Compatible C++ Code Implementation
#include <vector>
#include <algorithm>
class Solution {
public:
int lengthOfLIS(std::vector<int>& nums) {
std::vector<int> sub;
for (int num : nums) {
auto it = std::lower_bound(sub.begin(), sub.end(), num);
if (it == sub.end()) {
sub.push_back(num);
} else {
*it = num;
}
}
return sub.size();
}
};
Step-by-Step Explanation of Code
- Initialize an empty array
sub- This stores the elements forming the longest increasing subsequence.
- Iterate through
nums- Use
lower_boundto find the position wherenumcan replace an element insub. - If
numis greater than all elements insub, append it. - Otherwise, replace an existing element to keep
subminimal.
- Use
- Return the length of
sub, which represents the longest increasing subsequence.
Dry Run / Execution Steps
Input: nums = [10,9,2,5,3,7,101,18]
Step-by-Step Execution:
| Step | num |
sub (LIS at each step) |
Explanation |
|---|---|---|---|
| 1 | 10 | [10] |
Start LIS |
| 2 | 9 | [9] |
Replace 10 with 9 |
| 3 | 2 | [2] |
Replace 9 with 2 |
| 4 | 5 | [2,5] |
Extend LIS |
| 5 | 3 | [2,3] |
Replace 5 with 3 |
| 6 | 7 | [2,3,7] |
Extend LIS |
| 7 | 101 | [2,3,7,101] |
Extend LIS |
| 8 | 18 | [2,3,7,18] |
Replace 101 with 18 |
Final Output: 4 (LIS is [2,3,7,18])
Alternative Approaches & Why Not?
1. Brute Force – Recursion
- Try all subsequences recursively.
- Time Complexity:
O(2^n)(Exponential) - Space Complexity:
O(n)(Recursion depth) - Why Not? Too slow for large inputs.
2. Dynamic Programming (O(n²))
- Maintain a
dparray wheredp[i]stores LIS ending ati. - Use nested loops to compute
dp[i] = max(dp[j]) + 1forj < i. - Time Complexity:
O(n²) - Space Complexity:
O(n) - Why Not? Slower than
O(n log n)solution.
Best Solution & Why It’s Best
| Approach | Time Complexity | Space Complexity | Optimal? |
|---|---|---|---|
| Brute Force | O(2^n) |
O(n) |
❌ No (Too slow) |
DP (O(n²)) |
O(n²) |
O(n) |
✅ Faster but suboptimal |
| DP + Binary Search | O(n log n) |
O(n) |
✅ Best |
The Binary Search Approach (O(n log n)) is optimal because it efficiently builds sub with minimal replacements.
Conclusion
- Best Approach: DP + Binary Search (
O(n log n)) - Why? Efficient, simple, and avoids extra computations.
- Similar problems to practice:
🚀 This is the fastest way to solve Longest Increasing Subsequence!