Python program for Longest Increasing Subsequence
Last Updated :
28 Jun, 2023
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[] = {3, 2}
Output:1
Explanation: The longest increasing subsequences are {3} and {2}
Input: arr[] = {50, 3, 10, 7, 40, 80}
Output: 4
Explanation: The longest increasing subsequence is {3, 7, 40, 80}
Longest Increasing Sequence using Recursion:
The problem can be solved based on the following idea:
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(j) ) where 0 < j < i and arr[j] < arr[i]; or
- L(i) = 1, if no such j 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 j such that arr[j] < arr[i] where j < i.
We can see that the above recurrence relation follows the optimal substructure property.
Below is the implementation of the recursive approach:
Python3
# A naive Python implementation of LIS problem
# Global variable to store the maximum
global maximum
# To make use of recursive calls, this function must return
# two things:
# 1) Length of LIS ending with element arr[n-1]. We use
# max_ending_here for this purpose
# 2) Overall maximum as the LIS may end with an element
# before arr[n-1] max_ref is used this purpose.
# The value of LIS of full array of size n is stored in
# *max_ref which is our final result
def _lis(arr, n):
# To allow the access of global variable
global maximum
# Base Case
if n == 1:
return 1
# maxEndingHere is the length of LIS ending with arr[n-1]
maxEndingHere = 1
# Recursively get all LIS ending with
# arr[0], arr[1]..arr[n-2]
# If arr[i-1] is smaller than arr[n-1], and
# max ending with arr[n-1] needs to be updated,
# then update it
for i in range(1, n):
res = _lis(arr, i)
if arr[i-1] < arr[n-1] and res+1 > maxEndingHere:
maxEndingHere = res + 1
# Compare maxEndingHere with overall maximum. And
# update the overall maximum if needed
maximum = max(maximum, maxEndingHere)
return maxEndingHere
def lis(arr):
# To allow the access of global variable
global maximum
# Length of arr
n = len(arr)
# Maximum variable holds the result
maximum = 1
# The function _lis() stores its result in maximum
_lis(arr, n)
return maximum
# Driver program to test the above function
if __name__ == '__main__':
arr = [10, 22, 9, 33, 21, 50, 41, 60]
n = len(arr)
# Function call
print("Length of lis is", lis(arr))
# This code is contributed by NIKHIL KUMAR SINGH
Complexity Analysis:
- Time Complexity: O(2n) The time complexity of this recursive approach is exponential as there is a case of overlapping subproblems as explained in the recursive tree diagram above.
- Auxiliary Space: O(1). No external space is used for storing values apart from the internal stack space.
Longest Increasing Subsequence using Memoization:
If noticed carefully, we can see that the above recursive solution 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.
We can see that each state can be uniquely identified using two parameters:
- Current index (denotes the last index of the LIS) and
- Previous index (denotes the ending index of the previous LIS behind which the arr[i] is being concatenated).
Below is the implementation of the above approach.
Python3
# A Naive Python recursive implementation
# of LIS problem
import sys
# To make use of recursive calls, this
# function must return two things:
# 1) Length of LIS ending with element arr[n-1].
# We use max_ending_here for this purpose
# 2) Overall maximum as the LIS may end with
# an element before arr[n-1] max_ref is
# used this purpose.
# The value of LIS of full array of size n
# is stored in *max_ref which is our final result
def f(idx, prev_idx, n, a, dp):
if (idx == n):
return 0
if (dp[idx][prev_idx + 1] != -1):
return dp[idx][prev_idx + 1]
notTake = 0 + f(idx + 1, prev_idx, n, a, dp)
take = -sys.maxsize - 1
if (prev_idx == -1 or a[idx] > a[prev_idx]):
take = 1 + f(idx + 1, idx, n, a, dp)
dp[idx][prev_idx + 1] = max(take, notTake)
return dp[idx][prev_idx + 1]
# Function to find length of longest increasing
# subsequence.
def longestSubsequence(n, a):
dp = [[-1 for i in range(n + 1)]for j in range(n + 1)]
return f(0, -1, n, a, dp)
# Driver program to test above function
if __name__ == '__main__':
a = [3, 10, 2, 1, 20]
n = len(a)
# Function call
print("Length of lis is", longestSubsequence(n, a))
# This code is contributed by shinjanpatra
Time Complexity: O(N2)
Auxiliary Space: O(N2)
Because of the optimal substructure and overlapping subproblem property, we can also utilise Dynamic programming to solve the problem. Instead of memoization, we can use the nested loop to implement the recursive relation.
The outer loop will run from i = 1 to N and the inner loop will run from j = 0 to i and use the recurrence relation to solve the problem.
Below is the implementation of the above approach:
Python3
# Dynamic programming Python implementation
# of LIS problem
# lis returns length of the longest
# increasing subsequence in arr of size n
def lis(arr):
n = len(arr)
# Declare the list (array) for LIS and
# initialize LIS values for all indexes
lis = [1]*n
# Compute optimized LIS values in bottom up manner
for i in range(1, n):
for j in range(0, i):
if arr[i] > arr[j] and lis[i] < lis[j] + 1:
lis[i] = lis[j]+1
# Initialize maximum to 0 to get
# the maximum of all LIS
maximum = 0
# Pick maximum of all LIS values
for i in range(n):
maximum = max(maximum, lis[i])
return maximum
# Driver program to test above function
if __name__ == '__main__':
arr = [10, 22, 9, 33, 21, 50, 41, 60]
print("Length of lis is", lis(arr))
# This code is contributed by Nikhil Kumar Singh
Time Complexity: O(N2) As a nested loop is used.
Auxiliary Space: O(N) Use of any array to store LIS values at each index.
Please Refer Longest Increasing Subsequence for detailed article.
Similar Reads
Python Program for Longest Common Subsequence LCS Problem Statement: Given two sequences, find the length of longest subsequence present in both of them. A subsequence is a sequence that appears in the same relative order, but not necessarily contiguous. For example, "abc", "abg", "bdf", "aeg", '"acefg", .. etc are subsequences of "abcdefg". So
3 min read
Python Program to Find the Longest Bitonic Subsequence Given an array arr[0 ... n-1] containing n positive integers, a subsequence of arr[] is called Bitonic if it is first increasing, then decreasing. Write a function that takes an array as argument and returns the length of the longest bitonic subsequence. A sequence, sorted in increasing order is con
3 min read
Python - Longest Substring Length of K Given a String and a character K, find longest substring length of K. Input : test_str = 'abcaaaacbbaa', K = b Output : 2 Explanation : b occurs twice, 2 > 1. Input : test_str = 'abcaacccbbaa', K = c Output : 3 Explanation : Maximum times c occurs is 3. Method #1: Using loop This is brute way to
7 min read
Reverse sequence of strictly increasing integers in a list-Python The task of reversing the sequence of strictly increasing integers in a list in Python involves identifying consecutive increasing subsequences and reversing each subsequence individually. For example, given a list a = [0, 1, 9, 8, 7, 5, 3, 14], the goal is to reverse the strictly increasing subsequ
3 min read
Python3 Program for Longest subsequence of a number having same left and right rotation Given a numeric string S, the task is to find the maximum length of a subsequence having its left rotation equal to its right rotation.Examples:Input: S = "100210601" Output: 4 Explanation: The subsequence "0000" satisfies the necessary condition. The subsequence "1010" generates the string "0101" o
4 min read