Open In App

Longest Increasing Subsequence in C++

Last Updated : 24 Jul, 2024
Comments
Improve
Suggest changes
Like Article
Like
Report

In this article, we will learn the concept of the Longest Increasing Subsequence (LIS) in a given sequence using C++ language. The LIS problem is about finding the longest subsequence of a sequence in which the elements are in sorted order, from lowest to highest, and not necessarily contiguous.

Example:

Input:
int arr[] = { 3, 10, 2, 1, 20 };

Output:
Length of the longest increasing subsequence is: 3
LIS
Longest Increasing Subsequence in C++

How to Find Longest Increasing Subsequence (LIS) in C++?

Below, we have discussed the multiple approaches to find the Longest Increasing Subsequence from a given array in C++.

Method 1: Recursive Approach

In this approach, we will explore all the possible subsequences to find the longest increasing one by comparing each element with previous elements to check if it can be included in the increasing sequence or not.

Approach:

  • Define a function lisRecursive that takes the array arr and its length n.
  • Base Case: If the length is 1, return 1.
  • For each element, recursively compute the LIS ending at each index.
  • Return the maximum length found.

Below is the implementation of the above recursive approach in C++:

C++
// C++ program to find the length of the longest increasing
// subsequence (LIS) using a recursive approach
#include <iostream>
using namespace std;

// Function to return the maximum of two integers
int max(int a, int b) { return (a > b) ? a : b; }

// Recursive function to calculate LIS ending at arr[n-1]
int lisRecursive(int arr[], int n, int* max_ref)
{
    // Base case: single element is an increasing
    // subsequence of length 1
    if (n == 1)
        return 1;
    // Initialize the max ending length
    int res, max_ending_here = 1;

    // Loop through all elements before arr[n-1]
    for (int i = 1; i < n; i++) {
        // Recursively calculate LIS ending at arr[i-1]
        res = lisRecursive(arr, i, max_ref);

        // Update max_ending_here if arr[i-1] is less than
        // arr[n-1] and results in a longer subsequence
        if (arr[i - 1] < arr[n - 1]
            && res + 1 > max_ending_here)
            max_ending_here = res + 1;
    }

    // Update the overall maximum length of LIS found so far
    if (*max_ref < max_ending_here)
        *max_ref = max_ending_here;
    // Return length of LIS ending at arr[n-1]
    return max_ending_here;
}

// Function to calculate the length of the longest
// increasing subsequence
int lis(int arr[], int n)
{
    // Initialize maximum length of LIS
    int max = 1;
    // Call recursive function
    lisRecursive(arr, n, &max);
    // Return the maximum length of LIS
    return max;
}

int main()
{
    // Initialize array and find its size
    int arr[] = { 3, 10, 2, 1, 20 };
    int n = sizeof(arr) / sizeof(arr[0]);

    // Print the length of the longest increasing
    // subsequence
    cout << "Length of LIS is " << lis(arr, n) << endl;

    return 0;
}

Output
Length of LIS is 3

Time Complexity: O(2^n), due to overlapping subproblems.
Auxiliary Space: O(1), excluding internal stack space.

Method 2: Memoization Technique

We can use the memoization technique to improve the recursive approach by storing the results of subproblems to avoid redundant calculations.

Approach:

  • Create a memoization array memo to store the LIS for each index.
  • Define a function lisMemoization that takes arr, n, and memo.
  • Base Case: If the length is 1, return 1.
  • If the result is already computed, return it.
  • Recursively compute and store results for the remaining elements.
  • Return the computed result from the memoization array.

Below is the implementation of the above memoization technique in C++:

C++
// C++ program to find the length of the longest increasing
// subsequence (LIS) using memoization
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;

// Function to return the maximum of two integers
int max(int a, int b) { return (a > b) ? a : b; }

// Recursive function to calculate LIS ending at arr[n-1]
// using memoization
int lisMemoization(int arr[], int n, int* memo)
{
    // Check if result is already computed
    if (memo[n] != -1)
        return memo[n];
    // Initialize the max ending length
    int res, max_ending_here = 1;

    // Loop through all elements before arr[n-1]
    for (int i = 1; i < n; i++) {
        // Recursively calculate LIS ending at arr[i-1] and
        // update max_ending_here
        res = lisMemoization(arr, i, memo);
        if (arr[i - 1] < arr[n - 1]
            && res + 1 > max_ending_here)
            max_ending_here = res + 1;
    }

    // Store the computed value in memo array and return it
    return memo[n] = max_ending_here;
}

// Function to calculate the length of the longest
// increasing subsequence
int lis(int arr[], int n)
{
    // Initialize memoization vector with -1
    vector<int> memo(n + 1, -1);
    // Initialize maximum length of LIS
    int maxLength = 1;

    // Calculate LIS for each element and update maxLength
    for (int i = 1; i <= n; i++)
        maxLength = max(
            maxLength, lisMemoization(arr, i, memo.data()));
    // Return the maximum length of LIS
    return maxLength;
}

int main()
{
    // Initialize array and find its size
    int arr[] = { 3, 10, 2, 1, 20 };
    int n = sizeof(arr) / sizeof(arr[0]);

    // Print the length of the longest increasing
    // subsequence
    cout << "Length of LIS is " << lis(arr, n) << endl;

    return 0;
}

Output
Length of LIS is 3

Time Complexity: O(n^2), due to nested loop.
Auxiliary Space: O(n), for memoization array.

Method 3: Dynamic Programming

In this method, we will use the concept of dynamic programming to iteratively computes the LIS by storing intermediate results, making the process more efficient.

Approach:

  • Create an array lis of size n and initialize all values to 1.
  • For each element, find the longest subsequence ending at that element.
  • Return the maximum value in the lis array, which will be the length of the LIS.

Below is the implementation of the above dynamic programming approach in C++:

C++
// C++ program to find the length of the longest increasing
// subsequence (LIS) using tabulation
#include <iostream>
#include <vector>
using namespace std;

// Function to return the maximum of two integers
int max(int a, int b) { return (a > b) ? a : b; }

// Function to calculate the length of the longest
// increasing subsequence using tabulation
int lisTabulation(int arr[], int n)
{
    // Initialize a vector to store the LIS values for each
    // element
    vector<int> lis(n, 1);

    // Loop through each element starting from the second
    for (int i = 1; i < n; i++) {
        // For each element, compare it with all previous
        // elements
        for (int j = 0; j < i; j++) {
            // If the current element is greater and the LIS
            // value can be increased
            if (arr[i] > arr[j] && lis[i] < lis[j] + 1)
                lis[i] = lis[j] + 1;
        }
    }

    // Initialize the variable to store the maximum length
    // of LIS
    int maxLength = 0;

    // Find the maximum value in the lis vector
    for (int i = 0; i < n; i++)
        maxLength = max(maxLength, lis[i]);

    return maxLength; // Return the maximum length of LIS
}

int main()
{
    // Initialize array and find its size
    int arr[] = { 3, 10, 2, 1, 20 };
    int n = sizeof(arr) / sizeof(arr[0]);

    // Print the length of the longest increasing
    // subsequence
    cout << "Length of LIS is " << lisTabulation(arr, n)
         << endl;

    return 0;
}

Output
Length of LIS is 3

Time Complexity: O(n^2)
Auxiliary Space: O(n), for storing LIS values at each index.

Method 4: Using Binary Search

We can also, use the binary search to improve time complexity by efficiently finding the position where the current element should be placed in the subsequence.

Approach:

  • Create an array tail to store the smallest ending element of all increasing subsequences of length i+1.
  • Use binary search to find the position of the current element in tail.
  • If the element is larger than the largest element in tail, extend tail. Otherwise, replace the existing element in tail.
  • Return the length of the tail array, which is the length of the LIS.

Below is the implementation of the above binary search approach in C++:

C++
// C++ program to find the length of the longest increasing
// subsequence (LIS) using binary search
#include <iostream>
#include <vector>
using namespace std;

// Function to perform binary search on the tail array
int binarySearch(vector<int>& tail, int l, int r, int key)
{
    // Loop until the search space is reduced to one element
    while (r - l > 1) {
        // Find the middle element
        int m = l + (r - l) / 2;

        // Update the search space based on the comparison
        if (tail[m] >= key)
            r = m;
        else
            l = m;
    }
    // Return the position to insert the key
    return r;
}

// Function to calculate the length of the longest
// increasing subsequence using binary search
int lisBinarySearch(int arr[], int n)
{
    // Check if the array is empty
    if (n == 0)
        return 0;

    // Initialize the tail array to store the smallest
    // ending element of each length subsequence
    vector<int> tail(n);
    // Initialize the length of the LIS
    int length = 1;
    // The first element is the initial subsequence
    tail[0] = arr[0];

    // Loop through the array elements
    for (int i = 1; i < n; i++) {
        // If the current element is smaller than the first
        // element in tail
        if (arr[i] < tail[0])
            tail[0] = arr[i];
        // If the current element is greater than the last
        // element in tail
        else if (arr[i] > tail[length - 1])
            tail[length++] = arr[i];
        // If the current element can replace an element in
        // the middle
        else
            tail[binarySearch(tail, -1, length - 1, arr[i])]
                = arr[i];
    }

    // Return the length of the LIS
    return length;
}

int main()
{
    // Initialize the array and find its size
    int arr[] = { 3, 10, 2, 1, 20 };
    int n = sizeof(arr) / sizeof(arr[0]);

    // Print the length of the longest increasing
    // subsequence
    cout << "Length of LIS is " << lisBinarySearch(arr, n)
         << endl;

    return 0;
}

Output
Length of LIS is 3

Time Complexity: O(n log n)
Auxiliary Space: O(n), for storing the tail array.


Next Article
Article Tags :
Practice Tags :

Similar Reads