Aller au contenu principal
Interactive Algorithm Education

Visualize & Master Algorithms & Data Structures

Explore classic & modern sorting algorithms, efficient searching techniques, and interactive data structure visualizations — all with real-time step-by-step animation, comparisons, swaps, and Big-O metrics.

Dynamic Programming Visualizer

Longest Increasing Subsequence

Étape 0 / 0
Speed 100ms
Step Progress 0 / 0
Table Size 0×0
Cells Filled 0
Status Ready
Uncomputed
Filling
Optimal path
0 / base case
Step Explanation

Select an algorithm and press Play to watch the table fill in.

—
Pseudocode
 

Longest Increasing Subsequence

Intermediate (3/5) ~45 minutes Subsequence ordering dp[i] = best length ending at i Strict vs non-strict increasing Patience-sorting / binary-search optimization Prereqs: Dynamic programming basics, Subsequences, Binary search (for the O(n log n) variant)
Quick Reference

Longest Increasing Subsequence

The Longest Increasing Subsequence problem finds the longest subsequence of an array whose values are strictly increasing. The classic O(n²) DP tracks the length of the best increasing subsequence ending at each index.

Difficulty: Intermediate (3/5) dp

Complexity

Best Time
O(n²)
Average Time
O(n²)
Worst Time
O(n²)
Space
O(n)

When to Use

For sequencing and scheduling problems: longest chain, patience sorting, and box-stacking variants.

Pros

  • Simple O(n²) DP with an O(n log n) optimization via binary search
  • Clear optimal substructure
  • Great introduction to DP state design

Cons

  • O(n²) worst case in the simple form
  • Reconstruction adds complexity
  • Requires strictly increasing values for the classic form

History

The LIS problem dates to the study of patience sorting in the 1960s and was connected to dynamic programming by Fredman and Knuth. An O(n log n) solution exists using patience-sorting piles.

Find the longest subsequence whose elements are strictly increasing — elements appear in original order, values rise as you go. Unlike a subarray, gaps are allowed.

This deceptively simple problem has two classic solutions:

  • O(n²) DP — clean and easy to reconstruct.
  • O(n log n) — a clever binary-search optimization.

Both are worth knowing.

How It Works

O(n²) DP: define dp[i] = length of the longest increasing subsequence that ends at index i.

  1. Initialize: every dp[i] = 1 — a single element is always a valid subsequence.
  2. For each i: scan all j < i; if nums[j] < nums[i], extend that subsequence: dp[i] = max(dp[i], dp[j] + 1).
  3. Answer: max(dp) over all indices.

O(n log n) optimization: maintain an array tails where tails[k] = the smallest possible ending value of an increasing subsequence of length k. For each element, binary-search the insertion point and overwrite — tails stays sorted, and its length is the answer.

Key Insight

The O(n²) version keeps the full history because the best subsequence ending at i may chain onto any earlier element. The tails trick compresses that history: you never need the actual subsequence for the length — only the minimal possible last value per length, which keeps tails monotonic and binary-searchable.

Worked Example

The visualizer runs on [3, 1, 8, 2, 5, 9, 4, 6, 7]:

Element318259467
dp[i]112234345
best subseq311,81,21,2,51,2,5,91,2,41,2,4,61,2,4,6,7

The answer is max(dp) = 5, and the visualizer’s backtrack reveals 1, 2, 4, 6, 7. The tails array ends at [1, 2, 4, 6, 7] — length 5, matching.

Edge Cases & Pitfalls

  • Strict vs non-strict — 1, 1, 1 has LIS length 1 (strict, use <) but 3 (non-decreasing, use ≤). Pick the right comparison.
  • Unsorted input — the answer is at least 1 (any single element): never 0 for a non-empty array.
  • Backtracking with tails — the tails array stores values, not a valid subsequence. To recover the actual sequence, keep parent pointers or stick to the O(n²) table.
  • Multiple answers — many subsequences can tie for the max length.

Comparison: O(n²) DP vs O(n log n) tails

AspectO(n²) DPtails array
TimeO(n²)O(n log n)
SpaceO(n)O(n)
ReconstructEasyTricky (needs parents)
Best forSmall n / teachingLarge n

Applications

  • Longest chain / nesting problems — boxes, envelopes, intervals (sort + LIS)
  • Routing & scheduling — patience sorting lies at the heart of bridge-card play
  • Data pipeline ordering — the minimum number of increasing subsequences to partition an array

Practice Trajectory

  1. Hand-fill dp[i] for the visualizer array and confirm max(dp) = 5.
  2. Trace which earlier element each dp[i] chains onto.
  3. Re-derive the tails insertion for a few elements and confirm monotonicity.
  4. Repeat the same problem allowing non-decreasing sequences and note the difference.
  5. Implement the parent-pointer version to reconstruct the actual subsequence.