Longest Increasing Subsequence - JS Solution

JS
Editorial

Solution explanation

Solution explanation

Approach

Goal

Find the length of the longest increasing subsequence (LIS) using a greedy + binary search method.

Key Idea

Maintain an array minLisTails[] where minLisTails[i] is the smallest possible last element of any increasing subsequence of length i + 1. This structure may not equal an actual LIS, but its length always equals the LIS length.

How We Place Each Number

For each incoming value num (i.e., nums[index]), we find its replacement position in minLisTails[] — the first index idx such that minLisTails[idx] ≥ num. Replacing there keeps the tail for length idx + 1 as small as possible, maximizing our ability to extend to longer subsequences later. If no such index exists (the value is larger than all current tails), we append it, which extends the LIS by one.

Binary Search (Replacement Position)

  • We binary search over minLisTails using pointers left and right. At each step, inspect minLisTails[mid] (the element currently highlighted in blue in the visualization).
  • If minLisTails[mid] < num: move left rightward, since the replacement spot (if it exists) must be after mid.
    💡 If no such spot exists (i.e., the value is greater than all tails), we will append it to extend the LIS.
  • If minLisTails[mid] ≥ num: move right leftward (this mid or an earlier index could be the replacement spot).
  • When the search finishes, left is the first index satisfying minLisTails[left] ≥ num — i.e., the replacement position. If left === minLisTails.length, it means that no replacement point can be found and num is the greatest value encountered so far, so we append it to extend the LIS.

Why This Works

By keeping each minLisTails[i] as small as possible, we maximize the ability to extend subsequences to greater lengths. Replacements do not reduce the length already achieved; they only improve the “future potential” of increasing thatlength. Appends increase the achieved length by one.

Result

After processing all numbers, minLisTails.length is the length of the LIS.

Time Complexity

O(n × log n), where n is the number of elements in the input array.

For each element num in the array, we perform a binary search on minLisTails[] to find its replacement position. The size of minLisTails[] grows gradually from 1 up to at most n, so the total work is: log(1) + log(2) + ... + log(n) = log(n!), which simplifies to O(n × log n).

This is much faster than the O(n²) dynamic programming approach, since we use binary search to locate each element’s position efficiently rather than scanning all previous elements.

Space Complexity

O(n), where n is the number of elements in the input array.

The algorithm uses an auxiliary array minLisTails[], which at most grows to size n — in the case where the entire array is strictly increasing. Apart from this, only a few constant-sized variables (left, right, mid, etc.) are used during binary search.

Therefore, the overall space requirement is O(n).

Visucodize editorial solution titled JS Solution for LeetCode's Longest Increasing Subsequence coding problem. Includes code and may include explanation. You can animate the code step by step using the provided input by clicking the run button, or fork it locally to update the code and input for custom visualization. View the original problem at https://leetcode.com/problems/longest-increasing-subsequence.