The longest increasing subsequence problem is to find a subsequence of a given sequence in which the subsequence’s elements are in sorted order, lowest to highest, and in which the subsequence is as long as possible. This subsequence is not necessarily contiguous, or unique.

For example, consider below subsequence

0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15

Longest increasing subsequence is

0, 2, 6, 9, 11, 15

This subsequence has length 6; the input sequence has no 7-member increasing subsequences. The longest increasing subsequence in this example is not unique: for instance,

0, 4, 6, 9, 11, 15 or

0, 4, 6, 9, 13, 15

are other increasing subsequences of equal length in the same input sequence.

The idea is to use **recursion** to solve this problem. For each item, there are two possibilities –

2. We exclude current item from LIS and recurse for remaining items.

Finally, we return maximum value we get by including or excluding current item. The base case of the recursion would be when no items are left.

Below is C++ and Java implementation of the idea:

## C++

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include <iostream> #include <climits> using namespace std; // Function to find length of longest increasing subsequence int LIS(int arr[], int i, int n, int prev) { // Base case: nothing is remaining if (i == n) return 0; // case 1: exclude the current element and process the // remaining elements int excl = LIS(arr, i + 1, n, prev); // case 2: include the current element if it is greater // than previous element in LIS int incl = 0; if (arr[i] > prev) incl = 1 + LIS(arr, i + 1, n, arr[i]); // return maximum of above two choices return max(incl, excl); } // main function int main() { int arr[] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; int n = sizeof(arr) / sizeof(arr[0]); cout << "Length of LIS is " << LIS(arr, 0, n, INT_MIN); return 0; } |

`Output:`

Length of LIS is 6

## Java

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
class LIS { // Function to find length of longest increasing subsequence public static int LIS(int[] A, int i, int n, int prev) { // Base case: nothing is remaining if (i == n) { return 0; } // case 1: exclude the current element and process the // remaining elements int excl = LIS(A, i + 1, n, prev); // case 2: include the current element if it is greater // than previous element in LIS int incl = 0; if (A[i] > prev) { incl = 1 + LIS(A, i + 1, n, A[i]); } // return maximum of above two choices return Integer.max(incl, excl); } // Program for Longest Increasing Subsequence public static void main(String[] args) { int[] A = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; System.out.print("Length of LIS is " + LIS(A, 0, A.length, Integer.MIN_VALUE)); } } |

`Output:`

Length of LIS is 6

The time complexity of above solution is exponential and auxiliary space used by the program is O(1).

The problem has an **optimal substructure**. That means the problem can be broken down into smaller, simple “subproblems”, which can further be divided into yet simpler, smaller subproblems until the solution becomes trivial. Above solution also exhibits **overlapping subproblems**. If we draw the recursion tree of the solution, we can see that the same sub-problems are getting computed again and again. We know that problems having optimal substructure and overlapping subproblems can be solved by using **Dynamic Programming**, in which subproblem solutions are *Memo*ized rather than computed again and again. The *Memo*ized version follows the top-down approach, since we first break the problem into subproblems and then calculate and store values.

We can also solve this problem in bottom-up manner. In the bottom-up approach, we solve smaller sub-problems first, then solve larger sub-problems from them. The following bottom-up approach computes L[i], for each 0 <= i < n, which stores the length of the longest increasing subsequence of subarray arr[0..i] that ends with arr[i]. To calculate L[i], we consider LIS of all smaller values of i (say j) already computed and pick the maximum L[j] where arr[j] is less than the current element arr[i]. It has the same asymptotic run-time as Memoization but no recursion overhead.

Below is C++ and Java implementation of the idea:

## C++

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
#include <iostream> #include <climits> using namespace std; // Iterative function to find length of longest increasing subsequence // of given array int LIS(int arr[], int n) { // array to store sub-problem solution. L[i] stores the length // of the longest increasing subsequence ends with arr[i] int L[n] = { 0 }; // longest increasing subsequence ending with arr[0] has length 1 L[0] = 1; // start from second element in the array for (int i = 1; i < n; i++) { // do for each element in subarray arr[0..i-1] for (int j = 0; j < i; j++) { // find longest increasing subsequence that ends with arr[j] // where arr[j] is less than the current element arr[i] if (arr[j] < arr[i] && L[j] > L[i]) L[i] = L[j]; } // include arr[i] in LIS L[i]++; } // find longest increasing subsequence (having maximum length) int lis = INT_MIN; for (int x : L) lis = max(lis, x); return lis; } // main function int main() { int arr[] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; int n = sizeof(arr)/sizeof(arr[0]); cout << "Length of LIS is " << LIS(arr, n); return 0; } |

`Output:`

Length of LIS is 6

## Java

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
import java.util.Arrays; class LIS { // Iterative function to find length of longest increasing sub-sequence // of given array public static int LIS(int[] A) { // array to store sub-problem solution. L[i] stores the length // of the longest increasing sub-sequence ends with A[i] int[] L = new int[A.length]; // longest increasing sub-sequence ending with A[0] has length 1 L[0] = 1; // start from second element in the array for (int i = 1; i < A.length; i++) { // do for each element in sub-array A[0..i-1] for (int j = 0; j < i; j++) { // find longest increasing sub-sequence that ends with A[j] // where A[j] is less than the current element A[i] if (A[j] < A[i] && L[j] > L[i]) { L[i] = L[j]; } } // include A[i] in LIS L[i]++; } // return longest increasing sub-sequence (having maximum length) return Arrays.stream(L).max().getAsInt(); } // main function public static void main(String[] args) { int[] A = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; System.out.print("Length of LIS is " + LIS(A)); } } |

`Output:`

Length of LIS is 6

The time complexity of above solution is O(n^{2}) and auxiliary space used by the program is O(n).

**How to print LIS?**

Above solutions only print the length of LIS but do not actually print LIS itself. To print the LIS, we actually have to store the longest increasing subsequence in lookup table instead of storing just LIS length.

For example consider

arr = [ 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15]

The Longest Increasing Subsequence of subarray arr[0..i] that ends with arr[i] are –

LIS[0] – 0

LIS[1] – 0 8

LIS[2] – 0 4

LIS[3] – 0 8 12

LIS[4] – 0 2

LIS[5] – 0 8 10

LIS[6] – 0 4 6

LIS[7] – 0 8 12 14

LIS[8] – 0 1

LIS[9] – 0 4 6 9

LIS[10] – 0 4 5

LIS[11] – 0 4 6 9 13

LIS[12] – 0 2 3

LIS[13] – 0 4 6 9 11

LIS[14] – 0 4 6 7

LIS[15] – 0 4 6 9 13 15

Below is C++ implementation of the idea:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
#include <iostream> #include <vector> using namespace std; // Iterative function to find longest increasing subsequence // of given array void findLIS(int arr[], int n) { // LIS[i] stores the longest increasing subsequence of subarray // arr[0..i] that ends with arr[i] vector<int> LIS[n]; // LIS[0] denotes longest increasing subsequence ending with arr[0] LIS[0].push_back(arr[0]); // start from second element in the array for (int i = 1; i < n; i++) { // do for each element in subarray arr[0..i-1] for (int j = 0; j < i; j++) { // find longest increasing subsequence that ends with arr[j] // where arr[j] is less than the current element arr[i] if (arr[j] < arr[i] && LIS[j].size() > LIS[i].size()) LIS[i] = LIS[j]; } // include arr[i] in LIS[i] LIS[i].push_back(arr[i]); } // uncomment below lines to print contents of vector LIS /* for (int i = 0; i < n; i++) { cout << "LIS[" << i << "] - "; for (int j : LIS[i]) cout << j << " "; cout << endl; } */ // j will contain index of LIS int j; for (int i = 0; i < n; i++) if (LIS[j].size() < LIS[i].size()) j = i; // print LIS for (int i : LIS[j]) cout << i << " "; } // main function int main() { int arr[] = { 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15 }; int n = sizeof(arr)/sizeof(arr[0]); findLIS(arr, n); return 0; } |

`Output:`

0 4 6 9 13 15

The time complexity of above solution is O(n^{2}) and auxiliary space used by the program is O(n^{2}).

We will soon be discussing O(nlogn) algorithm to find length of Longest increasing subsequence.

**References: **

1. https://en.wikipedia.org/wiki/Longest_increasing_subsequence

2. Dynamic Programming #1: Longest Increasing Subsequence

**Thanks for reading.**

Please use ideone or C++ Shell or any other online compiler link to post code in comments.

Like us? Please spread the word and help us grow. Happy coding 🙂

## Leave a Reply

Great post. Thanks to the team.

Recursive solution is neat…

This is trivial but in the last function findLIS,

L should be a vector of vector. Small typo. Thanks!

Yes, LIS should be a 2D int vector. @Author, please make a change!