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.
Examples:
Input: arr[] = [3, 10, 2, 1, 20] Output: 3 Explanation: The longest increasing subsequence is 3, 10, 20
Input: arr[] = [30, 20, 10] Output:1 Explanation: The longest increasing subsequences are [30], [20] and [10]
Input: arr[] = [2, 2, 2] Output: 1 Explanation: We consider only strictly increasing.
Input: arr[] = [10, 20, 35, 80] Output: 4 Explanation: The whole array is sorted
[Naive Approach] Using Recursion - Exponential Time and Linear Space
The idea to do traverse the input array from left to right and find lengthof the Longest Increasing Subsequence (LIS) ending with every element arr[i]. Let the length found for arr[i] be L[i]. At the end we return maximum of all L[i] values. Now to compute L[i], we use recursion, we consider all smaller elements onleft of arr[i], recursively compute LIS value for all the smaller elements on left, take the maximum of all and add 1 to it. If there is no smaller element on left of an element, we return 1.
Let L(i) be the length of the LIS ending at index i such that arr[i] is the last element of the LIS. Then, L(i) can be recursively written as:
L(i) = 1 + max(L(prev) ) where 0 < prev < i and arr[prev] < arr[i]; or
L(i) = 1, if no such prev exists.
Formally, the length of LIS ending at index i, is 1 greater than the maximum of lengths of all LIS ending at some index prev such that arr[prev] < arr[i] where prev < i.
After we fill the L array, we find LIS as maximum of all in L[]
Overall LIS = max(L[i]) where 0 <= i < n
We can see that the above recurrence relation follows the optimal substructure property. Follow the below illustration to see overlapping subproblems.
Consider arr[] = [3, 10, 2, 11]
L(i): Denotes LIS of subarray ending at position 'i'
Recursion Tree
C++
// Cpp program to find lis using recursion// in Exponential Time and Linear Space#include<bits/stdc++.h>usingnamespacestd;// Returns LIS of subarray ending with index i.intlisEndingAtIdx(vector<int>&arr,intidx){// Base caseif(idx==0)return1;// Consider all elements on the left of i,// recursively compute LISs ending with // them and consider the largestintmx=1;for(intprev=0;prev<idx;prev++)if(arr[prev]<arr[idx])mx=max(mx,lisEndingAtIdx(arr,prev)+1);returnmx;}intlis(vector<int>&arr){intn=arr.size();intres=1;for(inti=1;i<n;i++)res=max(res,lisEndingAtIdx(arr,i));returnres;}intmain(){vector<int>arr={10,22,9,33,21,50,41,60};cout<<lis(arr);return0;}
Java
// Java program to find lis using recursion// in Exponential Time and Linear Spaceimportjava.util.*;classGfG{staticintlisEndingAtIdx(int[]arr,intidx){// Base caseif(idx==0)return1;// Consider all elements on the left of i,// recursively compute LISs ending with // them and consider the largestintmx=1;for(intprev=0;prev<idx;prev++)if(arr[prev]<arr[idx])mx=Math.max(mx,lisEndingAtIdx(arr,prev)+1);returnmx;}staticintlis(int[]arr){intn=arr.length;intres=1;for(intidx=1;idx<n;idx++)res=Math.max(res,lisEndingAtIdx(arr,idx));returnres;}publicstaticvoidmain(String[]args){int[]arr={10,22,9,33,21,50,41,60};System.out.println(lis(arr));}}
Python
# Python program to find lis using recursion# in Exponential Time and Linear SpacedeflisEndingAtIdx(arr,idx):# Base caseifidx==0:return1# Consider all elements on the left of i,# recursively compute LISs ending with # them and consider the largestmx=1forprevinrange(idx):ifarr[prev]<arr[idx]:mx=max(mx,lisEndingAtIdx(arr,prev)+1)returnmxdeflis(arr):n=len(arr)res=1foridxinrange(1,n):res=max(res,lisEndingAtIdx(arr,idx))returnresif__name__=="__main__":arr=[10,22,9,33,21,50,41,60]print(lis(arr))
C#
// C# program to find lis using recursion// in Exponential Time and Linear SpaceusingSystem;usingSystem.Collections.Generic;classGfG{staticintlisEndingAtIdx(List<int>arr,intidx){// Base caseif(idx==0)return1;// Consider all elements on the left of i,// recursively compute LISs ending with // them and consider the largestintmx=1;for(intprev=0;prev<idx;prev++)if(arr[prev]<arr[idx])mx=Math.Max(mx,lisEndingAtIdx(arr,prev)+1);returnmx;}staticintlis(List<int>arr){intn=arr.Count;intres=1;for(intidx=1;idx<n;idx++)res=Math.Max(res,lisEndingAtIdx(arr,idx));returnres;}staticvoidMain(string[]args){List<int>arr=newList<int>{10,22,9,33,21,50,41,60};Console.WriteLine(lis(arr));}}
JavaScript
// JavaScript program to find lis using recursion// in Exponential Time and Linear SpacefunctionlisEndingAtIdx(arr,idx){// Base caseif(idx===0)return1;// Consider all elements on the left of i,// recursively compute LISs ending with// them and consider the largestletmx=1;for(letprev=0;prev<idx;prev++){if(arr[prev]<arr[idx]){mx=Math.max(mx,lisEndingAtIdx(arr,prev)+1);}}returnmx;}functionlis(arr){letn=arr.length;letres=1;for(letidx=1;idx<n;idx++){res=Math.max(res,lisEndingAtIdx(arr,idx));}returnres;}letarr=[10,22,9,33,21,50,41,60];console.log(lis(arr));
Output
5
[Better Approach - 1] Using Memoization - O(n^2) Time and O(n) Space
If notice carefully, we can see that the above recursive function lisEndingAtIdx() also follows the overlapping subproblems property i.e., same substructure solved again and again in different recursion call paths. We can avoid this using the memoization approach. Since there is only one parameter that changes in recursive calls and the range of the parameter goes from 0 to n-1, so we us a 1D array of size n and initialize it as -1 to indicate that the values are not computed yet.
C++
#include<bits/stdc++.h>usingnamespacestd;intlisEndingAtIdx(vector<int>&arr,intidx,vector<int>&memo){// Base caseif(idx==0)return1;// Check if the result is already computedif(memo[idx]!=-1)returnmemo[idx];// Consider all elements on left of i,// recursively compute LISs ending with // them and consider the largestintmx=1;for(intprev=0;prev<idx;prev++)if(arr[prev]<arr[idx])mx=max(mx,lisEndingAtIdx(arr,prev,memo)+1);// Store the result in the memo arraymemo[idx]=mx;returnmemo[idx];}intlis(vector<int>&arr){intn=arr.size();vector<int>memo(n,-1);intres=1;for(inti=1;i<n;i++)res=max(res,lisEndingAtIdx(arr,i,memo));returnres;}intmain(){vector<int>arr={10,22,9,33,21,50,41,60};cout<<lis(arr);return0;}
Java
importjava.util.*;classGfG{staticintlisEndingAtIdx(int[]arr,intidx,int[]memo){// Base caseif(idx==0)return1;// Check if the result is already computedif(memo[idx]!=-1)returnmemo[idx];// Consider all elements on left of i,// recursively compute LISs ending with // them and consider the largestintmx=1;for(intprev=0;prev<idx;prev++)if(arr[prev]<arr[idx])mx=Math.max(mx,lisEndingAtIdx(arr,prev,memo)+1);// Store the result in the memo arraymemo[idx]=mx;returnmemo[idx];}staticintlis(int[]arr){intn=arr.length;int[]memo=newint[n];Arrays.fill(memo,-1);intres=1;for(intidx=1;idx<n;idx++)res=Math.max(res,lisEndingAtIdx(arr,idx,memo));returnres;}publicstaticvoidmain(String[]args){int[]arr={10,22,9,33,21,50,41,60};System.out.println(lis(arr));}}
Python
deflisEndingAtIdx(arr,idx,memo):# Base caseifidx==0:return1# Check if the result is already computedifmemo[idx]!=-1:returnmemo[idx]# Consider all elements on left of i,# recursively compute LISs ending with # them and consider the largestmx=1forprevinrange(idx):ifarr[prev]<arr[idx]:mx=max(mx,lisEndingAtIdx(arr,prev,memo)+1)# Store the result in the memo arraymemo[idx]=mxreturnmemo[idx]deflis(arr):n=len(arr)memo=[-1]*nres=1foridxinrange(1,n):res=max(res,lisEndingAtIdx(arr,idx,memo))returnresif__name__=="__main__":arr=[10,22,9,33,21,50,41,60]print(lis(arr))
C#
usingSystem;usingSystem.Collections.Generic;classGfG{staticintlisEndingAtIdx(List<int>arr,intidx,int[]memo){// Base caseif(idx==0)return1;// Check if the result is already computedif(memo[idx]!=-1)returnmemo[idx];// Consider all elements on left of i,// recursively compute LISs ending with // them and consider the largestintmx=1;for(intprev=0;prev<idx;prev++)if(arr[prev]<arr[idx])mx=Math.Max(mx,lisEndingAtIdx(arr,prev,memo)+1);// Store the result in the memo arraymemo[idx]=mx;returnmemo[idx];}staticintlis(List<int>arr){intn=arr.Count;int[]memo=newint[n];for(inti=0;i<n;i++){memo[i]=-1;}intres=1;for(intidx=1;idx<n;idx++)res=Math.Max(res,lisEndingAtIdx(arr,idx,memo));returnres;}staticvoidMain(string[]args){List<int>arr=newList<int>{10,22,9,33,21,50,41,60};Console.WriteLine(lis(arr));}}
JavaScript
functionlisEndingAtIdx(arr,idx,memo){// Base caseif(idx===0)return1;// Check if the result is already computedif(memo[idx]!==-1)returnmemo[idx];// Consider all elements on left of i,// recursively compute LISs ending with// them and consider the largestletmx=1;for(letprev=0;prev<idx;prev++){if(arr[prev]<arr[idx]){mx=Math.max(mx,lisEndingAtIdx(arr,prev,memo)+1);}}// Store the result in the memo arraymemo[idx]=mx;returnmemo[idx];}functionlis(arr){constn=arr.length;constmemo=Array(n).fill(-1);letres=1;for(letidx=1;idx<n;idx++){res=Math.max(res,lisEndingAtIdx(arr,idx,memo));}returnres;}constarr=[10,22,9,33,21,50,41,60];console.log(lis(arr));
Output
5
[Better Approach - 2] Using DP (Bottom Up Tabulation) - O(n^2) Time and O(n) Space
The tabulation approach for finding the Longest Increasing Subsequence (LIS) solves the problem iteratively in a bottom-up manner. The idea is to maintain a 1D array lis[], where lis[i] stores the length of the longest increasing subsequence that ends at index i. Initially, each element in lis[] is set to 1, as the smallest possible subsequence for any element is the element itself.
The algorithm then iterates over each element of the array. For each element arr[i], it checks all previous elements arr[0] to arr[i-1]. If arr[i] is greater than arr[prev] (ensuring the subsequence is increasing), it updates lis[i] to the maximum of its current value or lis[prev] + 1, indicating that we can extend the subsequence ending at arr[prev] by including arr[i].
Finally, the length of the longest increasing subsequence is the maximum value in the lis[] array.
C++
#include<bits/stdc++.h>usingnamespacestd;// lis() returns the length of the longest// increasing subsequence in arr of size nintlis(vector<int>&arr){intn=arr.size();vector<int>lis(n,1);// Compute optimized LIS values in// bottom-up mannerfor(inti=1;i<n;i++){for(intprev=0;prev<i;prev++){if(arr[i]>arr[prev]&&lis[i]<lis[prev]+1){lis[i]=lis[prev]+1;}}}// Return maximum value in lisreturn*max_element(lis.begin(),lis.end());}intmain(){vector<int>arr={10,22,9,33,21,50,41,60};cout<<lis(arr)<<endl;return0;}
Java
importjava.lang.*;classGfG{// lis() returns the length of the longest// increasing subsequence in arr[] of size nstaticintlis(intarr[]){intn=arr.length;intlis[]=newint[n];// Initialize LIS values for all indexesfor(inti=0;i<n;i++)lis[i]=1;// Compute optimized LIS values in// bottom up mannerfor(inti=1;i<n;i++)for(intprev=0;prev<i;prev++)if(arr[i]>arr[prev]&&lis[i]<lis[prev]+1)lis[i]=lis[prev]+1;// Pick maximum of all LIS valuesintmax=1;for(inti=0;i<n;i++)max=Math.max(max,lis[i]);returnmax;}publicstaticvoidmain(Stringargs[]){intarr[]={10,22,9,33,21,50,41,60};System.out.println(lis(arr));}}
Python
# lis returns length of the longest# increasing subsequence in arr of size ndeflis(arr):n=len(arr)# Declare the list (array) for LIS and# initialize LIS values for all indexeslis=[1]*n# Compute optimized LIS values in bottom# -up mannerforiinrange(1,n):forprevinrange(0,i):ifarr[i]>arr[prev]:lis[i]=max(lis[i],lis[prev]+1)# Return the maximum of all LIS valuesreturnmax(lis)if__name__=='__main__':arr=[10,22,9,33,21,50,41,60]print(lis(arr))
C#
usingSystem;classGfG{// lis() returns the length of the longest // increasing subsequence in arr[] of size nstaticintlis(int[]arr){intn=arr.Length;int[]lis=newint[n];// Initialize LIS values for all indexesfor(inti=0;i<n;i++)lis[i]=1;// Compute optimized LIS values in bottom up mannerfor(inti=1;i<n;i++){for(intprev=0;prev<i;prev++){if(arr[i]>arr[prev]&&lis[i]<lis[prev]+1)lis[i]=lis[prev]+1;}}// Pick maximum of all LIS values intmax=0;for(inti=0;i<n;i++){if(max<lis[i])max=lis[i];}returnmax;}staticvoidMain(){int[]arr={10,22,9,33,21,50,41,60};Console.WriteLine(lis(arr));}}
JavaScript
// lis() returns the length of the longest// increasing subsequence in arr[] of size nfunctionlis(arr){letn=arr.length;letlis=Array(n).fill(0);letmax=0;// Initialize LIS values for all indexesfor(leti=0;i<n;i++)lis[i]=1;// Compute optimized LIS values in// bottom up manner for(leti=1;i<n;i++){for(letprev=0;prev<i;prev++){if(arr[i]>arr[prev]&&lis[i]<lis[prev]+1)lis[i]=lis[prev]+1;}}// Pick maximum of all LIS values for(leti=0;i<n;i++)if(max<lis[i])max=lis[i];returnmax;}letarr=[10,22,9,33,21,50,41,60];console.log(lis(arr));
Output
5
[Expected Approach] Using Binary Search - O(n Log n) Time and O(n) Space
We can solve this in O(n Log n) time using Binary Search. The idea is to traverse the given sequence and maintain a separate list of sorted subsequence so far. For every new element, find its position in the sorted subsequence using Binary Search.
We use cookies to ensure you have the best browsing experience on our website. By using our site, you
acknowledge that you have read and understood our
Cookie Policy &
Privacy Policy
Improvement
Suggest Changes
Help us improve. Share your suggestions to enhance the article. Contribute your expertise and make a difference in the GeeksforGeeks portal.
Create Improvement
Enhance the article with your expertise. Contribute to the GeeksforGeeks community and help create better learning resources for all.