Given an unsorted array of integers, find the length of the longest increasing subsequence.
For example:
[10, 9, 2, 5, 3, 7, 101, 18]
4
[2, 3, 7, 101]
, therefore the length is 4
.Another example:
[0, 1, 0, 3, 2, 3]
4
[0, 1, 2, 3]
.Constraints:
Could you describe your approach and implement the solution? Consider the time and space complexity of your solution.
In the first example, [10, 9, 2, 5, 3, 7, 101, 18], a longest increasing subsequence is [2, 3, 7, 18]. Note that subsequences are not required to be contiguous. Another longest increasing subsequence is [2, 5, 7, 101]. It is important to understand what "increasing" means. The numbers must be strictly increasing (e.g., 2, 3, 7 is increasing, but 2, 2, 7 is not). Another important point to note is that there may be multiple longest increasing subsequences, but your function should return the length of such a subsequence, not the subsequence itself.
Given an unsorted array of integers, find the length of the longest increasing subsequence.
For example:
[10, 9, 2, 5, 3, 7, 101, 18]
4
[2, 3, 7, 101]
, therefore the length is 4
.Another example:
[0, 1, 0, 3, 2, 3]
4
[0, 1, 2, 3]
.Constraints:
We can solve this problem using dynamic programming. We will maintain an array dp
where dp[i]
stores the length of the longest increasing subsequence ending at index i
. We iterate through the input array and for each element, we iterate through all the preceding elements. If a preceding element is smaller than the current element, we update dp[i]
with the maximum of its current value and dp[j] + 1
, where j
is the index of the preceding element. Finally, we return the maximum value in the dp
array.
Another, more efficient approach uses binary search. We maintain an array tails
where tails[i]
is the smallest tail of all increasing subsequences with length i+1
. For each number in the input array, we try to extend an existing subsequence by finding the smallest tail that is greater than or equal to the current number. If we find such a tail, we replace it with the current number because the current number gives us an increasing subsequence of the same length but with a smaller tail. If we don't find such a tail, it means the current number extends the longest increasing subsequence by 1. The length of the tails
array is the length of the longest increasing subsequence.
def longest_increasing_subsequence_dp(nums):
n = len(nums)
if n == 0:
return 0
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
nums1 = [10, 9, 2, 5, 3, 7, 101, 18]
print(f"Input: {nums1}, Output: {longest_increasing_subsequence_dp(nums1)}")
nums2 = [0, 1, 0, 3, 2, 3]
print(f"Input: {nums2}, Output: {longest_increasing_subsequence_dp(nums2)}")
import bisect
def longest_increasing_subsequence_binary_search(nums):
tails = []
for num in nums:
i = bisect.bisect_left(tails, num)
if i == len(tails):
tails.append(num)
else:
tails[i] = num
return len(tails)
# Example usage
nums1 = [10, 9, 2, 5, 3, 7, 101, 18]
print(f"Input: {nums1}, Output: {longest_increasing_subsequence_binary_search(nums1)}")
nums2 = [0, 1, 0, 3, 2, 3]
print(f"Input: {nums2}, Output: {longest_increasing_subsequence_binary_search(nums2)}")
dp
of size n
to store the lengths of the longest increasing subsequences ending at each index.tails
array can grow up to the size of the input array if the input array is strictly increasing.