Printing Longest Increasing Subsequence (LIS) (original) (raw)
Last Updated : 23 Jul, 2025
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
- [Better Approach]: Dynamic programming with Single 1D Extra Array - O(n^2) Time and O(n) Space
- [Expected Approach] Using DP with Binary Search - O(n*log(n)) Time and O(n) Space
[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]whereprev < i) to find the longest subsequence that can be extended byarr[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
- Create an array of arrays
LwhereL[i]stores the **longest increasing subsequence (LIS) ending at indexi. - Initialize
L[0]with the first element (arr[0]), as it’s the only subsequence possible at this point. - For each element
arr[i](starting fromi=1ton-1):- Check all previous indices
prev(from0toi-1). - If
arr[i] > arr[prev](valid to extend the subsequence) **and the subsequenceL[prev]is longer than the current best forL[i]: **UpdateL[i]by copyingL[prev](this extends the subsequence ending atprev). - **Append
arr[i]toL[i](sincearr[i]is the endpoint of this subsequence).
- Check all previous indices
- After processing all elements, iterate through
L[0]toL[n-1]to find the **longest subsequence inL, which is the LIS. C++ `
#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 resDriver 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.
- 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.
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 resif 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 likelower_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
- Create an empty list
dpto store pairs(-value, index). - Create a map
prvto remember the previous index for each element. - Iterate from the last element to the first.
- For each element, take its negative (
ve = -arr[ix]) to handle increasing order via decreasing values. - Use **binary search (
lower_bound) to find the right position indp. - If the position is at the end of
dp, append the element and set its previous index. - Else, update
dp[i]with the current value and set its previous link. - Start from the last element's index (from
dp.back().second). - Follow previous pointers (
prv[cur]) to collect elements into the result list. - Since we built the sequence backward, finally reverse the
retarray to get the LIS in correct order. C++ `
#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 retExample 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.