Printing Longest Increasing Subsequence (LIS)

Last Updated : 16 Sep, 2026

Given an array of integers arr[], return the Longest Increasing Subsequence (LIS) of the given array. LIS is the longest subsequence where each element is strictly greater than the previous one.

If multiple LIS exist, return the one that appears first based on the lexicographical order of indices (i.e., the earliest combination of positions from the original sequence).

Examples:  

Input: arr[] = [10, 20, 3, 40]
Output: [10, 20, 40]
Explanation: [10, 20, 40] is the longest subsequence where each number is greater than the previous one, maintaining the original order.

Input: arr[] = [10, 22, 9, 33, 21, 50, 41, 60, 80]
Output: [10, 22, 33, 50, 60, 80]
Explanation: There are multiple longest Increasing subsequence of length 6, that is [10, 22, 33, 50, 60, 80] and [10 22 33 41 60 80]. The first one has lexicographic smallest order of indices.

Try It Yourself
redirect icon

[Naive Approach] DP with Subsequence Storage - O(n^3) Time and O(n^2) Space

The idea is to extend the bottom-up DP solution of LIS problem.

To also construct the actual LIS, we maintain an array of arrays L, where L[i] stores the longest increasing subsequence that ends at index i. Initially, L[i] contains only arr[i]. If arr[prev] < arr[i], we can extend the subsequence stored in L[prev] by adding arr[i].

For example, for arr = [3, 2, 6, 4, 5, 1]:

L[0] = [3]
L[1] = [2]
L[2] = [2, 6]
L[3] = [2, 4]
L[4] = [2, 4, 5]
L[5] = [1]

Finally, we choose the longest subsequence among all L[i]. If multiple subsequences have the same length, we choose the one whose sequence of original indices is lexicographically smallest.

  • Initialize lisList[i] to store the longest increasing subsequence ending at index i.
  • Set lisList[0] to contain the first element of the array.
  • For each index i, check all previous indices prev < i.
  • If arr[i] > arr[prev] and extending lisList[prev] gives a longer subsequence, copy lisList[prev] to lisList[i].
  • Append arr[i] to lisList[i] to complete the subsequence ending at index i.
  • Finally, traverse all lisList entries and return the subsequence with the maximum length.
C++
#include <iostream>
#include <vector>
using namespace std;

vector<int> getLIS(const vector<int> &arr)
{
    int n = arr.size();

    // lisList[i] - The longest increasing
    // subsequence ending with arr[i]
    vector<vector<int>> lisList;

    // Initialize L[i] as empty lists
    for (int i = 0; i < n; i++)
    {
        lisList.push_back(vector<int>());
    }

    // Base case: first element
    lisList[0].push_back(arr[0]);

    // Build the LIS lists
    for (int i = 1; i < n; i++)
    {
        int lis = 1;

        for (int prev = 0; prev < i; prev++)
        {

            if (arr[i] > arr[prev] && lis < (int)lisList[prev].size() + 1)
            {
                lisList[i] = lisList[prev];
                lis = lisList[prev].size() + 1;
            }
        }

        // add arr[i] to the longest increasing
        // subsequence ending at index i
        lisList[i].push_back(arr[i]);
    }

    vector<int> res = lisList[0];

    // choose the increasing subsequence of max length
    for (auto &incSubs : lisList)
    {
        if (incSubs.size() > res.size())
        {
            res = incSubs;
        }
    }

    return res;
}

int main()
{
    vector<int> arr = {10, 20, 3, 40};
    vector<int> maxLis = getLIS(arr);

    for (int x : maxLis)
    {
        cout << x << " ";
    }

    return 0;
}
Java
import java.util.ArrayList;
import java.util.List;

class GFG {
    static ArrayList<Integer> getLIS(int arr[])
    {
        int n = arr.length;

        // lisList[i] - The longest increasing
        // subsequence ending with arr[i]
        List<List<Integer> > lisList = new ArrayList<>();

        // Initialize L[i] as empty lists
        for (int i = 0; i < n; i++) {
            lisList.add(new ArrayList<>());
        }

        // Base case: first element
        lisList.get(0).add(arr[0]);

        // Build the LIS lists
        for (int i = 1; i < n; i++) {
            int lis = 1;

            for (int prev = 0; prev < i; prev++) {
                if (arr[i] > arr[prev]
                    && lis < lisList.get(prev).size() + 1) {
                    lisList.set(i, new ArrayList<>(
                                       lisList.get(prev)));
                    lis = lisList.get(prev).size() + 1;
                }
            }

            // add arr[i] to the longest increasing
            // subsequence ending at index i
            lisList.get(i).add(arr[i]);
        }

        ArrayList<Integer> res
            = new ArrayList<>(lisList.get(0));

        // choose the increasing subsequence of max length
        for (List<Integer> incSubs : lisList) {
            if (incSubs.size() > res.size()) {
                res = new ArrayList<>(incSubs);
            }
        }

        return res;
    }
    public static void main(String[] args)
    {
        int[] arr = { 10, 20, 3, 40 };
        
        ArrayList<Integer> maxLis = getLIS(arr);
        
        for (int i = 0; i < maxLis.size(); i++) {
            System.out.print(maxLis.get(i) + " ");
        }
    }
}
Python
def getLIS(arr):
    n = len(arr)

    # lisList[i] - The longest increasing
    # subsequence ending with arr[i]
    lisList = []

    # Initialize L[i] as empty lists
    for i in range(n):
        lisList.append([])

    # Base case: first element
    lisList[0].append(arr[0])

    # Build the LIS lists
    for i in range(1, n):
        lis = 1

        for prev in range(i):
            if arr[i] > arr[prev] and lis < len(lisList[prev]) + 1:
                lisList[i] = lisList[prev].copy()
                lis = len(lisList[prev]) + 1

        # add arr[i] to the longest increasing
        # subsequence ending at index i
        lisList[i].append(arr[i])

    res = lisList[0].copy()

    # choose the increasing subsequence of max length
    for incSubs in lisList:
        if len(incSubs) > len(res):
            res = incSubs.copy()

    return res


# Driver Code
if __name__ == "__main__":
    arr = [10, 20, 3, 40]
    ans = getLIS(arr)
    print(*ans)
C#
using System;
using System.Collections.Generic;

class GFG {
    static List<int> getLIS(int[] arr)
    {
        int n = arr.Length;

        // lisList[i] - The longest increasing
        // subsequence ending with arr[i]
        List<List<int> > lisList = new List<List<int> >();

        // Initialize L[i] as empty lists
        for (int i = 0; i < n; i++) {
            lisList.Add(new List<int>());
        }

        // Base case: first element
        lisList[0].Add(arr[0]);

        // Build the LIS lists
        for (int i = 1; i < n; i++) {
            int lis = 1;

            for (int prev = 0; prev < i; prev++) {

                if (arr[i] > arr[prev]
                    && lis < lisList[prev].Count + 1) {
                    lisList[i]
                        = new List<int>(lisList[prev]);
                    lis = lisList[prev].Count + 1;
                }
            }

            // add arr[i] to the longest increasing
            // subsequence ending at index i
            lisList[i].Add(arr[i]);
        }

        List<int> res = new List<int>(lisList[0]);

        // choose the increasing subsequence of max length
        foreach(var incSubs in lisList)
        {
            if (incSubs.Count > res.Count) {
                res = new List<int>(incSubs);
            }
        }

        return res;
    }

    static void Main()
    {
        int[] arr = { 10, 20, 3, 40 };
        List<int> maxLis = getLIS(arr);

        foreach(int x in maxLis) { Console.Write(x + " "); }
    }
}
JavaScript
function getLIS(arr)
{
    const n = arr.length;

    // lisList[i] - The longest increasing
    // subsequence ending with arr[i]
    let lisList = [];

    // Initialize L[i] as empty lists
    for (let i = 0; i < n; i++) {
        lisList.push([]);
    }

    // Base case: first element
    lisList[0].push(arr[0]);

    // Build the LIS lists
    for (let i = 1; i < n; i++) {
        let lis = 1;

        for (let prev = 0; prev < i; prev++) {

            if (arr[i] > arr[prev]
                && lis < lisList[prev].length + 1) {
                lisList[i] = [...lisList[prev] ];
                lis = lisList[prev].length + 1;
            }
        }

        // add arr[i] to the longest increasing
        // subsequence ending at index i
        lisList[i].push(arr[i]);
    }

    let res = [...lisList[0] ];

    // choose the increasing subsequence of max length
    for (let incSubs of lisList) {
        if (incSubs.length > res.length) {
            res = [...incSubs ];
        }
    }

    return res;
}

// Driver code
const arr = [ 10, 20, 3, 40 ];
let ans = getLIS(arr);
console.log(...ans);

Output
10 20 40 

[Better Approach] DP with Single 1D Extra Array - O(n^2) Time and O(n) Space

In the previous solution, we use an array of arrays to store all subsequences ending with every index. The idea here is to store only indexes of only the previous item in the answer LIS.

  • We use an array seq[] to store indexes of previous items in LIS and used to construct the resultant sequence. Along with constructing dp[] array, we fill indexes in seq[].
  • After filling seq[] and dp[], we find the index of the largest element in dp.
  • Now using the index found in step 2 and seq[], we construct the result sequence.
  • Initialize dp[i] as 1, representing the LIS length ending at index i, and initialize seq[i] with i.
  • For every index i, check all previous indices prev < i.
  • If arr[prev] < arr[i] and extending the LIS ending at prev gives a longer subsequence,
    update dp[i] and set seq[i] = prev.
  • Find the index ansInd having the maximum value in dp[].
  • Starting from ansInd, use seq[] to repeatedly move to the previous index and store the corresponding elements in the result.
  • Reverse the result to obtain the LIS in the correct order.
C++
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

vector<int> getLIS(vector<int> &arr)
{
    int n = arr.size();

    // Initialize dp array with 1.
    vector<int> dp(n, 1);

    // Initialize hash array with index values.
    // We store previous indexs in LIS here
    vector<int> seq(n);

    for (int i = 0; i < n; i++)
    {
        seq[i] = i;

        for (int prev = 0; prev < i; prev++)
        {
            // Update dp and previous values if
            // condition satisfies.
            if (arr[prev] < arr[i] && 1 + dp[prev] > dp[i])
            {
                dp[i] = 1 + dp[prev];
                seq[i] = prev;
            }
        }
    }

    // Now we find the last element
    // in LIS using dp[]
    int ans = -1;
    int ansInd = -1;
    for (int i = 0; i < n; i++)
    {
        if (dp[i] > ans)
        {
            ans = dp[i];
            ansInd = i;
        }
    }

    // Construct the result sequence using seq array
    vector<int> res;
    res.push_back(arr[ansInd]);

    while (seq[ansInd] != ansInd)
    {
        ansInd = seq[ansInd];
        res.push_back(arr[ansInd]);
    }

    // Reverse the result to get the correct order
    reverse(res.begin(), res.end());
    return res;
}

int main()
{
    vector<int> arr = {10, 20, 3, 40};
    vector<int> LIS = getLIS(arr);

    for (int num : LIS)
    {
        cout << num << " ";
    }
    cout << endl;

    return 0;
}
Java
import java.util.ArrayList;
import java.util.Collections;

class GFG {
    static ArrayList<Integer> getLIS(int[] arr)
    {
        int n = arr.length;

        // Initialize dp array with 1
        int[] dp = new int[n];
        for (int i = 0; i < n; i++) {
            dp[i] = 1;
        }

        // Initialize seq array with index values
        // (to store previous indices in LIS)
        int[] seq = new int[n];
        for (int i = 0; i < n; i++) {
            seq[i] = i;
        }

        // Compute dp and seq arrays
        for (int i = 0; i < n; i++) {
            for (int prev = 0; prev < i; prev++) {
                if (arr[prev] < arr[i]
                    && 1 + dp[prev] > dp[i]) {
                    dp[i] = 1 + dp[prev];
                    seq[i] = prev;
                }
            }
        }

        // Find the index of the last element in the LIS
        int ans = -1;
        int ansInd = -1;
        for (int i = 0; i < n; i++) {
            if (dp[i] > ans) {
                ans = dp[i];
                ansInd = i;
            }
        }

        // Construct the result sequence using seq array
        ArrayList<Integer> res = new ArrayList<>();
        res.add(arr[ansInd]);

        while (seq[ansInd] != ansInd) {
            ansInd = seq[ansInd];
            res.add(arr[ansInd]);
        }

        // Reverse the result to get the correct order
        Collections.reverse(res);
        return res;
    }

    public static void main(String[] args)
    {
        int[] arr = { 10, 20, 3, 40 };
        ArrayList<Integer> LIS = getLIS(arr);

        for (int i = 0; i < LIS.size(); i++) {
            System.out.print(LIS.get(i) + " ");
        }
    }
}
Python
def getLIS(arr):
    n = len(arr)

    # Initialize dp array with 1
    dp = [1] * n

    # Initialize seq array with index values (to store previous indices in LIS)
    seq = list(range(n))

    # Compute dp and seq arrays
    for i in range(n):
        for prev in range(i):
            if arr[prev] < arr[i] and 1 + dp[prev] > dp[i]:
                dp[i] = 1 + dp[prev]
                seq[i] = prev

    # Find the index of the last element in the LIS
    ans = -1
    ans_ind = -1
    for i in range(n):
        if dp[i] > ans:
            ans = dp[i]
            ans_ind = i

    # Construct the result sequence using seq array
    res = []
    res.append(arr[ans_ind])
    while seq[ans_ind] != ans_ind:
        ans_ind = seq[ans_ind]
        res.append(arr[ans_ind])

    # Reverse the result to get the correct order
    res.reverse()
    return res


# Driver Code
if __name__ == "__main__":
    arr = [10, 20, 3, 40]

    LIS = getLIS(arr)
    print(" ".join(map(str, LIS)))
C#
using System;
using System.Collections.Generic;

class GFG {
    static List<int> getLIS(int[] arr)
    {
        int n = arr.Length;

        // Initialize dp array with 1
        int[] dp = new int[n];
        for (int i = 0; i < n; i++) {
            dp[i] = 1;
        }

        // Initialize seq array with index values (to store
        // previous indices in LIS)
        int[] seq = new int[n];
        for (int i = 0; i < n; i++) {
            seq[i] = i;
        }

        // Compute dp and seq arrays
        for (int i = 0; i < n; i++) {
            for (int prev = 0; prev < i; prev++) {
                if (arr[prev] < arr[i]
                    && 1 + dp[prev] > dp[i]) {
                    dp[i] = 1 + dp[prev];
                    seq[i] = prev;
                }
            }
        }

        // Find the index of the last element in the LIS
        int ans = -1;
        int ansInd = -1;
        for (int i = 0; i < n; i++) {
            if (dp[i] > ans) {
                ans = dp[i];
                ansInd = i;
            }
        }

        // Construct the result sequence using seq array
        List<int> res = new List<int>();
        res.Add(arr[ansInd]);
        while (seq[ansInd] != ansInd) {
            ansInd = seq[ansInd];
            res.Add(arr[ansInd]);
        }

        // Reverse the result to get the correct order
        res.Reverse();
        return res;
    }

    static void Main(string[] args)
    {
        int[] arr = { 10, 20, 3, 40 };
        List<int> LIS = getLIS(arr);

        Console.WriteLine(string.Join(" ", LIS));
    }
}
JavaScript
function getLIS(arr)
{
    const n = arr.length;

    // Initialize dp array with 1
    const dp = new Array(n).fill(1);

    // Initialize seq array with index values (to store
    // previous indices in LIS)
    const seq = [...Array(n).keys() ];

    // Compute dp and seq arrays
    for (let i = 0; i < n; i++) {
        for (let prev = 0; prev < i; prev++) {
            if (arr[prev] < arr[i]
                && 1 + dp[prev] > dp[i]) {
                dp[i] = 1 + dp[prev];
                seq[i] = prev;
            }
        }
    }

    // Find the index of the last element in the LIS
    let ans = -1;
    let ansInd = -1;
    for (let i = 0; i < n; i++) {
        if (dp[i] > ans) {
            ans = dp[i];
            ansInd = i;
        }
    }

    // Construct the result sequence using seq array
    const res = [];
    res.push(arr[ansInd]);
    while (seq[ansInd] !== ansInd) {
        ansInd = seq[ansInd];
        res.push(arr[ansInd]);
    }

    // Reverse the result to get the correct order
    res.reverse();
    return res;
}

// Driver code
const arr = [ 10, 20, 3, 40 ];

const LIS = getLIS(arr);
console.log(LIS.join(" "));

Output
10 20 40 

[Expected Approach] Using DP with Binary Search - O(n * log(n)) Time and O(n) Space

The idea is based on the LIS using binary search approach to improve the time complexity.

We process the array from right to left and use negative values to convert the increasing subsequence problem into a decreasing subsequence problem (because we are processing from right)

We maintain a dp list and use binary search to efficiently find the correct position for each element. We also store the required indices to reconstruct the LIS with the lexicographically earliest sequence of indices.

  • Initialize dp to store value-index pairs and prv to store the previous index for each element.
  • Process the array from right to left and negate each value to convert the problem into a decreasing subsequence problem.
  • Use binary search to find the correct position of the current value in dp.
  • Update dp and store the previous index in prv for reconstruction.
  • Start from the last index stored in dp and follow prv to reconstruct the LIS.
  • Return the reconstructed elements in the required order.
C++
#include <algorithm>
#include <iostream>
#include <unordered_map>
#include <vector>

using namespace std;

vector<int> getLIS(vector<int> &arr)
{
    int N = arr.size();

    vector<pair<int, int>> dp;
    unordered_map<int, int> prv;

    // Process array in reverse order
    for (int ix = N - 1; ix >= 0; --ix)
    {
        int ve = -arr[ix];

        // Binary search to find insertion point
        auto it =
            lower_bound(dp.begin(), dp.end(), make_pair(ve, 0),
                        [](const pair<int, int> &a, const pair<int, int> &b) { return a.first < b.first; });

        int tmp = -1; // Default previous index
        int i = distance(dp.begin(), it);

        if (i == dp.size())
        {
            if (!dp.empty())
            {
                tmp = dp.back().second;
            }

            dp.emplace_back(ve, ix);
        }
        else
        {
            if (i > 0)
            {
                tmp = dp[i - 1].second;
            }

            dp[i] = {ve, ix};
        }

        prv[ix] = tmp;
    }

    // Reconstruct the LIS
    vector<int> ret;
    int cur = dp.back().second;

    while (cur >= 0)
    {
        ret.push_back(arr[cur]);
        cur = prv[cur];
    }

    return ret;
}

int main()
{
    vector<int> arr = {10, 20, 3, 40};
    vector<int> lis = getLIS(arr);

    for (int num : lis)
    {
        cout << num << " ";
    }

    return 0;
}
Java
import java.util.*;

class GFG {
    static ArrayList<Integer> getLIS(int[] arr)
    {
        int N = arr.length;

        ArrayList<int[]> dp = new ArrayList<>();
        HashMap<Integer, Integer> prv = new HashMap<>();

        // Process array in reverse order
        for (int ix = N - 1; ix >= 0; --ix) {
            int ve = -arr[ix];

            // Binary search to find insertion point
            int low = 0, high = dp.size();

            while (low < high) {
                int mid = low + (high - low) / 2;

                if (dp.get(mid)[0] < ve)
                    low = mid + 1;
                else
                    high = mid;
            }

            int i = low;

            int tmp = -1; // Default previous index

            if (i == dp.size()) {
                if (!dp.isEmpty()) {
                    tmp = dp.get(dp.size() - 1)[1];
                }

                dp.add(new int[] { ve, ix });
            }
            else {
                if (i > 0) {
                    tmp = dp.get(i - 1)[1];
                }

                dp.set(i, new int[] { ve, ix });
            }

            prv.put(ix, tmp);
        }

        // Reconstruct the LIS
        ArrayList<Integer> ret = new ArrayList<>();
        int cur = dp.get(dp.size() - 1)[1];

        while (cur >= 0) {
            ret.add(arr[cur]);
            cur = prv.get(cur);
        }

        return ret;
    }

    public static void main(String[] args)
    {
        int[] arr = { 10, 20, 3, 40 };
        ArrayList<Integer> lis = getLIS(arr);

        for (int num : lis) {
            System.out.print(num + " ");
        }
    }
}
Python
from bisect import bisect_left

def getLIS(arr):
    N = len(arr)

    dp = []
    prv = {}

    # Process array in reverse order
    for ix in range(N - 1, -1, -1):
        ve = -arr[ix]

        # Binary search to find insertion point
        low = 0
        high = len(dp)

        while low < high:
            mid = low + (high - low) // 2

            if dp[mid][0] < ve:
                low = mid + 1
            else:
                high = mid

        i = low

        tmp = -1  # Default previous index

        if i == len(dp):
            if dp:
                tmp = dp[-1][1]

            dp.append((ve, ix))
        else:
            if i > 0:
                tmp = dp[i - 1][1]

            dp[i] = (ve, ix)

        prv[ix] = tmp

    # Reconstruct the LIS
    ret = []
    cur = dp[-1][1]

    while cur >= 0:
        ret.append(arr[cur])
        cur = prv[cur]

    return ret


# Driver Code
if __name__ == "__main__":
    arr = [10, 20, 3, 40]
    lis = getLIS(arr)

    for num in lis:
        print(num, end=" ")
C#
using System;
using System.Collections.Generic;

class GFG {
    static List<int> getLIS(int[] arr)
    {
        int N = arr.Length;

        List<(int value, int index)> dp
            = new List<(int, int)>();
        Dictionary<int, int> prv
            = new Dictionary<int, int>();

        // Process array in reverse order
        for (int ix = N - 1; ix >= 0; --ix) {
            int ve = -arr[ix];

            // Binary search to find insertion point
            int low = 0, high = dp.Count;

            while (low < high) {
                int mid = low + (high - low) / 2;

                if (dp[mid].value < ve)
                    low = mid + 1;
                else
                    high = mid;
            }

            int i = low;

            int tmp = -1; // Default previous index

            if (i == dp.Count) {
                if (dp.Count > 0) {
                    tmp = dp[dp.Count - 1].index;
                }

                dp.Add((ve, ix));
            }
            else {
                if (i > 0) {
                    tmp = dp[i - 1].index;
                }

                dp[i] = (ve, ix);
            }

            prv[ix] = tmp;
        }

        // Reconstruct the LIS
        List<int> ret = new List<int>();
        int cur = dp[dp.Count - 1].index;

        while (cur >= 0) {
            ret.Add(arr[cur]);
            cur = prv[cur];
        }

        return ret;
    }

    static void Main()
    {
        int[] arr = { 10, 20, 3, 40 };
        List<int> lis = getLIS(arr);

        foreach(int num in lis)
        {
            Console.Write(num + " ");
        }
    }
}
JavaScript
function getLIS(arr)
{
    let N = arr.length;

    let dp = [];
    let prv = new Map();

    // Process array in reverse order
    for (let ix = N - 1; ix >= 0; --ix) {
        let ve = -arr[ix];

        // Binary search to find insertion point
        let low = 0;
        let high = dp.length;

        while (low < high) {
            let mid = low + Math.floor((high - low) / 2);

            if (dp[mid][0] < ve)
                low = mid + 1;
            else
                high = mid;
        }

        let i = low;

        let tmp = -1; // Default previous index

        if (i === dp.length) {
            if (dp.length > 0) {
                tmp = dp[dp.length - 1][1];
            }

            dp.push([ ve, ix ]);
        }
        else {
            if (i > 0) {
                tmp = dp[i - 1][1];
            }

            dp[i] = [ ve, ix ];
        }

        prv.set(ix, tmp);
    }

    // Reconstruct the LIS
    let ret = [];
    let cur = dp[dp.length - 1][1];

    while (cur >= 0) {
        ret.push(arr[cur]);
        cur = prv.get(cur);
    }

    return ret;
}

// Driver Code
let arr = [ 10, 20, 3, 40 ];
let lis = getLIS(arr);

let ans = "";
for (let num of lis) {
    ans += num + " ";
}

console.log(ans.trim());

Output
10 20 40 
Comment