Problem overview
The method lengthOfLIS receives an array of integers, nums, and returns the length of its longest strictly increasing subsequence. A subsequence is what remains after deleting some elements, possibly none, without reordering the rest. It is strictly increasing when every element is larger than the one before it, so two equal values can never both be in it. Only the length is returned, not the subsequence itself.
For nums = [10, 9, 2, 5, 3, 7, 101, 18] the answer is 4. One such subsequence is [2, 3, 7, 101], and [2, 5, 7, 18] works too. No subsequence has 5 elements. For [0, 1, 0, 3, 2, 3] the answer is 4 ([0, 1, 2, 3]), and for [7, 7, 7, 7, 7, 7, 7] it is 1, because equal values don't increase.
The statement sets O(n²) time and O(n) extra space, with n up to 2500.
Approach: the best subsequence ending at each index
Trying every subsequence means looking at 2^n choices, far too many. Instead, ask a smaller question for each position: what is the longest strictly increasing subsequence that ends with nums[i]? Call that number dp[i].
Every dp[i] is at least 1, because nums[i] alone is a valid subsequence. It can be longer if some earlier element nums[j] (with j < i) is smaller than nums[i]: any subsequence ending at j can be extended by nums[i], giving length dp[j] + 1. Taking the largest of these over all valid j gives dp[i].
The subsequence can end anywhere, so the final answer is the largest value in dp, not the last one.
The trace for [10, 9, 2, 5, 3, 7, 101, 18] shows how entries build on each other:
nums = [10, 9, 2, 5, 3, 7, 101, 18]
dp = [ 1, 1, 1, 2, 2, 3, 4, 4]
Here dp[5] = 3 belongs to the 7: it extends the 5 (or the 3), which has dp 2. Then 101 extends that 7 for dp[6] = 4, and 18 does the same. The largest entry is 4.
The statement mentions a faster O(n log n) method that keeps, for each length, the smallest value a subsequence of that length can end with, and places each element in that list with binary search. This lesson uses the simpler O(n²) table, which is the natural first step.
Step by step
- Store the length of
numsin anintnamedn, because both loops run over the indexes. - Create
dpwithnentries and fill it with 1 usingArrays.fill. A newintarray holds zeros, but every element is a subsequence of length 1 by itself. - Create
beststarting at 0 to track the largestdp[i]seen. - Loop
iover every index from 0 ton - 1. Eachdp[i]only needs entries before it, and those are finished by the time the loop reachesi. - Loop
jover every earlier index, from 0 toi - 1. Only elements beforenums[i]can come before it in a subsequence. Fori = 0the inner loop doesn't run. - Compare the values with
nums[j] < nums[i]. The test is strict: an equal value can't follow, so<=would be a bug. - Update
dp[i]withMath.max(dp[i], dp[j] + 1). Extending the best subsequence ending atjis one candidate, and the loop keeps the best over allj. - Update
bestafter the inner loop, whendp[i]is final. - Return
bestafter the outer loop.
The solution in Java
class Solution {
public int lengthOfLIS(int[] nums) {
int n = nums.length;
int[] dp = new int[n];
Arrays.fill(dp, 1);
int best = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (nums[j] < nums[i]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
best = Math.max(best, dp[i]);
}
return best;
}
}
The Java details worth a closer look:
Arrays.fill(array, value)sets every entry of the array to the value. It lives injava.util, and the lesson's editor already provides that import. In a file of your own, addimport java.util.Arrays;.- Array defaults.
new int[n]starts with zeros. That is why the fill is needed: a zero would wrongly say that an element can't form even a subsequence of one. Math.maxtakes twointvalues and returns the larger one, so the update never lowers an entry.- No overflow worries. The values in
dpnever exceedn, and the comparison ofnums[j]withnums[i]doesn't do arithmetic, so large element values are safe.
Complexity
The two loops look at each pair j < i once, which is n · (n - 1) / 2 pairs, so the time is O(n²). For n = 2500 that is about 3 million comparisons. The dp array holds n integers, so the extra space is O(n).
Edge cases and common mistakes
- Returning
dp[n - 1]. The longest subsequence need not end at the last element. For[1, 2, 3, 0], the last entry is 1 while the answer is 3. That is whybestexists. - Using
<=instead of<. With all equal values, such as seven 7s, the answer would become 7 instead of 1. - Forgetting to start
dpat 1. With zeros, every length comes out one too small. - Confusing subsequence with subarray. Elements don't have to be neighbours. In
[0, 1, 0, 3, 2, 3]the subsequence[0, 1, 2, 3]skips two elements. - One element. The inner loop never runs and the answer is 1.
Testing
These results come from running the solution:
nums | Result |
|---|---|
[10, 9, 2, 5, 3, 7, 101, 18] | 4 |
[0, 1, 0, 3, 2, 3] | 4 |
[7, 7, 7, 7, 7, 7, 7] | 1 |
[5] | 1 |
[5, 4, 3, 2, 1] | 1 |
[1, 2, 3, 4] | 4 |
[-3, -1, -2, 0] | 3 |
