Дано масив обр[] розміру Н , завдання полягає в тому, щоб знайти довжину найдовшої зростаючої підпослідовності (LIS), тобто найдовшої можливої підпослідовності, в якій елементи підпослідовності відсортовані в порядку зростання.

Найдовша зростаюча підпослідовність
приклади:
введення: arr[] = {3, 10, 2, 1, 20}
Вихід: 3
Пояснення: Найдовша зростаюча підпослідовність – 3, 10, 20введення: arr[] = {50, 3, 10, 7, 40, 80}
Вихід: 4
Пояснення: Найдовша зростаюча підпослідовність: {3, 7, 40, 80}
введення: arr[] = {30, 20, 10}
Вихід: 1
Пояснення: Найдовшими зростаючими підпослідовностями є {30}, {20} і (10)введення: arr[] = {10, 20, 35, 80}
Вихід: 4
Пояснення: Весь масив відсортованийnp.нулики
Використання найдовшої зростаючої послідовності Рекурсія :
Ідея пройти вхідний масив зліва направо та знайти довжину найдовшої зростаючої підпослідовності (LIS), що закінчується кожним елементом arr[i]. Нехай знайдена довжина arr[i] дорівнює L[i]. Наприкінці ми повертаємо максимум усіх значень L[i]. Тепер виникає питання, як ми обчислюємо L[i]? Для цього ми використовуємо рекурсію, розглядаємо всі менші елементи ліворуч від arr[i], рекурсивно обчислюємо значення LIS для всіх менших елементів ліворуч, беремо максимум із усіх і додаємо до нього 1. Якщо ліворуч від елемента немає меншого елемента, ми повертаємо 1.
Дозволяти L(i) бути довжиною LIS, що закінчується індексом i таким чином, що arr[i] є останнім елементом LIS. Тоді L(i) можна рекурсивно записати так:
- L(i) = 1 + max(L(j)), де 0
- L(i) = 1, якщо такого j не існує.
Формально, довжина LIS, що закінчується індексом i , на 1 більший за максимальну довжину всіх LIS, що закінчуються деяким індексом j такий як arr[j]
де j .
Ми бачимо, що наведене вище рекурентне співвідношення слідує за оптимальна підструктура власність.
Ілюстрація:
Для кращого розуміння дотримуйтесь ілюстрації нижче:
Розглянемо arr[] = {3, 10, 2, 11}
що таке mac osL(i): Позначає LIS підмасиву, що закінчується в позиції «i»
Дерево рекурсії
Щоб реалізувати наведену вище ідею, виконайте наведені нижче дії.
- Створіть рекурсивну функцію.
- Для кожного рекурсивного виклику виконайте ітерацію з i = 1 на поточну позицію та виконайте такі дії:
- Знайдіть можливу довжину найдовшої зростаючої підпослідовності, яка закінчується в поточній позиції, якщо попередня послідовність закінчувалася на i .
- Відповідно оновіть максимально можливу довжину.
- Повторіть це для всіх індексів і знайдіть відповідь
Нижче наведено реалізацію рекурсивного підходу:
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; } // Порівняти max_ending_here із // загальним макс. І оновіть // загальний максимум, якщо потрібно, якщо (*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; } // Порівняти max_ending_here із загальним // макс. І за потреби оновіть загальний максимум, якщо (*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; } // Порівняти max_ending_here із загальним макс. І // за потреби оновіть загальний максимум 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>
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 # Порівняти maxEndingHere із загальним максимумом. І # оновіть загальний максимум, якщо необхідно. max = max(maximum, maxEndingHere) return maxEndingHere def lis(arr): # Щоб дозволити доступ до глобальної змінної global maximum # Length of arr n = len(arr) # Максимальна змінна містить результат maximum = 1 # Функція _lis() зберігає свій результат у максимумі _lis(arr, n) повертає максимум # Програма драйвера для перевірки наведеної вище функції if __name__ == '__main__': arr = [10, 22, 9, 33 , 21, 50, 41, 60] n = len(arr) # Виклик функції print('Length of lis is', lis(arr)) # Цей код створено 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; } // Порівняйте max_ending_here із загальним максимумом // і оновіть загальний максимум, якщо потрібно, якщо (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 >
Вихід
Length of lis is 5>
Часова складність: O(2п) Часова складність цього рекурсивного підходу експоненціальна, оскільки існує випадок накладення підпроблем, як пояснено на рекурсивній деревоподібній діаграмі вище.
Допоміжний простір: O(1). Жоден зовнішній простір не використовується для зберігання значень, крім внутрішнього простору стека.
Використання найдовшої зростаючої підпослідовності Запам'ятовування :
Якщо уважно помітити, ми можемо побачити, що наведене вище рекурсивне рішення також слідує перекриваються підпроблеми властивість, тобто та сама підструктура, що розв’язується знову і знову в різних шляхах виклику рекурсії. Ми можемо уникнути цього за допомогою підходу запам’ятовування.
Ми бачимо, що кожен стан можна однозначно ідентифікувати за допомогою двох параметрів:
- Поточний індекс (позначає останній індекс ЛІС) і
- Попередній індекс (позначає кінцевий індекс попереднього LIS, за яким знаходиться arr[i] об’єднується).
Нижче наведено реалізацію вищеописаного підходу.
рядок містить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); } // Функція для визначення довжини // найдовшої зростаючої підпослідовності int longestSubsequence(int n, int a[]) { вектор> dp(n + 1, вектор (n + 1, -1)); return f(0, -1, n, a, dp); } // Програма драйвера для тестування вище function int main() { int a[] = { 3, 10, 2, 1, 20 }; int n = sizeof(a) / sizeof(a[0]); // Виклик функції 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); } // Функція-огортка для _lis() static int lis(int arr[], int n) { // Функція _lis() зберігає свій результат у max int dp[][] = new int[n + 1][ n + 1]; for (int row[] : dp) Arrays.fill(row, -1); return f(0, -1, n, arr, dp); } // Програма драйвера для перевірки вищезазначених функцій public static void main(String args[]) { int a[] = { 3, 10, 2, 1, 20 }; int n = a.length; // Виклик функції System.out.println('Довжина lis дорівнює ' + lis(a, n)); } } // Цей код створено 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] # Функція для знаходження довжини найдовшої зростаючої # підпослідовності. 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) # Драйвер програма для тестування наведеної вище функції, якщо __name__ == '__main__': a = [3, 10, 2, 1, 20] n = len(a) # Виклик функції print('Довжина lis є', longestSubsequence( n, a)) # Цей код надано 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); } // Функція пошуку довжини найдовшої зростаючої // підпослідовності. public static int longestSubsequence(int n, int[] a) { int[, ] dp = new int[n + 1, n + 1]; для (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)); } // Функція пошуку довжини найдовшої зростаючої // підпослідовності. функція longestSubsequence(n, a) { var dp = Array(n + 1) .fill() .map(() => Array(n + 1).fill(-1)); return f(0, -1, n, a, dp); } /* Програма драйвера для перевірки вищезазначеної функції */ var a = [3, 10, 2, 1, 20]; змінна n = 5; console.log('Довжина lis дорівнює ' + longestSubsequence(n, a)); // Цей код створено satwiksuman.>
Вихід
Length of lis is 3>
Часова складність: O(N2)
Допоміжний простір: O(N2)
Використання найдовшої зростаючої підпослідовності Динамічне програмування :
Завдяки оптимальній підструктурі та властивості підпроблеми, що перекривається, ми також можемо використовувати динамічне програмування для вирішення проблеми. Замість запам'ятовування ми можемо використовувати вкладений цикл для реалізації рекурсивного відношення.
Зовнішній цикл буде проходити з i = 1 до N і внутрішній цикл працюватиме з j = 0 до i і використайте рекурентне відношення для розв’язування задачі.
Нижче наведено реалізацію вищезазначеного підходу:
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] і 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 >
Вихід
Length of lis is 5>
Часова складність: O(N2) Використовується як вкладений цикл.
Допоміжний простір: O(N) Використання будь-якого масиву для зберігання значень LIS за кожним індексом.
Примітка: Часова складність наведеного вище рішення динамічного програмування (DP) становить O(n^2), але є Розв’язок O(N* logN). для проблеми ЛІС. Ми не обговорювали тут рішення O(N log N).
Зверніться до: Розмір найдовшої підпослідовності, що збільшується (N * logN) за згаданий підхід.