Longest Increasing Subsequence

Problem Overview
The problem at hand is the "Longest Increasing Subsequence." Your task is to find the length of the longest subsequence of a given sequence where the subsequence’s elements are in sorted order, lowest to highest, and are not necessarily contiguous. This is a classic dynamic programming problem that focuses on manipulating sequences of numbers efficiently.
Understanding the Problem
A subsequence is a derived sequence obtained by deleting some or no elements of the array without changing the order of the remaining elements. The "Longest Increasing Subsequence" (LIS) problem requires us to identify the longest subsequence where each element is strictly greater than the previous one.
Example:
Given the array nums = [10, 9, 2, 5, 3, 7, 101, 18]
, the LIS is [2, 3, 7, 101]
which has a length of 4.
Key DSA Concepts and Theory
Dynamic Programming:
Dynamic programming is a method for solving complex problems by breaking them down into simpler subproblems. It is applicable to problems that have overlapping subproblems and optimal substructure.
Optimal Substructure: A problem exhibits optimal substructure if an optimal solution can be constructed from optimal solutions of its subproblems.
Overlapping Subproblems: The problem can be broken down into subproblems that are reused several times.
For the LIS problem, dynamic programming can be applied to store intermediate results which help in building the solution incrementally.
Binary Search:
Binary search is a search algorithm that finds the position of a target value within a sorted array. It is used in an efficient optimization of the LIS problem where we want to improve time complexity by reducing the search space.
Solution Approach
Dynamic Programming Approach:
Initialize an Array: Create an array
dp[]
wheredp[i]
holds the length of the longest increasing subsequence ending at indexi
. Initialize all entries ofdp[]
as 1 since the minimum LIS ending with each element is the element itself.Iterate over Elements: For each element
nums[i]
, check all elementsnums[j]
before it. Ifnums[i]
is larger thannums[j]
, updatedp[i]
ifdp[j] + 1
is greater thandp[i]
.Result Evaluation: The length of the longest increasing subsequence is the maximum value in the
dp[]
array.
C++ Code:
#include <vector>
#include <algorithm>
class Solution {
public:
int lengthOfLIS(std::vector<int>& nums) {
if (nums.empty()) return 0;
int n = nums.size();
std::vector<int> dp(n, 1);
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) {
dp[i] = std::max(dp[i], dp[j] + 1);
}
}
}
return *std::max_element(dp.begin(), dp.end());
}
};
Binary Search Optimized Approach:
Use List to Track Ends: Use a list
lis
to track the end elements of potential increasing subsequences.Traverse the Array: For each element
num
, use binary search to find its positionpos
in thelis
.Replace or Extend LIS: If
pos
equals the size of the list, appendnum
tolis
; else, replacelis[pos]
withnum
.Result Evaluation: The size of the list
lis
gives the length of the LIS.
Java Code:
import java.util.ArrayList;
import java.util.List;
class Solution {
public int lengthOfLIS(int[] nums) {
List<Integer> lis = new ArrayList<>();
for (int num : nums) {
int pos = binarySearch(lis, num);
if (pos < lis.size()) {
lis.set(pos, num);
} else {
lis.add(num);
}
}
return lis.size();
}
private int binarySearch(List<Integer> lis, int target) {
int low = 0, high = lis.size() - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (lis.get(mid) < target) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return low;
}
}
Time and Space Complexity Analysis
Dynamic Programming Approach:
- Time Complexity: O(n^2), where
n
is the length of the input array. This is because of the nested iteration over the array elements. - Space Complexity: O(n) because of the
dp
array.
Binary Search Optimized Approach:
- Time Complexity: O(n log n), due to binary search operations within the list which track the end of increasing subsequences.
- Space Complexity: O(n), as space is required to store the
lis
list.
Common Mistakes to Avoid
- Failing to update
dp[i]
properly when a longer subsequence is found. - Confusing subsequences with substrings or requiring the subsequence elements to be contiguous.
Similar Problems on LeetCode
- Longest Common Subsequence - LeetCode 1143
- Increasing Triplet Subsequence - LeetCode 334
- Longest Increasing Path in a Matrix - LeetCode 329
Additional Resources and References
- Dynamic Programming on LeetCode
- Binary Search Algorithms
- Books: "Introduction to Algorithms" by Cormen, Leiserson, Rivest, and Stein
- Online Courses: Coursera's Algorithms Specialization by Stanford University
This article aims to give a thorough understanding of solving the Longest Increasing Subsequence problem using dynamic programming and binary search techniques, equipping you with both a fundamental and optimized solution to tackle similar questions efficiently.