Dada una matriz llegar[] de tamaño norte , la tarea es encontrar la longitud de la subsecuencia creciente más larga (LIS), es decir, la subsecuencia más larga posible en la que los elementos de la subsecuencia se ordenan en orden creciente.

Subsecuencia creciente más larga
Ejemplos:
Aporte: arreglo[] = {3, 10, 2, 1, 20}
Producción: 3
Explicación: La subsecuencia creciente más larga es 3, 10, 20.Aporte: arreglo[] = {50, 3, 10, 7, 40, 80}
Producción: 4
Explicación: La subsecuencia creciente más larga es {3, 7, 40, 80}
Aporte: arreglo[] = {30, 20, 10}
Producción: 1
Explicación: Las subsecuencias crecientes más largas son {30}, {20} y (10)Aporte: arreglo[] = {10, 20, 35, 80}
Producción: 4
Explicación: Toda la matriz está ordenada
Secuencia creciente más larga usando recursividad :
La idea es recorrer la matriz de entrada de izquierda a derecha y encontrar la longitud de la subsecuencia creciente más larga (LIS) que termina con cada elemento arr[i]. Sea L[i] la longitud encontrada para arr[i]. Al final devolvemos el máximo de todos los valores de L[i]. Ahora surge la pregunta, ¿cómo calculamos L[i]? Para esto, utilizamos la recursividad, consideramos todos los elementos más pequeños a la izquierda de arr[i], calculamos recursivamente el valor LIS para todos los elementos más pequeños a la izquierda, tomamos el máximo de todos y le sumamos 1. Si no hay ningún elemento más pequeño a la izquierda de un elemento, devolvemos 1.
Dejar L(i) ser la longitud del LIS que termina en el índice i tal que arr[i] es el último elemento del LIS. Entonces, L(i) puede escribirse recursivamente como:
- L(i) = 1 + máx(L(j) ) donde 0
- L(i) = 1, si no existe tal j.
Formalmente, la longitud del LIS que termina en el índice i , es 1 mayor que el máximo de longitudes de todos los LIS que terminan en algún índice j tal que llegar[j]
dónde j .
Podemos ver que la relación de recurrencia anterior sigue la subestructura óptima propiedad.
Ilustración:
Siga la siguiente ilustración para una mejor comprensión:
Considere arr[] = {3, 10, 2, 11}
L(i): denota LIS del subarreglo que termina en la posición 'i'
Árbol de recursividad
Siga los pasos que se mencionan a continuación para implementar la idea anterior:
- Crea una función recursiva.
- Para cada llamada recursiva, Iterar desde el yo = 1 a la posición actual y haga lo siguiente:
- Encuentre la longitud posible de la subsecuencia creciente más larga que termina en la posición actual si la secuencia anterior terminó en i .
- Actualice la longitud máxima posible en consecuencia.
- Repita esto para todos los índices y encuentre la respuesta.
A continuación se muestra la implementación del enfoque recursivo:
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_ending_here) max_ending_here = res + 1; } // Comparar max_ending_here con // el máximo general. Y actualice el // máximo general si es necesario 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() 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_ending_here) max_ending_here = res + 1; } // Comparar max_ending_here con el // máximo general. Y actualice el máximo general si es necesario si (*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; }>
Java // 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_ending_here) max_ending_here = res + 1; } // Compara max_ending_here con el máximo general. Y // actualizar el máximo general si es necesario 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>
Pitón # 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 # Comparar maxEndingHere con el máximo general. Y # actualizar el máximo general si es necesario máximo = max(maximum, maxEndingHere) return maxEndingHere def lis(arr): # Para permitir el acceso de la variable global máximo global # Longitud de arr n = len(arr) # La variable máxima contiene el resultado máximo = 1 # La función _lis() almacena su resultado en máximo _lis(arr, n) return máximo # Programa controlador para probar la función anterior si __name__ == '__main__': arr = [10, 22, 9, 33 , 21, 50, 41, 60] n = len(arr) # Llamada a función print('La longitud de lis es', lis(arr)) # Este código es una contribución de 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_ending_here) max_ending_here = res + 1; } // Comparar max_ending_here con el máximo general // y actualizar el máximo general si es necesario 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 >
Producción
Length of lis is 5>
Complejidad del tiempo: o(2norte) La complejidad temporal de este enfoque recursivo es exponencial ya que existe un caso de subproblemas superpuestos como se explica en el diagrama de árbol recursivo anterior.
Espacio Auxiliar: O(1). No se utiliza ningún espacio externo para almacenar valores aparte del espacio de la pila interna.
Subsecuencia creciente más larga usando Memorización :
Si se observa con atención, podemos ver que la solución recursiva anterior también sigue el subproblemas superpuestos propiedad, es decir, la misma subestructura resuelta una y otra vez en diferentes rutas de llamada de recursividad. Podemos evitar esto utilizando el enfoque de memorización.
Podemos ver que cada estado se puede identificar de forma única mediante dos parámetros:
- Índice actual (denota el último índice del LIS) y
- Índice anterior (indica el índice final del LIS anterior detrás del cual se encuentra el llegar[yo] está siendo concatenado).
A continuación se muestra la implementación del enfoque anterior.
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 tomar = INT_MIN; if (idx_prev == -1 || a[idx]> a[idx_prev]) { tomar = 1 + f(idx + 1, idx, n, a, dp); } return dp[idx][prev_idx + 1] = max(tomar, noTomar); } // Función para encontrar la longitud de // la subsecuencia creciente más larga int longSubsequence(int n, int a[]) { vector> dp(n + 1, vector (norte + 1, -1)); devolver f(0, -1, n, a, dp); } // Programa controlador para probar la función anterior int main() { int a[] = { 3, 10, 2, 1, 20 }; int n = tamaño de (a) / tamaño de (a [0]); // Llamada a función cout<< 'Length of lis is ' << longestSubsequence(n, a); return 0; }>
Java // 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]) { tomar = 1 + f(idx + 1, idx, n, a, dp); } return dp[idx][prev_idx + 1] = Math.max(tomar, noTake); } // La función contenedora para _lis() static int lis(int arr[], int n) { // La función _lis() almacena su resultado en max int dp[][] = new int[n + 1][ norte + 1]; para (int fila[]: dp) Arrays.fill(fila, -1); devolver f(0, -1, n, arr, dp); } // Programa controlador para probar las funciones anteriores public static void main(String args[]) { int a[] = { 3, 10, 2, 1, 20 }; int n = a.longitud; // Llamada a función System.out.println('La longitud de lis es ' + lis(a, n)); } } // Este código es aportado por Sanskar.>
Pitón # 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_prev]): tomar = 1 + f(idx + 1, idx, n, a, dp) dp[idx][idx_prev + 1] = max(tomar, noTake) devolver dp[idx][idx_prev + 1] # Función para encontrar la longitud de la # subsecuencia creciente más larga. def Subsecuencia más larga (n, a): dp = [[-1 para i en el rango (n + 1)] para j en el rango (n + 1)] return f(0, -1, n, a, dp) # Controlador programa para probar la función anterior si __name__ == '__main__': a = [3, 10, 2, 1, 20] n = len(a) # Llamada a función print('La longitud de lis es', lowerSubsequence( n, a)) # Este código es aportado por 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]) { tomar = 1 + f(idx + 1, idx, n, a, dp); } return dp[idx, prev_idx + 1] = Math.Max(tomar, noTake); } // Función para encontrar la longitud de la // subsecuencia creciente más larga. public static int lowerSubsequence(int n, int[] a) { int[, ] dp = new int[n + 1, n + 1]; para (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]) { tomar = 1 + f(idx + 1, idx, n, a, dp); } return (dp[idx][prev_idx + 1] = Math.max(tomar, noTake)); } // Función para encontrar la longitud de la // subsecuencia creciente más larga. function lowerSubsequence(n, a) { var dp = Array(n + 1) .fill() .map(() => Array(n + 1).fill(-1)); devolver f(0, -1, n, a, dp); } /* Programa controlador para probar la función anterior */ var a = [3, 10, 2, 1, 20]; varn = 5; console.log('La longitud de lis es ' + lowerSubsequence(n, a)); // Este código es una contribución de satwiksuman.>
Producción
Length of lis is 3>
Complejidad del tiempo: EN2)
Espacio Auxiliar: EN2)
Subsecuencia creciente más larga usando Programación dinámica :
Debido a la subestructura óptima y la propiedad de subproblema superpuesta, también podemos utilizar la programación dinámica para resolver el problema. En lugar de memorizar, podemos usar el bucle anidado para implementar la relación recursiva.
El bucle exterior se ejecutará desde yo = 1 a norte y el bucle interior se ejecutará desde j = 0 a yo y utilice la relación de recurrencia para resolver el problema.
Katrina Kaif
A continuación se muestra la implementación del enfoque anterior:
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]>arreglo[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; }>
Java // 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]>arreglo[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>
Pitón # 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] y 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]>arreglo[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 >
Producción
Length of lis is 5>
Complejidad del tiempo: EN2) Como se utiliza un bucle anidado.
Espacio Auxiliar: O(N) Uso de cualquier matriz para almacenar valores LIS en cada índice.
Nota: La complejidad temporal de la solución de programación dinámica (DP) anterior es O(n^2), pero existe una Solución O(N* logN) para el problema LIS. No hemos discutido aquí la solución O(N log N).
Referirse: Tamaño de subsecuencia creciente más largo (N * logN) para el enfoque mencionado.