Open In App

Longest Increasing Subsequence Size (N log N)

Last Updated : 07 May, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Given an array arr[] of size N, the task is to find the length of the Longest Increasing Subsequence (LIS) i.e., the longest possible subsequence in which the elements of the subsequence are sorted in increasing order, in O(N log N).

Examples:            

Input: arr[] = {3, 10, 2, 1, 20}
Output: 3
Explanation: The longest increasing subsequence is 3, 10, 20

Input: arr[] = {3, 2}
Output:1
Explanation: The longest increasing subsequences are {3} and {2}

Input: arr[] = {50, 3, 10, 7, 40, 80}
Output: 4
Explanation: The longest increasing subsequence is {3, 7, 40, 80}

The main idea of the approach is to simulate the process of finding a subsequence by maintaining a list of "buckets" where each bucket represents a valid subsequence. Initially, we start with an empty list and iterate through the input vector arr from left to right.

For each number in arr, we perform the following steps:

  • If the number is greater than the last element of the last bucket (i.e., the largest element in the current subsequence), we append the number to the end of the list. This indicates that we have found a longer subsequence.
  • Otherwise, we perform a binary search on the list of buckets to find the smallest element that is greater than or equal to the current number. This step helps us maintain the property of increasing elements in the buckets.
  • Once we find the position to update, we replace that element with the current number. This keeps the buckets sorted and ensures that we have the potential for a longer subsequence in the future.

Note: The resulting array only stores the length of longest increasing subsequence, and not the actual subsequence. Go through the illustration to clear this doubt.

Illustration:

Example: arr = [3, 4, 5, 1, 2, 3, 4] Let's see why keeping 1 (the smallest value) helps:

We use binary search to find the position where new element is to be inserted.

  1. First three elements: buckets = [3, 4, 5]
  2. arr[3] = 1 buckets = [1, 4, 5] // 1 replaces 3
  3. arr[4] = 2 buckets = [1, 2, 5] // 2 replaces 4 as it's smaller
  4. arr[5] = 3 buckets = [1, 2, 3] // 3 replaces 5
  5. arr[6] = 4 buckets = [1, 2, 3, 4] // 4 is appended as it's larger

This shows that by replacing 3 with 1, we created the opportunity to find the subsequence [1, 2, 3, 4], which is longer than our initial [3, 4, 5]. If we had kept [3, 4, 5], we wouldn't have been able to add 2 to our sequence!

The key insight is that keeping smaller values at each position:

  1. Maintains the same length information
  2. Creates more opportunities for future elements to form longer increasing subsequences
C++
// Binary Search Approach of Finding LIS by
// reducing the problem to longest
// common Subsequence
#include <bits/stdc++.h>
using namespace std;

int lengthOfLIS(vector<int>& arr)
{

    // Binary search approach
    int n = arr.size();
    vector<int> ans;

    // Initialize the answer vector with the
    // first element of arr
    ans.push_back(arr[0]);

    for (int i = 1; i < n; i++) {
        if (arr[i] > ans.back()) {

            // If the current number is greater
            // than the last element of the answer
            // vector, it means we have found a
            // longer increasing subsequence.
            // Hence, we append the current number
            // to the answer vector.
            ans.push_back(arr[i]);
        }
        else {

            // If the current number is not
            // greater than the last element of
            // the answer vector, we perform
            // a binary search to find the smallest
            // element in the answer vector that
            // is greater than or equal to the
            // current number.

            // The lower_bound function returns
            // an iterator pointing to the first
            // element that is not less than
            // the current number.
            int low = lower_bound(ans.begin(), ans.end(),
                                  arr[i])
                      - ans.begin();

            // We update the element at the
            // found position with the current number.
            // By doing this, we are maintaining
            // a sorted order in the answer vector.
            ans[low] = arr[i];
        }
    }

    // The length of the answer vector
    // represents the length of the
    // longest increasing subsequence.
    return ans.size();
}

// Driver program to test above function
int main()
{
    vector<int> arr = { 10, 22, 9, 33, 21, 50, 41, 60 };
    printf("Length of LIS is %d\n", lengthOfLIS(arr));
    return 0;
}
Java
import java.util.*;

public class GFG {
    static int lengthOfLIS(int[] arr) {
        // Binary search approach
        int n = arr.length;
        List<Integer> ans = new ArrayList<>();

        // Initialize the answer list with the
        // first element of arr
        ans.add(arr[0]);

        for (int i = 1; i < n; i++) {
            if (arr[i] > ans.get(ans.size() - 1)) {
                // If the current number is greater
                // than the last element of the answer
                // list, it means we have found a
                // longer increasing subsequence.
                // Hence, we append the current number
                // to the answer list.
                ans.add(arr[i]);
            } else {
                // If the current number is not
                // greater than the last element of
                // the answer list, we perform
                // a binary search to find the smallest
                // element in the answer list that
                // is greater than or equal to the
                // current number.

                // The binarySearch method returns
                // the index of the first element that is not less than
                // the current number.
                int low = Collections.binarySearch(ans, arr[i]);

                // We update the element at the
                // found position with the current number.
                // By doing this, we are maintaining
                // a sorted order in the answer list.
                if (low < 0) {
                    low = -(low + 1);
                }
                ans.set(low, arr[i]);
            }
        }

        // The size of the answer list
        // represents the length of the
        // longest increasing subsequence.
        return ans.size();
    }

    // Driver program to test above function
    public static void main(String[] args) {
        int[] arr = {10, 22, 9, 33, 21, 50, 41, 60};
        System.out.println("Length of LIS is " + lengthOfLIS(arr));
    }
}
Python
def lengthOfLIS(arr):
    # Binary search approach
    n = len(arr)
    ans = []

    # Initialize the answer list with the
    # first element of arr
    ans.append(arr[0])

    for i in range(1, n):
        if arr[i] > ans[-1]:
            # If the current number is greater
            # than the last element of the answer
            # list, it means we have found a
            # longer increasing subsequence.
            # Hence, we append the current number
            # to the answer list.
            ans.append(arr[i])
        else:
            # If the current number is not
            # greater than the last element of
            # the answer list, we perform
            # a binary search to find the smallest
            # element in the answer list that
            # is greater than or equal to the
            # current number.
            low = 0
            high = len(ans) - 1
            while low < high:
                mid = low + (high - low) // 2
                if ans[mid] < arr[i]:
                    low = mid + 1
                else:
                    high = mid
            # We update the element at the
            # found position with the current number.
            # By doing this, we are maintaining
            # a sorted order in the answer list.
            ans[low] = arr[i]

    # The length of the answer list
    # represents the length of the
    # longest increasing subsequence.
    return len(ans)

# Driver program to test above function
if __name__ == "__main__":
    arr = [10, 22, 9, 33, 21, 50, 41, 60]
    print("Length of LIS is", lengthOfLIS(arr))
C#
using System;
using System.Collections.Generic;

class GFG
{
    static int LengthOfLIS(List<int> arr)
    {
        // Binary search approach
        int n = arr.Count;
        List<int> ans = new List<int>();

        // Initialize the answer list with the
        // first element of arr
        ans.Add(arr[0]);

        for (int i = 1; i < n; i++)
        {
            if (arr[i] > ans[ans.Count - 1])
            {
                // If the current number is greater
                // than the last element of the answer
                // list, it means we have found a
                // longer increasing subsequence.
                // Hence, we append the current number
                // to the answer list.
                ans.Add(arr[i]);
            }
            else
            {
                // If the current number is not
                // greater than the last element of
                // the answer list, we perform
                // a binary search to find the smallest
                // element in the answer list that
                // is greater than or equal to the
                // current number.

                // The BinarySearch method returns
                // the index of the first element that is not less than
                // the current number.
                int low = ans.BinarySearch(arr[i]);

                // We update the element at the
                // found position with the current number.
                // By doing this, we are maintaining
                // a sorted order in the answer list.
                if (low < 0)
                {
                    low = ~low;
                }
                ans[low] = arr[i];
            }
        }

        // The count of the answer list
        // represents the length of the
        // longest increasing subsequence.
        return ans.Count;
    }

    // Driver program to test above function
    static void Main()
    {
        List<int> arr = new List<int> { 10, 22, 9, 33, 21, 50, 41, 60 };
        Console.WriteLine("Length of LIS is " + LengthOfLIS(arr));
    }
}
JavaScript
function lengthOfLIS(arr) {
    // Binary search approach
    const n = arr.length;
    const ans = [];

    // Initialize the answer array with the first element of arr
    ans.push(arr[0]);

    for (let i = 1; i < n; i++) {
        if (arr[i] > ans[ans.length - 1]) {
            // If the current number is greater than the last element 
            // of the answer array, it means we have found a 
            // longer increasing subsequence. Hence, we push the current number
            // to the answer array.
            ans.push(arr[i]);
            
        } else {
            // If the current number is not greater than the last element of
            // the answer array, we perform a binary search to find the smallest
            // element in the answer array that is greater than or equal to the
            // current number.

            // The indexOf function returns the first index at which the current
            // number can be inserted to maintain sorted order.
            const low = ans.findIndex((el) => el >= arr[i]);

            // We update the element at the found position with the current number.
            // By doing this, we are maintaining a sorted order in the answer array.
            ans[low] = arr[i];
        }
    }

    // The length of the answer array represents the length of the
    // longest increasing subsequence.
    return ans.length;
}

// Driver program to test the function
const arr = [10, 22, 9, 33, 21, 50, 41, 60];
console.log("Length of LIS is " + lengthOfLIS(arr));

Output
Length of LIS is 5

Time Complexity: O(n*log(n)) where n is the size of the input vector arr. 
Auxiliary Space: O(n)


Next Article

Similar Reads