Printing Longest Increasing Subsequence (LIS) (original) (raw)

Last Updated : 23 Jul, 2025

Try it on GfG Practice redirect icon

Given a sequence of numbers, the task is to find and print the **Longest Increasing Subsequence (LIS), the longest subsequence where each element is strictly greater than the previous one. If multiple LIS of the same maximum length exist, we must select the one that appears first based on the lexicographical order of their indices (i.e., the earliest combination of positions from the original sequence). The goal is to ensure both maximum length and the earliest possible subsequence.

**Examples:

**Input: [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: [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, examples [10, 22, 33, 50, 60, 80] and [10 22 33 41 60 80]. The first one has lexicographic smallest order.

Table of Content

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

We extend the of bottom-up DP solution of LIS problem. Please remember, for every element arr[i], it iterates over all previous elements (arr[prev] where prev < i) to find the longest subsequence that can be extended by arr[i]. To find elements of LIS, we build an array of arrays, L such that L[i] stores LIS of arr that ends with arr[i]. For example, for array [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]

The final result is the longest subsequence among all L[i].

**Step by step Approach

#include #include using namespace std;

// Function to construct and print Longest // Increasing Subsequence vector getLIS(vector& arr){

// L[i] - The longest increasing 
// sub-sequence ends with arr[i]
int n = arr.size();
vector<vector<int>> L(n);

// L[0] is equal to arr[0]
L[0].push_back(arr[0]);

// Start from index 1
for (int i = 1; i < n; i++){
    
    int lis = 1;

    // Do for every prev less than i
    for (int prev = 0; prev < i; prev++){
        
        /* L[i] = {Max(L[prev])} + arr[i]
           where prev < i and arr[prev] < arr[i] */
        if ((arr[i] > arr[prev]) && 
            (lis < L[prev].size() + 1)) {
          
            // Copy the vector of prev and update lis of i
            L[i] = L[prev];
            lis = L[prev].size() + 1;
        }
    }

    // L[i] ends with arr[i]
    L[i].push_back(arr[i]);
}

// L[i] now stores increasing sub-sequence 
// of arr[0..i] that ends with arr[i]
vector<int> res = L[0];

// LIS will be max of all increasing 
// sub-sequences of arr
for (vector<int> x : L)
    if (x.size() > res.size())
        res = x;
return res;

}

// Driver function int main(){

vector<int> arr = {10, 20, 3, 40};

vector<int> max_lis = getLIS(arr);
for (int x : max_lis)
    cout << x << " ";
return 0;

}

Java

import java.util.ArrayList; import java.util.List;

class GfG {

// Function to construct and print Longest
// Increasing Subsequence
static ArrayList<Integer> getLIS(int arr[]) {

int n = arr.length;

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

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

// Base case: first element
L.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 < L.get(prev).size() + 1) {
        L.set(i, new ArrayList<>(L.get(prev)));
        lis = L.get(prev).size() + 1;
    }
}

L.get(i).add(arr[i]);

}

// Find the list with the maximum length
ArrayList<Integer> res = new ArrayList<>(L.get(0));

for (List<Integer> x : L) {
    if (x.size() > res.size()) {
        res = new ArrayList<>(x);
    }
}

return res;
    
}

// Driver function
public static void main(String[] args) {
    int[] arr = {10, 20, 3, 40};
    ArrayList<Integer> max_lis = getLIS(arr);
    for (int i = 0; i < max_lis.size(); i++) {
        System.out.print(max_lis.get(i) + " ");
    }

}

}

Python

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

# L[i] - The longest increasing sub-sequence ends with arr[i]
L = [[] for _ in range(n)]

# L[0] is equal to arr[0]
L[0].append(arr[0])

# Start from index 1
for i in range(1, n):
    lis = 1
    
    # Do for every prev less than i
    for prev in range(i):
        if arr[i] > arr[prev] and lis < len(L[prev]) + 1:
          
            # Copy the list of prev and update lis of i
            L[i] = L[prev][:]
            lis = len(L[prev]) + 1

    # L[i] ends with arr[i]
    L[i].append(arr[i])

# L[i] now stores increasing sub-sequence
# of arr[0..i] that ends with arr[i]
res = L[0]

# LIS will be max of all increasing 
# sub-sequences of arr
for x in L:
    if len(x) > len(res):
        res = x

# return the longest increasing subsequence
return res

Driver function

if name == "main": arr = [10, 20, 3, 40]

# Construct and print LIS of arr
max_lis = getLIS(arr)
print(" ".join(map(str, max_lis)))

C#

using System; using System.Collections.Generic;

class GfG { // Function to construct and return the Longest Increasing Subsequence static List getLIS(int[] arr) {
int n = arr.Length; // L[i] - The longest increasing subsequence ending with arr[i] List<List> L = new List<List>(n);

    // Initialize the lists
    for (int i = 0; i < n; i++)
    {
        L.Add(new List<int>());
    }

    // Base case: first element
    L[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 < L[prev].Count + 1)
            {
                // Copy the best previous subsequence
                L[i] = new List<int>(L[prev]);
                lis = L[prev].Count + 1;
            }
        }

        // Append current element
        L[i].Add(arr[i]);
    }

    // Find the list with the maximum length
    List<int> res = new List<int>(L[0]);
    foreach (List<int> x in L)
    {
        if (x.Count > res.Count)
        {
            res = x;
        }
    }

    return res;
}

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

    foreach (int x in max_lis){
        
        Console.Write(x + " ");
    }
    Console.WriteLine();
}

}

JavaScript

// Function to construct and print Longest // Increasing Subsequence function getLIS(arr) { const n = arr.length;

// L[i] - The longest increasing sub-sequence 
// ends with arr[i]
const L = Array.from({ length: n }, () => []);

// L[0] is equal to arr[0]
L[0].push(arr[0]);

// Start from index 1
for (let i = 1; i < n; i++) {
    let lis = 1;

    // Do for every prev less than i
    for (let prev = 0; prev < i; prev++) {
    
        if (arr[i] > arr[prev] && lis < L[prev].length + 1) {
            // Copy the list of prev and update lis of i
            L[i] = [...L[prev]];
            lis = L[prev].length + 1;
        }
    }

    // L[i] ends with arr[i]
    L[i].push(arr[i]);
}

// L[i] now stores increasing sub-sequence
// of arr[0..i] that ends with arr[i]
let res = L[0];

// LIS will be max of all increasing 
// sub-sequences of arr
for (const x of L) {
    if (x.length > res.length) {
        res = x;
    }
}

// Return the longest increasing subsequence
return res;

}

// Driver function const arr = [10, 20, 3, 40]; let max_lis = getLIS(arr); console.log(max_lis.join(' '));

`

**Time Complexity: O(n3), n2 for two nested loops and n for copying another vector in a vector (e.g. : L[i] = L[prev]) contributes O(n) also.
**Space Complexity: O(n2), 2d vector to store our LIS

[Better Approach]: Dynamic programming 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.

  1. 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[].
  2. After filling seq[] and dp[], we find the index of the largest element in dp.
  3. Now using the index found in step 2 and seq[], we construct the result sequence.

C++ `

#include #include #include using namespace std;

// Function to find the longest increasing // subsequence. vector getLIS(vector& 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; // Mark element itself as prev
  
    for (int prev = 0; prev < i; prev++) {
      
        // Update dp and hash 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;
    }
}

// Now construct result sequence using seq[]
// and ans_ind
vector<int> res;
res.push_back(arr[ansInd]);
while (seq[ansInd] != ansInd) {
    ansInd = seq[ansInd];
    res.push_back(arr[ansInd]);
}
reverse(res.begin(), res.end());
return res;

}

int main() { vector arr = {10, 20, 3, 40};

vector<int> max_lis = getLIS(arr);

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

return 0;

}

Java

import java.util.ArrayList; import java.util.Collections;

public class Main {

// Function to find the longest increasing subsequence
public 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> max_lis = getLIS(arr);

    for (int i = 0; i < max_lis.size(); i++) {
        System.out.print(max_lis.get(i) + " ");
    }

}

}

Python

Function to find the longest increasing subsequence

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

if name == "main": arr = [10, 20, 3, 40]

max_lis = getLIS(arr)

print(" ".join(map(str, max_lis)))

C#

using System; using System.Collections.Generic;

class GfG{ // Function to find the longest increasing subsequence static List 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> max_lis = getLIS(arr);

    Console.WriteLine(string.Join(" ", max_lis));
}

}

JavaScript

// Function to find the longest increasing subsequence 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;

}

const arr = [10, 20, 3, 40];

const max_lis = getLIS(arr);

console.log(max_lis.join(" "));

`

**Time Complexity: O(n2), We have two nested loops: one to fill the dp[] array and another to backtrack using the **seq[]**array.
**Space Complexity: O(n), We use two arrays, **dp[]**and seq[], each of size n.

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

The idea is based on LIS using binary search to improve efficiency. We maintain a list (dp) representing the minimum possible last value for increasing subsequences of different lengths. We process elements **backward and use **negative values to flip the problem into a decreasing order, making it easier to find the correct position using binary search function like lower_bound() in C++. Tracking previous indices allows us to reconstruct the actual sequence after processing. This method ensures both **maximum length and **earliest lexicographical order efficiently.

**Step by Step Approach

#include #include #include #include

using namespace std;

vector getLIS(int N, vector& arr) { 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 arr = {10, 20, 3, 40}; vector lis = getLIS(arr.size(), arr);

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

}

Java

import java.util.*;

public class Solution { // Function to find Longest Increasing Subsequence static ArrayList getLIS(int arr[]) { ArrayList ret = new ArrayList<>(); ArrayList<int[]> dp = new ArrayList<>();
HashMap<Integer, Integer> prv = new HashMap<>();
int N = arr.length;

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

        // Binary search to find insertion point
        int l = 0, r = dp.size();
        while (l < r) {
            int m = l + (r - l) / 2;
            if (dp.get(m)[0] < ve) {
                l = m + 1;
            } else {
                r = m;
            }
        }
        int i = l;
        
        int tmp = -1;  
        
        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
    int cur = dp.get(dp.size() - 1)[1];
    while (cur >= 0) {
        ret.add(arr[cur]);
        cur = prv.getOrDefault(cur, -1);
    }
    return ret;
}

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

}

Python

def getLIS(arr): ret = []
dp = []
prv = {} N = len(arr)

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

    # Binary search to find insertion point
    l, r = 0, len(dp)
    while l < r:
        m = l + (r - l) // 2
        if dp[m][0] < ve:
            l = m + 1
        else:
            r = m
    i = l
    
    # Default previous index
    tmp = -1  

    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
cur = dp[-1][1]
while cur >= 0:
    ret.append(arr[cur])
    cur = prv.get(cur, -1)

return ret

Example usage

arr = [10, 20, 3, 40] print(getLIS(arr))

C#

using System; using System.Collections.Generic;

public class Solution { // Function to find Longest Increasing Subsequence public List getLIS(List arr) { List ret = new List(); List<(int, int)> dp = new List<(int, int)>(); // Stores (-value, index) pairs Dictionary<int, int> prv = new Dictionary<int, int>(); // Stores previous index for each element int N = arr.Count;

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

        // Binary search to find insertion point
        int l = 0, r = dp.Count;
        while (l < r) {
            int m = l + (r - l) / 2;
            if (dp[m].Item1 < ve) {
                l = m + 1;
            } else {
                r = m;
            }
        }
        int i = l;
        
        // Default previous index
        int tmp = -1;  

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

    // Reconstruct the LIS
    int cur = dp[dp.Count - 1].Item2;
    while (cur >= 0) {
        ret.Add(arr[cur]);
        cur = prv.ContainsKey(cur) ? prv[cur] : -1;
    }
    
    return ret;
}

public static void Main(string[] args) {
    List<int> arr = new List<int> {10, 20, 3, 40};
    Solution sol = new Solution();
    List<int> lis = sol.getLIS(arr);
    Console.WriteLine(string.Join(" ", lis));
}

}

JavaScript

function getLIS(arr) { let ret = [];
let dp = [];
let prv = {}; let N = arr.length;

// Process array in reverse order
for (let ix = N - 1; ix >= 0; --ix) {
    let ve = -arr[ix];  
    
    // Binary search to find insertion point
    let l = 0, r = dp.length;
    while (l < r) {
        let m = l + Math.floor((r - l) / 2);
        if (dp[m][0] < ve) {
            l = m + 1;
        } else {
            r = m;
        }
    }
    let i = l;
    
    // Default previous index
    let tmp = -1;  

    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[ix] = tmp;
}

// Reconstruct the LIS
let cur = dp[dp.length - 1][1];
while (cur >= 0) {
    ret.push(arr[cur]);
    cur = prv[cur] !== undefined ? prv[cur] : -1;
}

return ret;

}

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

`

**Time Complexity: O(N log N) ,for each of the N elements we perform a binary search (using lower_bound) in the dp array, which takes O(log N) time. Thus, processing all elements gives an overall complexity of O(N log N).
**Auxiliary space: O(N), we use extra space for the dp list and the prv map, both of which at most store one entry per element in the input array.