logo

Sottosequenza crescente più lunga (LIS)

Dato un array arr[] di dimensione N , il compito è trovare la lunghezza della sottosequenza crescente più lunga (LIS), ovvero la sottosequenza più lunga possibile in cui gli elementi della sottosequenza sono ordinati in ordine crescente.

10 alla potenza di 6
LIS

Sottosequenza crescente più lunga



Esempi:

Ingresso: arr[] = {3, 10, 2, 1, 20}
Produzione: 3
Spiegazione: La sottosequenza crescente più lunga è 3, 10, 20

Ingresso: arr[] = {50, 3, 10, 7, 40, 80}
Produzione: 4
Spiegazione: La sottosequenza crescente più lunga è {3, 7, 40, 80}



Ingresso: arr[] = {30, 20, 10}
Produzione: 1
Spiegazione: Le sottosequenze crescenti più lunghe sono {30}, {20} e (10)

Ingresso: arr[] = {10, 20, 35, 80}
Produzione: 4
Spiegazione: L'intero array viene ordinato

Utilizzo della sequenza crescente più lunga Ricorsione :

L'idea di attraversare l'array di input da sinistra a destra e trovare la lunghezza della sottosequenza crescente più lunga (LIS) che termina con ogni elemento arr[i]. Sia la lunghezza trovata per arr[i] L[i]. Alla fine restituiamo il massimo di tutti i valori L[i]. Ora sorge la domanda: come calcoliamo L[i]? Per questo, usiamo la ricorsione, consideriamo tutti gli elementi più piccoli a sinistra di arr[i], calcoliamo ricorsivamente il valore LIS per tutti gli elementi più piccoli a sinistra, prendiamo il massimo di tutti e vi aggiungiamo 1. Se non c'è un elemento più piccolo a sinistra di un elemento, restituiamo 1.



Permettere L(i) essere la lunghezza della LIS che termina con indice io tale che arr[i] sia l'ultimo elemento della LIS. Allora L(i) può essere scritto ricorsivamente come:

  • L(i) = 1 + max(L(j) ) dove 0
  • L(i) = 1, se tale j non esiste.

Formalmente, la lunghezza della LIS che termina con l'indice io , è 1 maggiore del massimo delle lunghezze di tutte le LIS che terminano con un certo indice J tale che arr[j] Dove J .

Possiamo vedere che la relazione di ricorrenza di cui sopra segue il sottostruttura ottimale proprietà.

Illustrazione:

Seguire l'illustrazione seguente per una migliore comprensione:

Considera arr[] = {3, 10, 2, 11}

L(i): indica LIS del sottoarray che termina nella posizione 'i'

Albero di ricorsione

Albero di ricorsione

Seguire i passaggi indicati di seguito per implementare l'idea di cui sopra:

espressioni java lambda
  • Creare una funzione ricorsiva.
  • Per ogni chiamata ricorsiva, Iterate from the io = 1 alla posizione corrente e procedi come segue:
    • Trova la possibile lunghezza della sottosequenza crescente più lunga che termina nella posizione corrente se la sequenza precedente terminava in io .
    • Aggiornare di conseguenza la lunghezza massima possibile.
  • Ripeti l'operazione per tutti gli indici e trova la risposta

Di seguito è riportata l'implementazione dell'approccio ricorsivo:

C++
// A Naive C++ recursive implementation // of LIS problem #include  using namespace std; // 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 int _lis(int arr[], int n, int* max_ref) {  // Base case  if (n == 1)  return 1;  // 'max_ending_here' is length of  // LIS ending with arr[n-1]  int res, max_ending_here = 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 (int i = 1; i < n; i++) {  res = _lis(arr, i, max_ref);  if (arr[i - 1] < arr[n - 1]  && res + 1>max_fine_qui) max_fine_qui = res + 1;  } // Confronta max_ending_here con // il numero massimo complessivo. E aggiorna il // massimo complessivo se necessario se (*max_ref< max_ending_here)  *max_ref = max_ending_here;  // Return length of LIS ending  // with arr[n-1]  return max_ending_here; } // The wrapper function for _lis() int lis(int arr[], int n) {  // The max variable holds the result  int max = 1;  // The function _lis() stores its  // result in max  _lis(arr, n, &max);  // Returns max  return max; } // Driver program to test above function int main() {  int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };  int n = sizeof(arr) / sizeof(arr[0]);  // Function call  cout << 'Length of lis is ' << lis(arr, n);  return 0; }>
C
// A Naive C recursive implementation // of LIS problem #include  #include  // 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 int _lis(int arr[], int n, int* max_ref) {  // Base case  if (n == 1)  return 1;  // 'max_ending_here' is length of LIS  // ending with arr[n-1]  int res, max_ending_here = 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 (int i = 1; i < n; i++) {  res = _lis(arr, i, max_ref);  if (arr[i - 1] < arr[n - 1]  && res + 1>max_fine_qui) max_fine_qui = res + 1;  } // Confronta max_ending_here con il // max. E aggiorna il massimo complessivo, se necessario, se (*max_ref< max_ending_here)  *max_ref = max_ending_here;  // Return length of LIS ending with arr[n-1]  return max_ending_here; } // The wrapper function for _lis() int lis(int arr[], int n) {  // The max variable holds the result  int max = 1;  // The function _lis() stores its result in max  _lis(arr, n, &max);  // returns max  return max; } // Driver program to test above function int main() {  int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };  int n = sizeof(arr) / sizeof(arr[0]);  // Function call  printf('Length of lis is %d', lis(arr, n));  return 0; }>
Giava
// A Naive Java Program for LIS Implementation import java.io.*; import java.util.*; class LIS {  // Stores the LIS  static int max_ref;  // 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  static int _lis(int arr[], int n)  {  // Base case  if (n == 1)  return 1;  // 'max_ending_here' is length of LIS ending with  // arr[n-1]  int res, max_ending_here = 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 (int i = 1; i < n; i++) {  res = _lis(arr, i);  if (arr[i - 1] < arr[n - 1]  && res + 1>max_fine_qui) max_fine_qui = res + 1;  } // Confronta max_ending_here con il massimo complessivo. E // aggiorna il massimo complessivo se necessario if (max_ref< max_ending_here)  max_ref = max_ending_here;  // Return length of LIS ending with arr[n-1]  return max_ending_here;  }  // The wrapper function for _lis()  static int lis(int arr[], int n)  {  // The max variable holds the result  max_ref = 1;  // The function _lis() stores its result in max  _lis(arr, n);  // Returns max  return max_ref;  }  // Driver program to test above functions  public static void main(String args[])  {  int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };  int n = arr.length;  // Function call  System.out.println('Length of lis is '  + lis(arr, n));  } } // This code is contributed by Rajat Mishra>
Pitone
# 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 # Confronta maxEndingHere con il massimo complessivo. E # aggiorna il massimo complessivo se necessario maxim = max(maximum, maxEndingHere) return maxEndingHere def lis(arr): # Per consentire l'accesso alla variabile globale global maxim # Lunghezza di arr n = len(arr) # La variabile massima contiene il risultato massimo = 1 # La funzione _lis() memorizza il risultato in massimo _lis(arr, n) return massimo # Programma driver per testare la funzione precedente se __nome__ == '__main__': arr = [10, 22, 9, 33 , 21, 50, 41, 60] n = len(arr) # Chiamata di funzione print('La lunghezza di lis è', lis(arr)) # Questo codice è un contributo di NIKHIL KUMAR SINGH>
C#
using System; // A Naive C# Program for LIS Implementation class LIS {  // Stores the LIS  static int max_ref;  // 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  static int _lis(int[] arr, int n)  {  // Base case  if (n == 1)  return 1;  // 'max_ending_here' is length of LIS ending with  // arr[n-1]  int res, max_ending_here = 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 (int i = 1; i < n; i++) {  res = _lis(arr, i);  if (arr[i - 1] < arr[n - 1]  && res + 1>max_fine_qui) max_fine_qui = res + 1;  } // Confronta max_ending_here con il massimo complessivo // e aggiorna il massimo complessivo se necessario if (max_ref< max_ending_here)  max_ref = max_ending_here;  // Return length of LIS ending with arr[n-1]  return max_ending_here;  }  // The wrapper function for _lis()  static int lis(int[] arr, int n)  {  // The max variable holds the result  max_ref = 1;  // The function _lis() stores its result in max  _lis(arr, n);  // Returns max  return max_ref;  }  // Driver program to test above functions  public static void Main()  {  int[] arr = { 10, 22, 9, 33, 21, 50, 41, 60 };  int n = arr.Length;  // Function call  Console.Write('Length of lis is ' + lis(arr, n)  + '
');  } }>
Javascript
>

Produzione
Length of lis is 5>

Complessità temporale: O(2N) La complessità temporale di questo approccio ricorsivo è esponenziale in quanto esiste un caso di sottoproblemi sovrapposti come spiegato nel diagramma ad albero ricorsivo sopra.
Spazio ausiliario: O(1). Nessuno spazio esterno viene utilizzato per memorizzare valori oltre allo spazio dello stack interno.

long per int java

Utilizzo della sottosequenza crescente più lunga Memoizzazione :

Se notiamo attentamente, possiamo vedere che anche la soluzione ricorsiva di cui sopra segue l' sottoproblemi sovrapposti proprietà, ovvero la stessa sottostruttura risolta più e più volte in diversi percorsi di chiamate ricorsive. Possiamo evitarlo utilizzando l'approccio della memorizzazione.

Possiamo vedere che ogni stato può essere identificato univocamente utilizzando due parametri:

  • Indice attuale (indica l'ultimo indice della LIS) e
  • Indice precedente (indica l'indice finale della LIS precedente dietro la quale il arr[i] viene concatenato).

Di seguito è riportata l'implementazione dell'approccio di cui sopra.

C++
// C++ code of memoization approach for LIS #include  using namespace std; // 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 // 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 int f(int idx, int prev_idx, int n, int a[],  vector>& dp) { if (idx == n) { return 0;  } if (dp[idx][prev_idx + 1] != -1) { return dp[idx][prev_idx + 1];  } int notTake = 0 + f(idx + 1, prev_idx, n, a, dp);  int prendere = INT_MIN;  if (idx_prec == -1 || a[idx]> a[idx_prec]) { take = 1 + f(idx + 1, idx, n, a, dp);  } return dp[idx][prev_idx + 1] = max(prendi, nonPrendi); } // Funzione per trovare la lunghezza della // sottosequenza crescente più lunga int longestSubsequence(int n, int a[]) { vettore> dp(n + 1, vettore (n+1, -1));  return f(0, -1, n, a, dp); } // Programma driver per testare la funzione precedente int main() { int a[] = { 3, 10, 2, 1, 20 };  int n = dimensione(a) / dimensione(a[0]);  // Chiamata di funzione cout<< 'Length of lis is ' << longestSubsequence(n, a);  return 0; }>
Giava
// A Memoization Java Program for LIS Implementation import java.lang.*; import java.util.Arrays; class LIS {  // 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  static int f(int idx, int prev_idx, int n, int a[],  int[][] dp)  {  if (idx == n) {  return 0;  }  if (dp[idx][prev_idx + 1] != -1) {  return dp[idx][prev_idx + 1];  }  int notTake = 0 + f(idx + 1, prev_idx, n, a, dp);  int take = Integer.MIN_VALUE;  if (prev_idx == -1 || a[idx]>a[prev_idx]) { prendi = 1 + f(idx + 1, idx, n, a, dp);  } return dp[idx][prev_idx + 1] = Math.max(take, notTake);  } // La funzione wrapper per _lis() static int lis(int arr[], int n) { // La funzione _lis() memorizza il suo risultato in max int dp[][] = new int[n + 1][ n+1];  for (int riga[] : dp) Arrays.fill(riga, -1);  return f(0, -1, n, arr, dp);  } // Programma driver per testare le funzioni di cui sopra public static void main(String args[]) { int a[] = { 3, 10, 2, 1, 20 };  int n = a.lunghezza;  // Chiamata di funzione System.out.println('La lunghezza di lis è ' + lis(a, n));  } } // Questo codice è fornito da Sanskar.>
Pitone
# 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[idx_prec]): prendi = 1 + f(idx + 1, idx, n, a, dp) dp[idx][idx_prec + 1] = max(prendi, nonPrendi) return dp[idx][idx_prec + 1] # Funzione per trovare la lunghezza della sottosequenza # crescente più lunga. def sottosequenzapiùlunga(n, a): dp = [[-1 for i in range(n + 1)]for j in range(n + 1)] return f(0, -1, n, a, dp) # Driver programma per testare la funzione precedente if __name__ == '__main__': a = [3, 10, 2, 1, 20] n = len(a) # Chiamata di funzione print('La lunghezza di lis è', longestSubsequence( n, a)) # Questo codice è un contributo di shinjanpatra>
C#
// C# approach to implementation the memoization approach using System; class GFG {  // 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  public static int INT_MIN = -2147483648;  public static int f(int idx, int prev_idx, int n,  int[] a, int[, ] dp)  {  if (idx == n) {  return 0;  }  if (dp[idx, prev_idx + 1] != -1) {  return dp[idx, prev_idx + 1];  }  int notTake = 0 + f(idx + 1, prev_idx, n, a, dp);  int take = INT_MIN;  if (prev_idx == -1 || a[idx]>a[prev_idx]) { prendi = 1 + f(idx + 1, idx, n, a, dp);  } return dp[idx, prev_idx + 1] = Math.Max(take, notTake);  } // Funzione per trovare la lunghezza della // sottosequenza crescente più lunga.  public static int longestSubsequence(int n, int[] a) { int[, ] dp = new int[n + 1, n + 1];  for (int i = 0; i< n + 1; i++) {  for (int j = 0; j < n + 1; j++) {  dp[i, j] = -1;  }  }  return f(0, -1, n, a, dp);  }  // Driver code  static void Main()  {  int[] a = { 3, 10, 2, 1, 20 };  int n = a.Length;  Console.WriteLine('Length of lis is '  + longestSubsequence(n, a));  } } // The code is contributed by Nidhi goel.>
Javascript
/* A Naive Javascript recursive implementation  of LIS problem */  /* 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  */  function 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];  }  var notTake = 0 + f(idx + 1, prev_idx, n, a, dp);  var take = Number.MIN_VALUE;  if (prev_idx == -1 || a[idx]>a[prev_idx]) { prendi = 1 + f(idx + 1, idx, n, a, dp);  } return (dp[idx][prev_idx + 1] = Math.max(take, notTake));  } // Funzione per trovare la lunghezza della // sottosequenza crescente più lunga.  function longestSubsequence(n, a) { var dp = Array(n + 1) .fill() .map(() => Array(n + 1).fill(-1));  return f(0, -1, n, a, dp);  } /* Programma driver per testare la funzione sopra */ var a = [3, 10, 2, 1, 20];  var n = 5;  console.log('La lunghezza di lis è ' + longestSubsequence(n, a));    // Questo codice è fornito da satwiksuman.>

Produzione
Length of lis is 3>

Complessità temporale: SU2)
Spazio ausiliario: SU2)

Utilizzo della sottosequenza crescente più lunga Programmazione dinamica :

A causa della sottostruttura ottimale e della proprietà del sottoproblema sovrapposto, possiamo anche utilizzare la programmazione dinamica per risolvere il problema. Invece della memorizzazione, possiamo utilizzare il ciclo annidato per implementare la relazione ricorsiva.

Il ciclo esterno verrà eseguito da io = 1 a N e da cui partirà il ciclo interno j = da 0 a i e utilizzare la relazione di ricorrenza per risolvere il problema.

Di seguito è riportata l’implementazione dell’approccio di cui sopra:

C++
// Dynamic Programming C++ implementation // of LIS problem #include  using namespace std; // lis() returns the length of the longest // increasing subsequence in arr[] of size n int lis(int arr[], int n) {  int lis[n];  lis[0] = 1;  // Compute optimized LIS values in  // bottom up manner  for (int i = 1; i < n; i++) {  lis[i] = 1;  for (int j = 0; j < i; j++)  if (arr[i]>arr[j] && lis[i]< lis[j] + 1)  lis[i] = lis[j] + 1;  }  // Return maximum value in lis[]  return *max_element(lis, lis + n); } // Driver program to test above function int main() {  int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };  int n = sizeof(arr) / sizeof(arr[0]);  // Function call  printf('Length of lis is %d
', lis(arr, n));  return 0; }>
Giava
// Dynamic Programming Java implementation // of LIS problem import java.lang.*; class LIS {  // lis() returns the length of the longest  // increasing subsequence in arr[] of size n  static int lis(int arr[], int n)  {  int lis[] = new int[n];  int i, j, max = 0;  // Initialize LIS values for all indexes  for (i = 0; i < n; i++)  lis[i] = 1;  // Compute optimized LIS values in  // bottom up manner  for (i = 1; i < n; i++)  for (j = 0; j < i; j++)  if (arr[i]>arr[j] && lis[i]< lis[j] + 1)  lis[i] = lis[j] + 1;  // Pick maximum of all LIS values  for (i = 0; i < n; i++)  if (max < lis[i])  max = lis[i];  return max;  }  // Driver code  public static void main(String args[])  {  int arr[] = { 10, 22, 9, 33, 21, 50, 41, 60 };  int n = arr.length;  // Function call  System.out.println('Length of lis is '  + lis(arr, n));  } } // This code is contributed by Rajat Mishra>
Pitone
# 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] e 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>
C#
// Dynamic Programming C# implementation of LIS problem using System; class LIS {  // lis() returns the length of the longest increasing  // subsequence in arr[] of size n  static int lis(int[] arr, int n)  {  int[] lis = new int[n];  int i, j, max = 0;  // Initialize LIS values for all indexes  for (i = 0; i < n; i++)  lis[i] = 1;  // Compute optimized LIS values in bottom up manner  for (i = 1; i < n; i++)  for (j = 0; j < i; j++)  if (arr[i]>arr[j] && lis[i]< lis[j] + 1)  lis[i] = lis[j] + 1;  // Pick maximum of all LIS values  for (i = 0; i < n; i++)  if (max < lis[i])  max = lis[i];  return max;  }  // Driver code  public static void Main()  {  int[] arr = { 10, 22, 9, 33, 21, 50, 41, 60 };  int n = arr.Length;  // Function call  Console.WriteLine('Length of lis is '  + lis(arr, n));  } } // This code is contributed by Ryuga>
Javascript
>

Produzione
Length of lis is 5>

Complessità temporale: SU2) Come viene utilizzato un ciclo annidato.
Spazio ausiliario: O(N) Utilizzo di qualsiasi array per memorizzare valori LIS in ciascun indice.

Nota: La complessità temporale della soluzione di Programmazione Dinamica (DP) di cui sopra è O(n^2), ma esiste un Soluzione O(N* logN). per il problema LIS. Non abbiamo discusso qui la soluzione O(N log N).
Fare riferimento: Dimensione della sottosequenza crescente più lunga (N * logN) per l'approccio menzionato.