Dada una array ordenada [0.. n-1] de claves de búsqueda y una array freq[0.. n-1] de conteos de frecuencia, donde freq[i] es el número de búsquedas de claves[i] . Construya un árbol de búsqueda binaria de todas las claves de modo que el costo total de todas las búsquedas sea lo más pequeño posible.
Primero definamos el costo de un BST. El costo de un Node BST es el nivel de ese Node multiplicado por su frecuencia. El nivel de la raíz es 1.
Ejemplos:
Input: keys[] = {10, 12}, freq[] = {34, 50} There can be following two possible BSTs 10 12 \ / 12 10 I II Frequency of searches of 10 and 12 are 34 and 50 respectively. The cost of tree I is 34*1 + 50*2 = 134 The cost of tree II is 50*1 + 34*2 = 118 Input: keys[] = {10, 12, 20}, freq[] = {34, 8, 50} There can be following possible BSTs 10 12 20 10 20 \ / \ / \ / 12 10 20 12 20 10 \ / / \ 20 10 12 12 I II III IV V Among all possible BSTs, cost of the fifth BST is minimum. Cost of the fifth BST is 1*50 + 2*34 + 3*8 = 142
1) Subestructura óptima:
el costo óptimo para freq[i..j] se puede calcular recursivamente usando la siguiente fórmula.
Necesitamos calcular optCost(0, n-1) para encontrar el resultado.
La idea de la fórmula anterior es simple, uno por uno probamos todos los Nodes como raíz (r varía de i a j en el segundo término). Cuando hacemos que el Node r sea raíz, calculamos recursivamente el costo óptimo de i a r-1 y de r+1 a j.
Agregamos la suma de las frecuencias de i a j (ver el primer término en la fórmula anterior)
La razón para sumar la suma de frecuencias de i a j:
Esto se puede dividir en 2 partes, una es la frecuencia [r] + la suma de las frecuencias de todos los elementos de i a j excepto r. El término freq[r] se agrega porque va a ser root y eso significa nivel de 1, entonces freq[r]*1=freq[r]. Ahora viene la parte real, estamos sumando las frecuencias de los elementos restantes porque cuando tomamos r como raíz, todos los elementos que no sean ese van 1 nivel por debajo del calculado en el subproblema. Permítanme decirlo de una manera más clara, para calcular optcost(i,j) asumimos que r se toma como raíz y calculamos el mínimo de opt(i,r-1)+opt(r+1,j) para todos i<=r<=j. Aquí, para cada subproblema, estamos eligiendo un Node como raíz. Pero en realidad el nivel de la raíz del subproblema y todos sus Nodes descendientes serán 1 mayor que el nivel de la raíz del problema principal.
2) Subproblemas superpuestos
A continuación se presenta una implementación recursiva que simplemente sigue la estructura recursiva mencionada anteriormente.
C++
// A naive recursive implementation of // optimal binary search tree problem #include <bits/stdc++.h> using namespace std; // A utility function to get sum of // array elements freq[i] to freq[j] int sum(int freq[], int i, int j); // A recursive function to calculate // cost of optimal binary search tree int optCost(int freq[], int i, int j) { // Base cases if (j < i) // no elements in this subarray return 0; if (j == i) // one element in this subarray return freq[i]; // Get sum of freq[i], freq[i+1], ... freq[j] int fsum = sum(freq, i, j); // Initialize minimum value int min = INT_MAX; // One by one consider all elements // as root and recursively find cost // of the BST, compare the cost with // min and update min if needed for (int r = i; r <= j; ++r) { int cost = optCost(freq, i, r - 1) + optCost(freq, r + 1, j); if (cost < min) min = cost; } // Return minimum value return min + fsum; } // The main function that calculates // minimum cost of a Binary Search Tree. // It mainly uses optCost() to find // the optimal cost. int optimalSearchTree(int keys[], int freq[], int n) { // Here array keys[] is assumed to be // sorted in increasing order. If keys[] // is not sorted, then add code to sort // keys, and rearrange freq[] accordingly. return optCost(freq, 0, n - 1); } // A utility function to get sum of // array elements freq[i] to freq[j] int sum(int freq[], int i, int j) { int s = 0; for (int k = i; k <= j; k++) s += freq[k]; return s; } // Driver Code int main() { int keys[] = {10, 12, 20}; int freq[] = {34, 8, 50}; int n = sizeof(keys) / sizeof(keys[0]); cout << "Cost of Optimal BST is " << optimalSearchTree(keys, freq, n); return 0; } // This is code is contributed // by rathbhupendra
C
// A naive recursive implementation of optimal binary // search tree problem #include <stdio.h> #include <limits.h> // A utility function to get sum of array elements // freq[i] to freq[j] int sum(int freq[], int i, int j); // A recursive function to calculate cost of optimal // binary search tree int optCost(int freq[], int i, int j) { // Base cases if (j < i) // no elements in this subarray return 0; if (j == i) // one element in this subarray return freq[i]; // Get sum of freq[i], freq[i+1], ... freq[j] int fsum = sum(freq, i, j); // Initialize minimum value int min = INT_MAX; // One by one consider all elements as root and // recursively find cost of the BST, compare the // cost with min and update min if needed for (int r = i; r <= j; ++r) { int cost = optCost(freq, i, r-1) + optCost(freq, r+1, j); if (cost < min) min = cost; } // Return minimum value return min + fsum; } // The main function that calculates minimum cost of // a Binary Search Tree. It mainly uses optCost() to // find the optimal cost. int optimalSearchTree(int keys[], int freq[], int n) { // Here array keys[] is assumed to be sorted in // increasing order. If keys[] is not sorted, then // add code to sort keys, and rearrange freq[] // accordingly. return optCost(freq, 0, n-1); } // A utility function to get sum of array elements // freq[i] to freq[j] int sum(int freq[], int i, int j) { int s = 0; for (int k = i; k <=j; k++) s += freq[k]; return s; } // Driver program to test above functions int main() { int keys[] = {10, 12, 20}; int freq[] = {34, 8, 50}; int n = sizeof(keys)/sizeof(keys[0]); printf("Cost of Optimal BST is %d ", optimalSearchTree(keys, freq, n)); return 0; }
Java
// A naive recursive implementation of optimal binary // search tree problem public class GFG { // A recursive function to calculate cost of // optimal binary search tree static int optCost(int freq[], int i, int j) { // Base cases if (j < i) // no elements in this subarray return 0; if (j == i) // one element in this subarray return freq[i]; // Get sum of freq[i], freq[i+1], ... freq[j] int fsum = sum(freq, i, j); // Initialize minimum value int min = Integer.MAX_VALUE; // One by one consider all elements as root and // recursively find cost of the BST, compare the // cost with min and update min if needed for (int r = i; r <= j; ++r) { int cost = optCost(freq, i, r-1) + optCost(freq, r+1, j); if (cost < min) min = cost; } // Return minimum value return min + fsum; } // The main function that calculates minimum cost of // a Binary Search Tree. It mainly uses optCost() to // find the optimal cost. static int optimalSearchTree(int keys[], int freq[], int n) { // Here array keys[] is assumed to be sorted in // increasing order. If keys[] is not sorted, then // add code to sort keys, and rearrange freq[] // accordingly. return optCost(freq, 0, n-1); } // A utility function to get sum of array elements // freq[i] to freq[j] static int sum(int freq[], int i, int j) { int s = 0; for (int k = i; k <=j; k++) s += freq[k]; return s; } // Driver code public static void main(String[] args) { int keys[] = {10, 12, 20}; int freq[] = {34, 8, 50}; int n = keys.length; System.out.println("Cost of Optimal BST is " + optimalSearchTree(keys, freq, n)); } } // This code is contributed by Sumit Ghosh
Python3
# A naive recursive implementation of # optimal binary search tree problem # A recursive function to calculate # cost of optimal binary search tree def optCost(freq, i, j): # Base cases if j < i: # no elements in this subarray return 0 if j == i: # one element in this subarray return freq[i] # Get sum of freq[i], freq[i+1], ... freq[j] fsum = Sum(freq, i, j) # Initialize minimum value Min = 999999999999 # One by one consider all elements as # root and recursively find cost of # the BST, compare the cost with min # and update min if needed for r in range(i, j + 1): cost = (optCost(freq, i, r - 1) + optCost(freq, r + 1, j)) if cost < Min: Min = cost # Return minimum value return Min + fsum # The main function that calculates minimum # cost of a Binary Search Tree. It mainly # uses optCost() to find the optimal cost. def optimalSearchTree(keys, freq, n): # Here array keys[] is assumed to be # sorted in increasing order. If keys[] # is not sorted, then add code to sort # keys, and rearrange freq[] accordingly. return optCost(freq, 0, n - 1) # A utility function to get sum of # array elements freq[i] to freq[j] def Sum(freq, i, j): s = 0 for k in range(i, j + 1): s += freq[k] return s # Driver Code if __name__ == '__main__': keys = [10, 12, 20] freq = [34, 8, 50] n = len(keys) print("Cost of Optimal BST is", optimalSearchTree(keys, freq, n)) # This code is contributed by PranchalK
C#
// A naive recursive implementation of optimal binary // search tree problem using System; class GFG { // A recursive function to calculate cost of // optimal binary search tree static int optCost(int []freq, int i, int j) { // Base cases // no elements in this subarray if (j < i) return 0; // one element in this subarray if (j == i) return freq[i]; // Get sum of freq[i], freq[i+1], ... freq[j] int fsum = sum(freq, i, j); // Initialize minimum value int min = int.MaxValue; // One by one consider all elements as root and // recursively find cost of the BST, compare the // cost with min and update min if needed for (int r = i; r <= j; ++r) { int cost = optCost(freq, i, r-1) + optCost(freq, r+1, j); if (cost < min) min = cost; } // Return minimum value return min + fsum; } // The main function that calculates minimum cost of // a Binary Search Tree. It mainly uses optCost() to // find the optimal cost. static int optimalSearchTree(int []keys, int []freq, int n) { // Here array keys[] is assumed to be sorted in // increasing order. If keys[] is not sorted, then // add code to sort keys, and rearrange freq[] // accordingly. return optCost(freq, 0, n-1); } // A utility function to get sum of array elements // freq[i] to freq[j] static int sum(int []freq, int i, int j) { int s = 0; for (int k = i; k <=j; k++) s += freq[k]; return s; } // Driver code public static void Main() { int []keys = {10, 12, 20}; int []freq = {34, 8, 50}; int n = keys.Length; Console.Write("Cost of Optimal BST is " + optimalSearchTree(keys, freq, n)); } } // This code is contributed by Sam007
Javascript
<script> //Javascript Implementation // A recursive function to calculate // cost of optimal binary search tree function optCost(freq, i, j) { // Base cases if (j < i) // no elements in this subarray return 0; if (j == i) // one element in this subarray return freq[i]; // Get sum of freq[i], freq[i+1], ... freq[j] var fsum = sum(freq, i, j); // Initialize minimum value var min = Number. MAX_SAFE_INTEGER; // One by one consider all elements // as root and recursively find cost // of the BST, compare the cost with // min and update min if needed for (var r = i; r <= j; ++r) { var cost = optCost(freq, i, r - 1) + optCost(freq, r + 1, j); if (cost < min) min = cost; } // Return minimum value return min + fsum; } // The main function that calculates // minimum cost of a Binary Search Tree. // It mainly uses optCost() to find // the optimal cost. function optimalSearchTree(keys, freq, n) { // Here array keys[] is assumed to be // sorted in increasing order. If keys[] // is not sorted, then add code to sort // keys, and rearrange freq[] accordingly. return optCost(freq, 0, n - 1); } // A utility function to get sum of // array elements freq[i] to freq[j] function sum(freq, i, j) { var s = 0; for (var k = i; k <= j; k++) s += freq[k]; return s; } // Driver Code var keys = [10, 12, 20]; var freq = [34, 8, 50]; var n = keys.length; document.write("Cost of Optimal BST is " + optimalSearchTree(keys, freq, n)); // This code is contributed by shubhamsingh10 </script>
Producción:
Cost of Optimal BST is 142
La complejidad temporal del enfoque recursivo ingenuo anterior es exponencial. Cabe señalar que la función anterior calcula los mismos subproblemas una y otra vez. Podemos ver muchos subproblemas que se repiten en el siguiente árbol de recursión para freq[1..4].
Dado que los mismos subproblemas se vuelven a llamar, este problema tiene la propiedad Superposición de subproblemas. Entonces, el problema BST óptimo tiene ambas propiedades (ver this y this ) de un problema de programación dinámica. Al igual que otros problemas típicos de programación dinámica (DP), los cálculos de los mismos subproblemas se pueden evitar mediante la construcción de una array temporal cost[][] de forma ascendente.
Solución de programación dinámica
A continuación se muestra la implementación de C/C++ para un problema de BST óptimo mediante la programación dinámica. Usamos una array auxiliar cost[n][n] para almacenar las soluciones de los subproblemas. cost[0][n-1] mantendrá el resultado final. El desafío en la implementación es que todos los valores de la diagonal deben completarse primero, luego los valores que se encuentran en la línea justo encima de la diagonal. En otras palabras, primero debemos llenar todos los valores de costo[i][i], luego todos los valores de costo[i][i+1], luego todos los valores de costo[i][i+2]. Entonces, ¿cómo llenar la array 2D de esa manera? La idea utilizada en la implementación es la misma que el problema de multiplicación de strings de array , usamos una variable ‘L’ para la longitud de la string e incrementamos ‘L’, uno por uno. Calculamos el número de columna ‘j’ usando los valores de ‘i’ y ‘L’.
C++
// Dynamic Programming code for Optimal Binary Search // Tree Problem #include <bits/stdc++.h> using namespace std; // A utility function to get sum of array elements // freq[i] to freq[j] int sum(int freq[], int i, int j); /* A Dynamic Programming based function that calculates minimum cost of a Binary Search Tree. */ int optimalSearchTree(int keys[], int freq[], int n) { /* Create an auxiliary 2D matrix to store results of subproblems */ int cost[n][n]; /* cost[i][j] = Optimal cost of binary search tree that can be formed from keys[i] to keys[j]. cost[0][n-1] will store the resultant cost */ // For a single key, cost is equal to frequency of the key for (int i = 0; i < n; i++) cost[i][i] = freq[i]; // Now we need to consider chains of length 2, 3, ... . // L is chain length. for (int L = 2; L <= n; L++) { // i is row number in cost[][] for (int i = 0; i <= n-L+1; i++) { // Get column number j from row number i and // chain length L int j = i+L-1; cost[i][j] = INT_MAX; // Try making all keys in interval keys[i..j] as root for (int r = i; r <= j; r++) { // c = cost when keys[r] becomes root of this subtree int c = ((r > i)? cost[i][r-1]:0) + ((r < j)? cost[r+1][j]:0) + sum(freq, i, j); if (c < cost[i][j]) cost[i][j] = c; } } } return cost[0][n-1]; } // A utility function to get sum of array elements // freq[i] to freq[j] int sum(int freq[], int i, int j) { int s = 0; for (int k = i; k <= j; k++) s += freq[k]; return s; } // Driver code int main() { int keys[] = {10, 12, 20}; int freq[] = {34, 8, 50}; int n = sizeof(keys)/sizeof(keys[0]); cout << "Cost of Optimal BST is " << optimalSearchTree(keys, freq, n); return 0; } // This code is contributed by rathbhupendra
C
// Dynamic Programming code for Optimal Binary Search // Tree Problem #include <stdio.h> #include <limits.h> // A utility function to get sum of array elements // freq[i] to freq[j] int sum(int freq[], int i, int j); /* A Dynamic Programming based function that calculates minimum cost of a Binary Search Tree. */ int optimalSearchTree(int keys[], int freq[], int n) { /* Create an auxiliary 2D matrix to store results of subproblems */ int cost[n][n]; /* cost[i][j] = Optimal cost of binary search tree that can be formed from keys[i] to keys[j]. cost[0][n-1] will store the resultant cost */ // For a single key, cost is equal to frequency of the key for (int i = 0; i < n; i++) cost[i][i] = freq[i]; // Now we need to consider chains of length 2, 3, ... . // L is chain length. for (int L=2; L<=n; L++) { // i is row number in cost[][] for (int i=0; i<=n-L+1; i++) { // Get column number j from row number i and // chain length L int j = i+L-1; cost[i][j] = INT_MAX; // Try making all keys in interval keys[i..j] as root for (int r=i; r<=j; r++) { // c = cost when keys[r] becomes root of this subtree int c = ((r > i)? cost[i][r-1]:0) + ((r < j)? cost[r+1][j]:0) + sum(freq, i, j); if (c < cost[i][j]) cost[i][j] = c; } } } return cost[0][n-1]; } // A utility function to get sum of array elements // freq[i] to freq[j] int sum(int freq[], int i, int j) { int s = 0; for (int k = i; k <=j; k++) s += freq[k]; return s; } // Driver program to test above functions int main() { int keys[] = {10, 12, 20}; int freq[] = {34, 8, 50}; int n = sizeof(keys)/sizeof(keys[0]); printf("Cost of Optimal BST is %d ", optimalSearchTree(keys, freq, n)); return 0; }
Java
// Dynamic Programming Java code for Optimal Binary Search // Tree Problem public class Optimal_BST2 { /* A Dynamic Programming based function that calculates minimum cost of a Binary Search Tree. */ static int optimalSearchTree(int keys[], int freq[], int n) { /* Create an auxiliary 2D matrix to store results of subproblems */ int cost[][] = new int[n + 1][n + 1]; /* cost[i][j] = Optimal cost of binary search tree that can be formed from keys[i] to keys[j]. cost[0][n-1] will store the resultant cost */ // For a single key, cost is equal to frequency of the key for (int i = 0; i < n; i++) cost[i][i] = freq[i]; // Now we need to consider chains of length 2, 3, ... . // L is chain length. for (int L = 2; L <= n; L++) { // i is row number in cost[][] for (int i = 0; i <= n - L + 1; i++) { // Get column number j from row number i and // chain length L int j = i + L - 1; cost[i][j] = Integer.MAX_VALUE; // Try making all keys in interval keys[i..j] as root for (int r = i; r <= j; r++) { // c = cost when keys[r] becomes root of this subtree int c = ((r > i) ? cost[i][r - 1] : 0) + ((r < j) ? cost[r + 1][j] : 0) + sum(freq, i, j); if (c < cost[i][j]) cost[i][j] = c; } } } return cost[0][n - 1]; } // A utility function to get sum of array elements // freq[i] to freq[j] static int sum(int freq[], int i, int j) { int s = 0; for (int k = i; k <= j; k++) { if (k >= freq.length) continue; s += freq[k]; } return s; } public static void main(String[] args) { int keys[] = { 10, 12, 20 }; int freq[] = { 34, 8, 50 }; int n = keys.length; System.out.println("Cost of Optimal BST is " + optimalSearchTree(keys, freq, n)); } } //This code is contributed by Sumit Ghosh
Python3
# Dynamic Programming code for Optimal Binary Search # Tree Problem INT_MAX = 2147483647 """ A Dynamic Programming based function that calculates minimum cost of a Binary Search Tree. """ def optimalSearchTree(keys, freq, n): """ Create an auxiliary 2D matrix to store results of subproblems """ cost = [[0 for x in range(n)] for y in range(n)] """ cost[i][j] = Optimal cost of binary search tree that can be formed from keys[i] to keys[j]. cost[0][n-1] will store the resultant cost """ # For a single key, cost is equal to # frequency of the key for i in range(n): cost[i][i] = freq[i] # Now we need to consider chains of # length 2, 3, ... . L is chain length. for L in range(2, n + 1): # i is row number in cost for i in range(n - L + 2): # Get column number j from row number # i and chain length L j = i + L - 1 if i >= n or j >= n: break cost[i][j] = INT_MAX # Try making all keys in interval # keys[i..j] as root for r in range(i, j + 1): # c = cost when keys[r] becomes root # of this subtree c = 0 if (r > i): c += cost[i][r - 1] if (r < j): c += cost[r + 1][j] c += sum(freq, i, j) if (c < cost[i][j]): cost[i][j] = c return cost[0][n - 1] # A utility function to get sum of # array elements freq[i] to freq[j] def sum(freq, i, j): s = 0 for k in range(i, j + 1): s += freq[k] return s # Driver Code if __name__ == '__main__': keys = [10, 12, 20] freq = [34, 8, 50] n = len(keys) print("Cost of Optimal BST is", optimalSearchTree(keys, freq, n)) # This code is contributed by SHUBHAMSINGH10
C#
// Dynamic Programming C# code for Optimal Binary Search // Tree Problem using System; class GFG { /* A Dynamic Programming based function that calculates minimum cost of a Binary Search Tree. */ static int optimalSearchTree(int []keys, int []freq, int n) { /* Create an auxiliary 2D matrix to store results of subproblems */ int [,]cost = new int[n + 1,n + 1]; /* cost[i][j] = Optimal cost of binary search tree that can be formed from keys[i] to keys[j]. cost[0][n-1] will store the resultant cost */ // For a single key, cost is equal to frequency of the key for (int i = 0; i < n; i++) cost[i,i] = freq[i]; // Now we need to consider chains of length 2, 3, ... . // L is chain length. for (int L = 2; L <= n; L++) { // i is row number in cost[][] for (int i = 0; i <= n - L + 1; i++) { // Get column number j from row number i and // chain length L int j = i + L - 1; cost[i,j] = int.MaxValue; // Try making all keys in interval keys[i..j] as root for (int r = i; r <= j; r++) { // c = cost when keys[r] becomes root of this subtree int c = ((r > i) ? cost[i,r - 1] : 0) + ((r < j) ? cost[r + 1,j] : 0) + sum(freq, i, j); if (c < cost[i,j]) cost[i,j] = c; } } } return cost[0,n - 1]; } // A utility function to get sum of array elements // freq[i] to freq[j] static int sum(int []freq, int i, int j) { int s = 0; for (int k = i; k <= j; k++) { if (k >= freq.Length) continue; s += freq[k]; } return s; } public static void Main() { int []keys = { 10, 12, 20 }; int []freq = { 34, 8, 50 }; int n = keys.Length; Console.Write("Cost of Optimal BST is " + optimalSearchTree(keys, freq, n)); } } // This code is contributed by Sam007
Javascript
<script> // Dynamic Programming code for Optimal Binary Search // Tree Problem /* A Dynamic Programming based function that calculates minimum cost of a Binary Search Tree. */ function optimalSearchTree(keys, freq, n) { /* Create an auxiliary 2D matrix to store results of subproblems */ var cost = new Array(n); for (var i = 0; i < n; i++) cost[i] = new Array(n); /* cost[i][j] = Optimal cost of binary search tree that can be formed from keys[i] to keys[j]. cost[0][n-1] will store the resultant cost */ // For a single key, cost is equal to frequency of the key for (var i = 0; i < n; i++) cost[i][i] = freq[i]; // Now we need to consider chains of length 2, 3, ... . // L is chain length. for (var L = 2; L <= n; L++) { // i is row number in cost[][] for (var i = 0; i <= n-L+1; i++) { // Get column number j from row number i and // chain length L var j = i+L-1; if ( i >= n || j >= n) break cost[i][j] = Number. MAX_SAFE_INTEGER; // Try making all keys in interval keys[i..j] as root for (var r = i; r <= j; r++) { // c = cost when keys[r] becomes root of this subtree var c = 0; if (r > i) c += cost[i][r-1] if (r < j) c += cost[r+1][j] c += sum(freq, i, j); if (c < cost[i][j]) cost[i][j] = c; } } } return cost[0][n-1]; } // A utility function to get sum of array elements // freq[i] to freq[j] function sum(freq, i, j) { var s = 0; for (var k = i; k <= j; k++) s += freq[k]; return s; } var keys = [10, 12, 20]; var freq = [34, 8, 50]; var n = keys.length; document.write("Cost of Optimal BST is " + optimalSearchTree(keys, freq, n)); // This code contributed by shubhamsingh10 </script>
Producción:
Cost of Optimal BST is 142
Notas
1) La complejidad temporal de la solución anterior es O(n^4). La complejidad del tiempo se puede reducir fácilmente a O(n^3) calculando previamente la suma de frecuencias en lugar de llamar a sum() una y otra vez.
2) En las soluciones anteriores, solo hemos calculado el costo óptimo. Las soluciones también se pueden modificar fácilmente para almacenar la estructura de los BST. Podemos crear otra array auxiliar de tamaño n para almacenar la estructura del árbol. Todo lo que tenemos que hacer es almacenar la ‘r’ elegida en el ciclo más interno.
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.
Publicación traducida automáticamente
Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA