Podana matrika prihod [] velikosti N , naloga je najti dolžino najdaljšega naraščajočega podzaporedja (LIS), tj. najdaljšega možnega podzaporedja, v katerem so elementi podzaporedja razvrščeni v naraščajočem vrstnem redu.

Najdaljše naraščajoče podzaporedje
Primeri:
Vnos: arr[] = {3, 10, 2, 1, 20}
Izhod: 3
Pojasnilo: Najdaljše naraščajoče podzaporedje je 3, 10, 20Vnos: arr[] = {50, 3, 10, 7, 40, 80}
Izhod: 4
Pojasnilo: Najdaljše naraščajoče podzaporedje je {3, 7, 40, 80}
Vnos: arr[] = {30, 20, 10}
Izhod: 1
Pojasnilo: Najdaljša naraščajoča podzaporedja so {30}, {20} in (10)Vnos: arr[] = {10, 20, 35, 80}
Izhod: 4
Pojasnilo: Celoten niz je razvrščen
Uporaba najdaljšega naraščajočega zaporedja Rekurzija :
Zamisel, da bi prečkali vhodno matriko od leve proti desni in našli dolžino najdaljšega naraščajočega podzaporedja (LIS), ki se konča z vsakim elementom arr[i]. Naj bo ugotovljena dolžina arr[i] L[i]. Na koncu vrnemo največje število vseh vrednosti L[i]. Zdaj se pojavi vprašanje, kako izračunamo L[i]? Za to uporabimo rekurzijo, upoštevamo vse manjše elemente na levi strani arr[i], rekurzivno izračunamo vrednost LIS za vse manjše elemente na levi, vzamemo največje število vseh in mu dodamo 1. Če na levi strani elementa ni manjšega elementa, vrnemo 1.
java replaceall
Pustiti L(i) dolžina LIS, ki se konča z indeksom jaz tako da je arr[i] zadnji element LIS. Potem lahko L(i) rekurzivno zapišemo kot:
- L(i) = 1 + max(L(j) ), kjer je 0
- L(i) = 1, če tak j ne obstaja.
Formalno dolžina LIS, ki se konča z indeksom jaz , je za 1 večja od največje možne dolžine vseh LIS, ki se končajo pri nekem indeksu j tako da arr[j]
kje j .
Vidimo lahko, da zgornja relacija ponavljanja sledi optimalno podkonstrukcijo premoženje.
Ilustracija:
Sledite spodnji sliki za boljše razumevanje:
Razmislite o arr[] = {3, 10, 2, 11}
L(i): Označuje LIS podmatrike, ki se konča na položaju 'i'
Rekurzijsko drevo
Za uresničitev zgornje ideje sledite spodnjim korakom:
- Ustvarite rekurzivno funkcijo.
- Za vsak rekurziven klic ponovite od i = 1 na trenutni položaj in naredite naslednje:
- Poiščite možno dolžino najdaljšega naraščajočega podzaporedja, ki se konča na trenutnem položaju, če se je prejšnje zaporedje končalo pri jaz .
- Ustrezno posodobite največjo možno dolžino.
- To ponovite za vse indekse in poiščite odgovor
Spodaj je implementacija rekurzivnega pristopa:
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; } // Primerjaj max_ending_here s // skupnim maks. In po potrebi posodobite // skupno najvišjo vrednost, če (*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; } // Primerjaj max_ending_here s celotnim // max. In po potrebi posodobite skupno največjo vrednost, če (*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; } // Primerjaj max_ending_here s skupnim maks. In // po potrebi posodobi skupni maksimum, če (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>
Python # 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 # Primerjaj maxEndingHere s skupnim maksimumom. In # po potrebi posodobi skupni maksimum maximum = max(maximum, maxEndingHere) return maxEndingHere def lis(arr): # Omogočiti dostop globalne spremenljivke global maximum # Dolžina arr n = len(arr) # Največja spremenljivka vsebuje rezultat maksimum = 1 # Funkcija _lis() shrani svoj rezultat v maksimum _lis(arr, n) vrne maksimum # Program gonilnika za testiranje zgornje funkcije, če __name__ == '__main__': arr = [10, 22, 9, 33 , 21, 50, 41, 60] n = len(arr) # Klic funkcije print('Dolžina lis je', lis(arr)) # To kodo je prispeval 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; } // Primerjaj max_ending_here s skupnim maksimumom // in po potrebi posodobi skupni maksimum, če (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 >
Izhod
Length of lis is 5>
Časovna zapletenost: O(2n) Časovna kompleksnost tega rekurzivnega pristopa je eksponentna, saj obstaja primer prekrivanja podproblemov, kot je razloženo v zgornjem rekurzivnem drevesnem diagramu.
Pomožni prostor: O(1). Zunanji prostor se ne uporablja za shranjevanje vrednosti razen notranjega prostora sklada.
Uporaba najdaljšega naraščajočega podzaporedja Memoizacija :
Če natančno opazimo, lahko vidimo, da zgornja rekurzivna rešitev sledi tudi prekrivajočih se podproblemov lastnost, tj. ista podstruktura, ki se vedno znova rešuje v različnih rekurzijskih klicnih poteh. Temu se lahko izognemo s pristopom memoizacije.
Vidimo lahko, da je vsako stanje mogoče enolično identificirati z uporabo dveh parametrov:
- Trenutni indeks (označuje zadnji indeks LIS) in
- Prejšnje kazalo (označuje končni indeks prejšnjega LIS, za katerim je pride[i] se združuje).
Spodaj je implementacija zgornjega pristopa.
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 take = 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(take, notTake); } // Funkcija za iskanje dolžine // najdaljšega naraščajočega podzaporedja int longestSubsequence(int n, int a[]) { vektor> dp(n + 1, vektor (n + 1, -1)); vrni f(0, -1, n, a, dp); } // Program gonilnika za testiranje zgoraj function int main() { int a[] = { 3, 10, 2, 1, 20 }; int n = sizeof(a) / sizeof(a[0]); // Klic funkcije 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]) { take = 1 + f(idx + 1, idx, n, a, dp); } return dp[idx][prev_idx + 1] = Math.max(take, notTake); } // Ovojna funkcija za _lis() static int lis(int arr[], int n) { // Funkcija _lis() shrani svoj rezultat v max int dp[][] = new int[n + 1][ n + 1]; for (int row[] : dp) Arrays.fill(row, -1); vrni f(0, -1, n, arr, dp); } // Program gonilnika za testiranje zgornjih funkcij public static void main(String args[]) { int a[] = { 3, 10, 2, 1, 20 }; int n = a.length; // Klic funkcije System.out.println('Dolžina lis je ' + lis(a, n)); } } // To kodo je prispeval Sanskar.>
Python # 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] # Funkcija za iskanje dolžine najdaljšega naraščajočega # podzaporedja. def najdaljša podzaporedje(n, a): dp = [[-1 za i v obsegu (n + 1)]za j v obsegu (n + 1)] return f(0, -1, n, a, dp) # Gonilnik program za testiranje zgornje funkcije if __name__ == '__main__': a = [3, 10, 2, 1, 20] n = len(a) # Klic funkcije print('Dolžina lis je', longestSubsequence( n, a)) # To kodo je prispeval 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]) { take = 1 + f(idx + 1, idx, n, a, dp); } return dp[idx, prev_idx + 1] = Math.Max(take, notTake); } // Funkcija za iskanje dolžine najdaljšega naraščajočega // podzaporedja. public static int najdaljše podzaporedje(int n, int[] a) { int[, ] dp = novo int[n + 1, n + 1]; za (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]) { take = 1 + f(idx + 1, idx, n, a, dp); } return (dp[idx][prev_idx + 1] = Math.max(take, notTake)); } // Funkcija za iskanje dolžine najdaljšega naraščajočega // podzaporedja. funkcija longestSubsequence(n, a) { var dp = Array(n + 1) .fill() .map(() => Array(n + 1).fill(-1)); vrni f(0, -1, n, a, dp); } /* Program gonilnika za testiranje zgornje funkcije */ var a = [3, 10, 2, 1, 20]; spremen. n = 5; console.log('Dolžina lis je ' + najdaljše podzaporedje(n, a)); // To kodo je prispeval satwiksuman.>
Izhod
Length of lis is 3>
Časovna zapletenost: O(N2)
Pomožni prostor: O(N2)
Uporaba najdaljšega naraščajočega podzaporedja Dinamično programiranje :
Zaradi optimalne podstrukture in lastnosti podproblema, ki se prekriva, lahko za rešitev problema uporabimo tudi dinamično programiranje. Namesto memoizacije lahko uporabimo ugnezdeno zanko za implementacijo rekurzivne relacije.
Zunanja zanka bo potekala od i = 1 do N in notranja zanka bo potekala od j = 0 do i in za rešitev problema uporabite relacijo ponavljanja.
Spodaj je izvedba zgornjega pristopa:
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; }>
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]>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>
Python # 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] in 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 >
Izhod
Length of lis is 5>
Časovna zapletenost: O(N2) Uporablja se kot ugnezdena zanka.
Pomožni prostor: O(N) Uporaba katere koli matrike za shranjevanje vrednosti LIS pri vsakem indeksu.
Opomba: Časovna kompleksnost zgornje rešitve dinamičnega programiranja (DP) je O(n^2), vendar obstaja O(N* logN) rešitev za problem LIS. Tukaj nismo razpravljali o rešitvi O(N log N).
glej: Najdaljša rastoča velikost podzaporedja (N * logN) za omenjeni pristop.