300. Longest Increasing Subsequence (LIS)
https://leetcode.com/problems/longest-increasing-subsequence/

1. DP




O(n^2)
從原題可以看出來, 要找到最長遞增子序列
一定會基於前面的狀態
if (nums[i] > nums[j]) {
dp[i] = Math.max(dp[i], dp[j]+1)
}
初始狀態:
dp[i] 所有元素置 1,含義是每個元素都至少可以單獨成為子序列,此時長度都為 1。
所以 j 要在 i 前面, nums[i] > nums[j]

time: O(n^2)
space: O(n)
greedy + binary search
time: O(nlogn)
space: O(n)
Latest
Explaination:
🔍 Goal of Leetcode 300
Given an array nums[], return the length of the longest increasing subsequence (LIS).
The brute-force or DP approach is O(n2)O(n^2)O(n2). But this solution runs in O(nlogn)O(n \log n)O(nlogn) using binary search.
✅ Core Idea (High-level)
Maintain a list dp, where:
dp[i]is the smallest possible tail of an increasing subsequence of lengthi + 1.
🧠 Why this works:
Let’s say we build subsequences ending at:
This means:
We have an increasing subsequence of length 3 ending with
9If a new number
6comes in, we could replace9with6(making it easier to extend future subsequences)
So the idea is: always keep dp as optimized as possible, replacing with smaller values if we can.
✨ Breakdown of the code:
🔹 dp.add(nums[0]);
dp.add(nums[0]);Start with the first number — we’ve found a subsequence of length 1.
🔹 For each nums[i]:
nums[i]:If it’s smaller than everything so far:
→ Replace the first element (start new possible LIS).
If it's larger than the last element:
→ Extend the LIS.
Otherwise:
→ Find the first element in
dpgreater than or equal tonums[i]and replace it to keep the sequence tail as small as possible.
🔍 About binarySearch(dp, num)
binarySearch(dp, num)Your version of binarySerach() is slightly more complicated than necessary.
The goal is to find the first element ≥ num.
Here’s a cleaner and more standard version:
This returns the leftmost index where dp.get(index) >= num, which is what we want to replace.
🧪 Example:
nums = [10, 9, 2, 5, 3, 7, 101, 18]
Step-by-step dp:
Final dp.size() = 4, so LIS = 4.
✅ Final Notes
dpdoesn't store an actual LIS, just helps track the length.The trick is: we only replace, never remove — the
dplist is always valid.
Let me know if you’d like to adapt this code to recover the actual LIS sequence, or if you want a visual animation of how it builds!
4o
You said:
but seems 2, 3, 7, 18 is a LIS?
ChatGPT said:
Yes! You're absolutely right — in the example:
One possible Longest Increasing Subsequence (LIS) is:
And that's exactly what the algorithm’s dp ends up with at the end:
🔍 Important clarification:
The dp list does not always represent an actual LIS from the array, but:
It always maintains a list such that:
Its length equals the length of the LIS,
And the values are the smallest possible ending values for increasing subsequences of various lengths.
🧠 Why this matters:
The real LIS might use different elements from the original array — this greedy + binary search method only gives you the length, not necessarily the actual sequence.
In this case, dp = [2, 3, 7, 18] does happen to be a valid LIS, but:
That’s a coincidence — it won't always reconstruct the actual sequence.
It's just a helpful structure to find the length efficiently in O(nlogn)O(n \log n)O(nlogn).
🧪 Counter-example:
Step-by-step dp:
3→ [3]5→ [3, 5]7→ [3, 5, 7]1→ [1, 5, 7] ← replaced 3 with 12→ [1, 2, 7]8→ [1, 2, 7, 8]
Final dp = [1, 2, 7, 8]
LIS length = 4 ✔
But [3, 5, 7, 8] is also a valid LIS — dp doesn't give the original one.
✅ Summary
Yes, in your case
dp = [2, 3, 7, 18]is a valid LIS.But that’s not guaranteed —
dpis a tool to get the LIS length, not necessarily the actual sequence.
Would you like to see how to recover the actual LIS in O(nlogn)O(n \log n)O(nlogn)?
readable version
idea : Patience Sort

dp[0] means length 1's 結尾最小的 LIS
dp[1] means length 2 結尾最小的 LIS


num: 10

num: 9

num: 2

num: 5

num: 3

num: 7

num: 21

num: 18


DFS + memo
O(n^2)
Last updated
Was this helpful?