Open-Source Internship opportunity by OpenGenus for programmers. Apply now.
As the name suggest Longest Increasing Subsequence is to find the longest increasing subsequence in sequence such that elements of subsequence are in sorted increasing order.
Approcah to Solve this problem:
Longest Increasing Subsequence problem can be solved with the help of 3 different Approach:
- Brute Force
- Dynamic Programming
- Binary Search
Time Complexity
Time Complexity of LIS:
- Brute force: O(N^2)
- Dynamic Programming: O(n^2)
- Binary Search: O(nlogn)
Using Binary Search is a efficient Method
Reason: Possible increasing subsequences that can be formed with the elements seen so far, such that the sub-sequences we track are ones that could contribute to the final solution. We will aggressively discard subsequences that can never influence the solution, and this will allow the algorithm to become efficient.
NOTE: Longest Increasing Subsequence need not be consecutive in Original sequence
Example
array: 2,5,7,3,16,1,6,19
Subsequence 1: 2,5,7,16,19
Subsequence 2: 5,7,16,19
Subsequence 3: 2,3,6,19
Subsequence 4: 2,3,16,19
LIS: 2,5,7,16,19
(There can be many Subsequence, LIS has max. length)
Implementing LIS with Brute Force
Basic Idea:
The brute force solution is to consider all non-null subsets of Array. Then collect all the increasing subsequences and return the longest subsequence from them.
Pseudocode:
- Import java.util.*
- lis() returns the length of the longest increasing subsequence in arr[] of size n
static int lis(int[] arr, int n) int max = 0; int[] lst = new int[n];
- Initialize LIS values for all indexes
Arrays.fill(lst, 1);
- Now, Compute optimized LIS values
Create two for loopfor (int i = 1; i < n; i++) { for (int j = 0; j < i; j++) { if (arr[i] > arr[j] && lst[i] < lst[j] + 1) lst[i] = lst[j] + 1; } }
5.Pick maximum of all LIS values
for (int i = 0; i < n; i++)
if (max < lst[i])
max = lst[i];
return max;
6.Write main function and provide input, and it will return the max. length of subsequence possible.
Implementation
Following is the implementation in Java programming language:
import java.util.*;
class brute_LIS
{
static int lis(int[] arr, int n)
{
int max = 0;
int[] lst = new int[n];
// initialize LIS values for all indexes
Arrays.fill(lst, 1);
// Compute optimized LIS values
for (int i = 1; i < n; i++)
{
for (int j = 0; j < i; j++)
{
if (arr[i] > arr[j] &&
lst[i] < lst[j] + 1)
lst[i] = lst[j] + 1;
}
}
//Pick maximum of all LIS values
for (int i = 0; i < n; i++)
if (max < lst[i])
max = lst[i];
return max;
}
// Main function
public static void main(String[] args)
{
int[] arr = { 10, 22, 9, 33, 21, 50, 41, 60 };
int n = arr.length;
System.out.println("Length of lis is " +
lis(arr, n));
}
}
Output
The Time Complexity of this approach using DP is O(N2).
The Space Complexity of this approach using DP is O(N).
Implementing LIS with Binary Search
Basic Idea:
Take original array and put elements of original arrays in increasing subsequence. While filling this subsequence, follow two rules:
Compare current and last if
- current > last: Append current element
- current <= last : Replace it with current element
For Example:
Consider this Original Array, put 3 in increasing subsequence and then compare 3 with 1, Now 1 < 3, Apply 2 Rule: Replace it with current element i.e 1. Then 5, 5 > 1 , Append current element i.e 5. Now 2, 2 < 5, Replace it with 2. Now 6, 6 > 2, Append 6. Now 4 , 4 < 6. Replace 6 with 4
and Finally 9, 9 > 4 . Append 9.
Understand with this.
3
1<-5
1<-2<-6
1<-2<-4<-9
Pseudocode:
- Create arrya for tracking the predecessors/parents of elements of each subsequence.
int parent[] = new int[X.length];
- Create a variable for tracking ends of each increasing subsequence.
int increasingSub[] = new int[X.length + 1];
- To calculate length of longest subsequence
int length = 0;
- Create for loop
for(int i= 0; i<X.length; i++)
- Implement Binary Search to calculate low,high and mid
int low =1; int high = length; while(low <= high) { int mid = (int) Math.ceil((low + high)/2); if(X[increasingSub[mid]] < X[i]) low = mid + 1; else high = mid - 1; } int pos = low;
- Conditions for LIS
- Update parent/previous element for LIS or
parent[i] = increasingSub[pos-1];
- Replace or append
increasingSub[pos] = i;
- Now, update the length of the longest subsequence
if(pos > length) length = pos;
- Generate LIS by travering parent array
- Create a main function and provide the input
Insights:
- Why replace if element of array is small?
In the hope of getting a longer subsequence - Why do we need to track parent?
IncreasingSub array is not sufficient to remember multiple subsequences in consideration
Implementation
Following is the implementation in Java programming language:
public class LIS_binarySearch{
public static void LIS(int X[])
{
int parent[] = new int[X.length];
//Track the predecessors/parents of elements of each subsequence.
int increasingSub[] = new int[X.length + 1];
//Track ends of each increasing subsequence.
int length = 0;
//Length of longest subsequence
for(int i= 0; i<X.length; i++)
{
//Binary Search
int low =1;
int high = length;
while(low <= high)
{
int mid = (int) Math.ceil((low + high)/2);
if(X[increasingSub[mid]] < X[i]){
low = mid + 1;
}
else{
high = mid - 1;
}
}
int pos = low;
//update parent/previous element for LIS
parent[i] = increasingSub[pos-1];
//Replace or append
increasingSub[pos] = i;
//update the length of the longest subsequence
if(pos > length)
length = pos;
}
//Generate LIS by travering parent array
int LIS[] = new int[length];
int k = increasingSub[length];
for(int j = length-1;j>=0; j--)
{
LIS[j] = X[k];
k = parent[k];
}
for(int i = 0;i<length; i++)
{
System.out.println(LIS[i]);
}
}
public static void main(String args[])
{
int X[] = {3,1,5,0,6,4,9};
LIS(X);
}
}
Output
The Time Complexity of this approach using DP is O(N logN).
The Space Complexity of this approach using DP is O(N).
Implementing LIS with Dynamic Programming
Basic Idea:
Create 3 arrays: nums, sizes and paths
As the name suggest, nums is original array. sizes is to track the size of array and paths to track down the increasing subsequence array.
Initially, length is set to 1. Create two variable i and j . Now j is always at index 0 and i is at index 1. So, j always starts from 0 till j=i-1 and i is increment after j=i.
Here key is to compare j and i. So, if j < i then length is increment but max(j+1,previous len) is taken. Else ignore it and i is incremented.
And according mention and update path in third array.
Pseudocode:
- Create a nums array that will store the sequence.
- Now, create a size array to keep track of LIS ending with current position.
int[] sizes = new int[nums.length]
- And accordingly an array path to keep track of the path for printing out.
String[] paths = new String[nums.length]
- Now, check for every index if it is suitable for LIS.
- Create two variable i,j
- Base condition: Assign size of array equal to 1
sizes[i] = 1 paths[i] = nums[i]
- Create a support varialbe called maxLenth to keep track.
- Create two for loops:
i will start at 1 index
j will start at 0 indexfor(int i=1; i<nums.length; i++) for(int j=0; j<nums.length; j++)
- Condition:
nums[j]<nums[i] and sizes[i] < sizes[j] + 1 if(maxLength < sizes[i]) maxLength = sizes[i]
10.After checking condition i++ and j always starts from 0 index till i position.
11.Finally, LIS is printed.
Implementation
Following is the implementation in Java programming language:
public class LIS {
public static void main(String[] args){
int[] nums = {1,3,2,9,6,10,5};
printLIS(nums);
}
//main function
public static void printLIS(int[] nums){
//creating size and path array
String[] paths = new String[nums.length];
int[] sizes = new int[nums.length];
//Initially, assign sizes and path position
for(int i=0; i<nums.length; i++){
sizes[i] = 1;
paths[i] = nums[i] + " " ;
}
//Creating maxLenth to keep track
int maxLength = 1;
for(int i=1; i<nums.length; i++){
for(int j=0; j<nums.length; j++){
// i at firstposition
// j at zeroposition
//here j<i and size must be increasing
if(nums[i]>nums[j] && sizes[i] < sizes[j] + 1){;
//if so, we need to update sizes and path
sizes[i] = sizes[j] + 1;
paths[i] = paths[j] + nums[i] + " ";
// append current values to end!!
if(maxLength < sizes[i])
maxLength = sizes[i];
}
}
}
// finally go scanning the size of array again and print out the path when size matches
for(int i=1; i<nums.length; i++){
if(sizes[i] == maxLength)
System.out.println("LIS: " + paths[i]);
}
}
}
Output
The Time Complexity of this approach using DP is O(N2).
The Space Complexity of this approach using DP is O(N).