Biorąc pod uwagę tablicę arr[] wielkościowy N , zadaniem jest znalezienie długości najdłuższego podciągu rosnącego (LIS), czyli najdłuższego możliwego podciągu, w którym elementy podciągu są posortowane w kolejności rosnącej.

Najdłuższy podciąg rosnący
np. zera
Przykłady:
Wejście: tablica [] = {3, 10, 2, 1, 20}
Wyjście: 3
Wyjaśnienie: Najdłuższy rosnący podciąg to 3, 10, 20Wejście: tablica [] = {50, 3, 10, 7, 40, 80}
Wyjście: 4
Wyjaśnienie: Najdłuższy podciąg rosnący to {3, 7, 40, 80}
Wejście: tablica [] = {30, 20, 10}
Wyjście: 1
Wyjaśnienie: Najdłuższe rosnące podciągi to {30}, {20} i (10)Wejście: tablica [] = {10, 20, 35, 80}
Wyjście: 4
Wyjaśnienie: Cała tablica jest posortowana
Najdłuższy rosnący ciąg przy użyciu Rekurencja :
Pomysł polega na przechodzeniu przez tablicę wejściową od lewej do prawej i znajdowaniu długości najdłuższego podciągu rosnącego (LIS) kończącego się każdym elementem arr[i]. Niech długość znaleziona dla arr[i] będzie L[i]. Na koniec zwracamy maksimum wszystkich wartości L[i]. Teraz pojawia się pytanie, jak obliczyć L[i]? W tym celu używamy rekurencji, rozważamy wszystkie mniejsze elementy po lewej stronie arr[i], rekurencyjnie obliczamy wartość LIS dla wszystkich mniejszych elementów po lewej stronie, bierzemy maksimum ze wszystkich i dodajemy do niego 1. Jeśli po lewej stronie elementu nie ma mniejszego elementu, zwracamy 1.
Pozwalać L(i) będzie długością LIS kończącą się na indeksie I tak, że arr[i] jest ostatnim elementem LIS. Następnie L(i) można zapisać rekurencyjnie jako:
- L(i) = 1 + max(L(j) ) gdzie 0
- L(i) = 1, jeśli takie j nie istnieje.
Formalnie długość LIS kończąca się na indeksie I , jest o 1 większe niż maksymalna długość wszystkich LIS kończących się na pewnym indeksie J takie, że arr[j]
Gdzie J .
Widzimy, że powyższa relacja powtarzalności jest zgodna z optymalna podbudowa nieruchomość.
Ilustracja:
Aby lepiej zrozumieć, postępuj zgodnie z poniższą ilustracją:
Rozważ arr[] = {3, 10, 2, 11}
L(i): Oznacza LIS podtablicy kończącej się na pozycji „i”
Drzewo rekurencji
Wykonaj kroki wymienione poniżej, aby wdrożyć powyższy pomysł:
- Utwórz funkcję rekurencyjną.
- Dla każdego wywołania rekurencyjnego wykonaj iterację od ja = 1 do bieżącej pozycji i wykonaj następujące czynności:
- Znajdź możliwą długość najdłuższego rosnącego podciągu kończącego się w bieżącej pozycji, jeśli poprzednia sekwencja kończyła się w I .
- Odpowiednio zaktualizuj maksymalną możliwą długość.
- Powtórz tę czynność dla wszystkich indeksów i znajdź odpowiedź
Poniżej znajduje się implementacja podejścia rekurencyjnego:
jak zainicjować tablicę w JavieC++
// 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; } // Porównaj max_ending_here z // ogólnym max. I zaktualizuj // ogólne maksimum, jeśli to konieczne, jeśli (*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; } // Porównaj max_ending_here z ogólnym // max. I w razie potrzeby zaktualizuj ogólną wartość maksymalną, jeśli (*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; }>
Jawa // 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; } // Porównaj max_ending_here z ogólnym max. I // zaktualizuj ogólne maksimum, jeśli to konieczne, jeśli (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>
Pyton # 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 # Porównaj maxEndingHere z ogólnym maksimum. I # w razie potrzeby zaktualizuj całkowite maksimum maksimum = max(maximum, maxEndingHere) return maxEndingHere def lis(arr): # Aby umożliwić dostęp do zmiennej globalnej global maksimum # Długość arr n = len(arr) # Zmienna maksymalna przechowuje wynik maksimum = 1 # Funkcja _lis() zapisuje swój wynik w maksimum _lis(arr, n) return maksimum # Program sterownika testujący powyższą funkcję, jeśli __name__ == '__main__': arr = [10, 22, 9, 33 , 21, 50, 41, 60] n = len(arr) # Wywołanie funkcji print('Długość lis to', lis(arr)) # Ten kod został opracowany przez 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; } // Porównaj tutaj max_ending_here z ogólnym maksimum // i zaktualizuj ogólne maksimum, jeśli to konieczne, jeśli (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 >
Wyjście
Length of lis is 5>
Złożoność czasowa: O(2N) Złożoność czasowa tego podejścia rekurencyjnego jest wykładnicza, ponieważ istnieje przypadek nakładania się podproblemów, jak wyjaśniono na powyższym diagramie drzewa rekurencyjnego.
Przestrzeń pomocnicza: O(1). Do przechowywania wartości nie jest używana żadna przestrzeń zewnętrzna poza wewnętrzną przestrzenią stosu.
Najdłuższy rosnący podciąg przy użyciu Zapamiętywanie :
Jeśli zauważymy to uważnie, zobaczymy, że powyższe rozwiązanie rekurencyjne również następuje po nakładające się podproblemy właściwość, tj. ta sama podstruktura rozwiązywana wielokrotnie w różnych ścieżkach wywołań rekurencji. Możemy tego uniknąć, stosując metodę zapamiętywania.
Widzimy, że każdy stan można jednoznacznie zidentyfikować za pomocą dwóch parametrów:
- Aktualny indeks (oznacza ostatni indeks LIS) i
- Poprzedni indeks (oznacza indeks końcowy poprzedniego LIS, za którym znajduje się Arr[i] jest łączone).
Poniżej implementacja powyższego podejścia.
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][poprzedni_idx + 1] != -1) { return dp[idx][poprzedni_idx + 1]; } int notTake = 0 + f(idx + 1, prev_idx, n, a, dp); int pobranie = INT_MIN; if (prev_idx == -1 || a[idx]> a[prev_idx]) { take = 1 + f(idx + 1, idx, n, a, dp); } return dp[idx][prev_idx + 1] = max(weź, niebierz); } // Funkcja obliczająca długość // najdłuższego rosnącego podciągu int longestSubsequence(int n, int a[]) { wektor> dp(n + 1, wektor (n + 1, -1)); zwróć f(0, -1, n, a, dp); } // Program sterownika do testowania powyższej funkcji int main() { int a[] = { 3, 10, 2, 1, 20 }; int n = rozmiar(a) / rozmiar(a[0]); // Wywołanie funkcji cout<< 'Length of lis is ' << longestSubsequence(n, a); return 0; }>
Jawa // 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]) { weź = 1 + f(idx + 1, idx, n, a, dp); } return dp[idx][prev_idx + 1] = Math.max(weź, nieTake); } // Funkcja opakowująca dla _lis() static int lis(int arr[], int n) { // Funkcja _lis() przechowuje swój wynik w max int dp[][] = new int[n + 1][ n + 1]; for (int row[] : dp) Arrays.fill(row, -1); zwróć f(0, -1, n, tablica, dp); } // Program sterownika do testowania powyższych funkcji public static void main(String args[]) { int a[] = { 3, 10, 2, 1, 20 }; int n = a.długość; // Wywołanie funkcji System.out.println('Długość lis wynosi ' + lis(a, n)); } } // Ten kod pochodzi od firmy Sanskar.>
Pyton # 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] # Funkcja obliczająca długość najdłuższego rosnącego # podciągu. def longestSubsequence(n, a): dp = [[-1 dla i w zakresie (n + 1)]dla j w zakresie (n + 1)] return f(0, -1, n, a, dp) # Sterownik program do testowania powyższej funkcji, jeśli __name__ == '__main__': a = [3, 10, 2, 1, 20] n = len(a) # Wywołanie funkcji print('Długość lis to', longestSubsequence( n, a)) # Ten kod został napisany przez 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]) { weź = 1 + f(idx + 1, idx, n, a, dp); } return dp[idx, prev_idx + 1] = Math.Max(weź, nieTake); } // Funkcja obliczająca długość najdłuższego // rosnącego podciągu. public static int longestSubsequence(int n, int[] a) { int[, ] dp = nowy int[n + 1, n + 1]; for (int i = 0; tj< 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[poprzedni_idx]) { weź = 1 + f(idx + 1, idx, n, a, dp); } return (dp[idx][prev_idx + 1] = Math.max(weź, nieTake)); } // Funkcja obliczająca długość najdłuższego // rosnącego podciągu. funkcja longestSubsequence(n, a) { var dp = Array(n + 1) .fill() .map(() => Array(n + 1).fill(-1)); zwróć f(0, -1, n, a, dp); } /* Program sterownika testujący powyższą funkcję */ var a = [3, 10, 2, 1, 20]; zm n = 5; console.log('Długość lis wynosi ' + najdłuższa część(n, a)); // Ten kod został napisany przez satwiksuman.>
Wyjście
Length of lis is 3>
Złożoność czasowa: NA2)
Przestrzeń pomocnicza: NA2)
Najdłuższy rosnący podciąg przy użyciu Programowanie dynamiczne :
Ze względu na optymalną podstrukturę i nakładające się właściwości podproblemów, do rozwiązania problemu możemy również wykorzystać programowanie dynamiczne. Zamiast zapamiętywania możemy użyć zagnieżdżonej pętli do implementacji relacji rekurencyjnej.
Zewnętrzna pętla będzie działać ja = 1 do N i wewnętrzna pętla będzie działać j = 0 do i i użyj relacji powtarzania, aby rozwiązać problem.
Poniżej implementacja powyższego podejścia:
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; }>
Jawa // 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>
Pyton # 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] i 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 >
Wyjście
Length of lis is 5>
Złożoność czasowa: NA2) Ponieważ używana jest zagnieżdżona pętla.
Przestrzeń pomocnicza: O(N) Użycie dowolnej tablicy do przechowywania wartości LIS w każdym indeksie.
Notatka: Złożoność czasowa powyższego rozwiązania programowania dynamicznego (DP) wynosi O(n^2), ale istnieje Rozwiązanie O(N*logN). dla problemu LIS. Nie omawialiśmy tutaj rozwiązania O(N log N).
Wspominać: Rozmiar najdłuższego rosnącego podciągu (N * logN) dla wspomnianego podejścia.