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)

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(nlog⁡n)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 length i + 1.


🧠 Why this works:

Let’s say we build subsequences ending at:

This means:

  • We have an increasing subsequence of length 3 ending with 9

  • If a new number 6 comes in, we could replace 9 with 6 (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]);

Start with the first number — we’ve found a subsequence of length 1.

🔹 For each nums[i]:

  1. If it’s smaller than everything so far:

    → Replace the first element (start new possible LIS).

  2. If it's larger than the last element:

    → Extend the LIS.

  3. Otherwise:

    → Find the first element in dp greater than or equal to nums[i] and replace it to keep the sequence tail as small as possible.


🔍 About 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

  • dp doesn't store an actual LIS, just helps track the length.

  • The trick is: we only replace, never remove — the dp list 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(nlog⁡n)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 1

  • 2 → [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 guaranteeddp is 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(nlog⁡n)O(n \log n)O(nlogn)?

readable version

idea : Patience Sort

https://leetcode-cn.com/problems/longest-increasing-subsequence/solution/dong-tai-gui-hua-er-fen-cha-zhao-tan-xin-suan-fa-p/

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?