Have you ever found yourself wondering about the longest chain of increasing numbers in a sequence? Well, you're not alone! This seemingly simple question leads us to an fascinating algorithmic problem known as the Longest Increasing Subsequence (LIS).
The Longest Increasing Subsequence problem is a classic challenge in computer science that has applications in various fields, from analyzing stock market trends to optimizing network routing protocols. In this blog post, we'll dive deep into this problem, exploring different approaches to solve it and understanding their trade-offs.
Before we jump into the solutions, let's clarify what we mean by a Longest Increasing Subsequence. Given a sequence of numbers, the LIS is the longest subsequence (not necessarily contiguous) where each element is strictly greater than the previous one.
For example, consider the sequence: [3, 1, 8, 2, 5]
Some increasing subsequences are:
The Longest Increasing Subsequence is [1, 2, 5], with a length of 3.
Now that we understand the problem, let's explore different approaches to solve it.
The most straightforward approach is to generate all possible subsequences, check if each one is increasing, and keep track of the longest one. While this method guarantees the correct answer, it's extremely inefficient with a time complexity of O(2^n), making it impractical for large inputs.
A more efficient solution uses dynamic programming. The idea is to build up the solution for larger subsequences using the solutions of smaller subsequences.
Here's how it works:
dp
where dp[i]
stores the length of the LIS ending at index i.dp
to 1, as each individual element is an increasing subsequence of length 1.dp[i]
to be the maximum of its current value and dp[j] + 1
, where j is the index of the smaller element.dp
is the length of the LIS.Let's implement this in Python:
def longest_increasing_subsequence(nums): if not nums: return 0 n = len(nums) dp = [1] * n for i in range(1, n): for j in range(i): if nums[i] > nums[j]: dp[i] = max(dp[i], dp[j] + 1) return max(dp) # Example usage sequence = [10, 9, 2, 5, 3, 7, 101, 18] print(longest_increasing_subsequence(sequence)) # Output: 4
This solution has a time complexity of O(n^2) and space complexity of O(n), which is a significant improvement over the brute force approach.
We can optimize the previous solution further by using binary search. Instead of checking all previous elements, we can maintain a sorted array of potential candidates for the LIS.
Here's how this optimized approach works:
tails
where tails[i]
is the smallest tail of all increasing subsequences of length i+1.tails
.tails
at the end is the length of the LIS.Let's implement this in Python:
import bisect def longest_increasing_subsequence_optimized(nums): tails = [] for num in nums: if not tails or num > tails[-1]: tails.append(num) else: idx = bisect.bisect_left(tails, num) tails[idx] = num return len(tails) # Example usage sequence = [10, 9, 2, 5, 3, 7, 101, 18] print(longest_increasing_subsequence_optimized(sequence)) # Output: 4
This optimized solution has a time complexity of O(n log n) and space complexity of O(n), making it suitable for larger inputs.
The Longest Increasing Subsequence algorithm finds applications in various domains:
23/09/2024 | DSA
15/11/2024 | DSA
06/12/2024 | DSA
13/10/2024 | DSA
08/12/2024 | DSA
05/12/2024 | DSA
15/11/2024 | DSA
06/12/2024 | DSA
08/12/2024 | DSA
23/09/2024 | DSA
23/09/2024 | DSA