Given a sorted array keys[0.. n-1] of search keys and an array freq[0.. n-1] of frequency counts, where freq[i] is the number of searches for keys[i]. Construct a binary search tree of all keys such that the total cost of all the searches is as small as possible.
The cost of a BST node is the level of that node multiplied by its frequency. The level of the root is 1.
Examples:
Input: keys[] = [10, 12], freq[]= [34, 50] Output: 118 Explanation: There can be following two possible BSTs 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] Output: 142 Explanation: There can be many possible BSTs. Among all possible BSTs, cost of the fifth BST is 1*50 + 2*34 + 3*8 = 142 which is the minimum.
We would multiply each frequency by its tree level, but keeping track of levels makes the solution complicated.
Instead of that, we use a smarter approach. Whenever we choose a node as the root, we add the sum of the frequencies of all the nodes between the start and end index (i to j). After choosing the root, every node in that group (both left and right subtree) automatically shifts one level deeper. When recursion goes deeper and solves the smaller subtrees, their frequency sum is added again automatically, which has the exact same effect as multiplying each node by its level.
[Naive Approach] Using Recursion - O(2^n) Time and O(n) Space
As discussed in our intuition, we try each key as the root of the tree. For every root choice, we recursively compute the cost of its left and right subtrees. At each step, we add the sum of freq in that range to account for all keys moving one level deeper. We repeat this process for all possible roots and finally pick the configuration that gives the minimum total cost.
C++
//Driver Code Starts#include<iostream>#include<vector>usingnamespacestd;//Driver Code Ends//function to get sum of// array elements freq[i] to freq[j]intsum(vector<int>&freq,inti,intj){ints=0;for(intk=i;k<=j;k++)s+=freq[k];returns;}// A recursive function to calculate// cost of optimal binary search treeintoptCost(vector<int>&freq,inti,intj){// no elements in this subarrayif(j<i)return0;// one element in this subarrayif(j==i)returnfreq[i];// Get sum of freq[i], freq[i+1], ... freq[j]intfsum=sum(freq,i,j);intminCost=INT_MAX;// One by one consider all elements// as root and recursively find cost// of the BSTfor(intr=i;r<=j;++r){intcost=optCost(freq,i,r-1)+optCost(freq,r+1,j);if(cost<minCost)minCost=cost;}// Return minimum valuereturnminCost+fsum;}intminCost(vector<int>&keys,vector<int>&freq){intn=keys.size();returnoptCost(freq,0,n-1);}//Driver Code Startsintmain(){vector<int>keys={10,12,20};vector<int>freq={34,8,50};cout<<minCost(keys,freq);return0;}//Driver Code Ends
Java
//Driver Code Startsimportjava.util.Arrays;publicclassGFG{//Driver Code Ends// function to get sum of// array elements freq[i] to freq[j]staticintsum(int[]freq,inti,intj){ints=0;for(intk=i;k<=j;k++)s+=freq[k];returns;}// A recursive function to calculate// cost of optimal binary search treestaticintoptCost(int[]freq,inti,intj){// no elements in this subarrayif(j<i)return0;// one element in this subarrayif(j==i)returnfreq[i];// Get sum of freq[i], freq[i+1], ... freq[j]intfsum=sum(freq,i,j);intminCost=Integer.MAX_VALUE;// One by one consider all elements// as root and recursively find cost// of the BSTfor(intr=i;r<=j;r++){intcost=optCost(freq,i,r-1)+optCost(freq,r+1,j);if(cost<minCost)minCost=cost;}// Return minimum valuereturnminCost+fsum;}staticintminCost(int[]keys,int[]freq){intn=keys.length;returnoptCost(freq,0,n-1);}//Driver Code Startspublicstaticvoidmain(String[]args){int[]keys={10,12,20};int[]freq={34,8,50};System.out.println(minCost(keys,freq));}}//Driver Code Ends
Python
# function to get sum of# array elements freq[i] to freq[j]defsum(freq,i,j):s=0forkinrange(i,j+1):s+=freq[k]returns# A recursive function to calculate# cost of optimal binary search treedefoptCost(freq,i,j):# no elements in this subarrayifj<i:return0# one element in this subarrayifj==i:returnfreq[i]# Get sum of freq[i], freq[i+1], ... freq[j]fsum=sum(freq,i,j)minCost=float('inf')# One by one consider all elements# as root and recursively find cost# of the BSTforrinrange(i,j+1):cost=optCost(freq,i,r-1)+optCost(freq,r+1,j)ifcost<minCost:minCost=cost# Return minimum valuereturnminCost+fsumdefminCost(keys,freq):n=len(keys)returnoptCost(freq,0,n-1)#Driver Code Startsif__name__=='__main__':keys=[10,12,20]freq=[34,8,50]print(minCost(keys,freq))#Driver Code Ends
C#
//Driver Code StartsusingSystem;classGFG{//Driver Code Ends// function to get sum of// array elements freq[i] to freq[j]staticintsum(int[]freq,inti,intj){ints=0;for(intk=i;k<=j;k++)s+=freq[k];returns;}// A recursive function to calculate// cost of optimal binary search treestaticintoptCost(int[]freq,inti,intj){// no elements in this subarrayif(j<i)return0;// one element in this subarrayif(j==i)returnfreq[i];// Get sum of freq[i], freq[i+1], ... freq[j]intfsum=sum(freq,i,j);intminCost=int.MaxValue;// One by one consider all elements// as root and recursively find cost// of the BSTfor(intr=i;r<=j;r++){intcost=optCost(freq,i,r-1)+optCost(freq,r+1,j);if(cost<minCost)minCost=cost;}// Return minimum valuereturnminCost+fsum;}staticintminCost(int[]keys,int[]freq){intn=keys.Length;returnoptCost(freq,0,n-1);}//Driver Code StartsstaticvoidMain(){int[]keys={10,12,20};int[]freq={34,8,50};Console.WriteLine(minCost(keys,freq));}}//Driver Code Ends
JavaScript
// function to get sum of// array elements freq[i] to freq[j]functionsum(freq,i,j){lets=0;for(letk=i;k<=j;k++)s+=freq[k];returns;}// A recursive function to calculate// cost of optimal binary search treefunctionoptCost(freq,i,j){// no elements in this subarrayif(j<i)return0;// one element in this subarrayif(j===i)returnfreq[i];// Get sum of freq[i], freq[i+1], ... freq[j]letfsum=sum(freq,i,j);letminCost=Number.MAX_SAFE_INTEGER;// One by one consider all elements// as root and recursively find cost// of the BSTfor(letr=i;r<=j;r++){letcost=optCost(freq,i,r-1)+optCost(freq,r+1,j);if(cost<minCost)minCost=cost;}// Return minimum valuereturnminCost+fsum;}functionminCostKeys(keys,freq){letn=keys.length;returnoptCost(freq,0,n-1);}//Driver Code Starts//Driver Codeletkeys=[10,12,20];letfreq=[34,8,50];console.log(minCostKeys(keys,freq));//Driver Code Ends
Output
142
[Expected Approach 1] Using Top - Down Dp (Memoization) - O(n^3) Time and O(n^2) Space
If we look at the recursive approach, we notice that many subproblems are solved repeatedly, which leads to exponential time complexity. To avoid this, we use memoization.
We create a 2D DP array of size n x n to store the results of already solved subproblems. Whenever we encounter a subproblem, If the answer is already computed, we simply return it instead of recalculating.
C++
//Driver Code Starts#include<iostream>#include<vector>usingnamespacestd;//Driver Code Ends//function to get sum of// array elements freq[i] to freq[j]intsum(vector<int>&freq,inti,intj){ints=0;for(intk=i;k<=j;k++)s+=freq[k];returns;}// A recursive function to calculate// cost of optimal binary search treeintoptCost(vector<int>&freq,inti,intj,vector<vector<int>>&dp){// Base casesif(j<i)return0;if(j==i)returnfreq[i];if(dp[i][j]!=-1)returndp[i][j];// Get sum of freq[i], freq[i+1], ... freq[j]intfsum=sum(freq,i,j);intminCost=INT_MAX;// One by one consider all elements// as root and recursively find cost// of the BSTfor(intr=i;r<=j;++r){intcost=optCost(freq,i,r-1,dp)+optCost(freq,r+1,j,dp);if(cost<minCost)minCost=cost;}// Return minimum valuereturndp[i][j]=minCost+fsum;}// The main function that calculates// minimum cost of a Binary Search Tree.intminCost(vector<int>&keys,vector<int>&freq){intn=keys.size();vector<vector<int>>dp(n,vector<int>(n,-1));returnoptCost(freq,0,n-1,dp);}//Driver Code Startsintmain(){vector<int>keys={10,12,20};vector<int>freq={34,8,50};cout<<minCost(keys,freq);return0;}//Driver Code Ends
Java
//Driver Code Startsimportjava.util.Arrays;publicclassGFG{//Driver Code Ends//function to get sum of// array elements freq[i] to freq[j]staticintsum(int[]freq,inti,intj){ints=0;for(intk=i;k<=j;k++)s+=freq[k];returns;}// A recursive function to calculate// cost of optimal binary search treestaticintoptCost(int[]freq,inti,intj,int[][]dp){// Base casesif(j<i)return0;if(j==i)returnfreq[i];if(dp[i][j]!=-1)returndp[i][j];// Get sum of freq[i], freq[i+1], ... freq[j]intfsum=sum(freq,i,j);intminCost=Integer.MAX_VALUE;// One by one consider all elements// as root and recursively find cost// of the BSTfor(intr=i;r<=j;r++){intcost=optCost(freq,i,r-1,dp)+optCost(freq,r+1,j,dp);if(cost<minCost)minCost=cost;}// Return minimum valuereturndp[i][j]=minCost+fsum;}// The main function that calculates// minimum cost of a Binary Search Tree.staticintminCost(int[]keys,int[]freq){intn=keys.length;int[][]dp=newint[n][n];for(int[]row:dp)Arrays.fill(row,-1);returnoptCost(freq,0,n-1,dp);}//Driver Code Startspublicstaticvoidmain(String[]args){int[]keys={10,12,20};int[]freq={34,8,50};System.out.println(minCost(keys,freq));}}//Driver Code Ends
Python
#function to get sum of# array elements freq[i] to freq[j]defsum(freq,i,j):s=0forkinrange(i,j+1):s+=freq[k]returns# A recursive function to calculate# cost of optimal binary search treedefoptCost(freq,i,j,dp):# Base casesifj<i:return0ifj==i:returnfreq[i]ifdp[i][j]!=-1:returndp[i][j]# Get sum of freq[i], freq[i+1], ... freq[j]fsum=sum(freq,i,j)minCost=float('inf')# One by one consider all elements# as root and recursively find cost# of the BSTforrinrange(i,j+1):cost=optCost(freq,i,r-1,dp)+optCost(freq,r+1,j,dp)ifcost<minCost:minCost=cost# Return minimum valuedp[i][j]=minCost+fsumreturndp[i][j]# The main function that calculates# minimum cost of a Binary Search Tree.defminCost(keys,freq):n=len(keys)dp=[[-1]*nfor_inrange(n)]returnoptCost(freq,0,n-1,dp)#Driver Code Startsif__name__=='__main__':keys=[10,12,20]freq=[34,8,50]print(minCost(keys,freq))#Driver Code Ends
C#
//Driver Code StartsusingSystem;classGFG{//Driver Code Ends//function to get sum of// array elements freq[i] to freq[j]staticintsum(int[]freq,inti,intj){ints=0;for(intk=i;k<=j;k++)s+=freq[k];returns;}// A recursive function to calculate// cost of optimal binary search treestaticintoptCost(int[]freq,inti,intj,int[,]dp){// Base casesif(j<i)return0;if(j==i)returnfreq[i];if(dp[i,j]!=-1)returndp[i,j];// Get sum of freq[i], freq[i+1], ... freq[j]intfsum=sum(freq,i,j);intminCost=int.MaxValue;// One by one consider all elements// as root and recursively find cost// of the BSTfor(intr=i;r<=j;r++){intcost=optCost(freq,i,r-1,dp)+optCost(freq,r+1,j,dp);if(cost<minCost)minCost=cost;}// Return minimum valuereturndp[i,j]=minCost+fsum;}// The main function that calculates// minimum cost of a Binary Search Tree.staticintminCost(int[]keys,int[]freq){intn=keys.Length;int[,]dp=newint[n,n];for(intx=0;x<n;x++)for(inty=0;y<n;y++)dp[x,y]=-1;returnoptCost(freq,0,n-1,dp);}//Driver Code StartsstaticvoidMain(){int[]keys={10,12,20};int[]freq={34,8,50};Console.WriteLine(minCost(keys,freq));}}//Driver Code Ends
JavaScript
// function to get sum of// array elements freq[i] to freq[j]functionsum(freq,i,j){lets=0;for(letk=i;k<=j;k++)s+=freq[k];returns;}// A recursive function to calculate// cost of optimal binary search treefunctionoptCost(freq,i,j,dp){// Base casesif(j<i)return0;if(j===i)returnfreq[i];if(dp[i][j]!==-1)returndp[i][j];// Get sum of freq[i], freq[i+1], ... freq[j]letfsum=sum(freq,i,j);letminCost=Number.MAX_SAFE_INTEGER;// One by one consider all elements// as root and recursively find cost// of the BSTfor(letr=i;r<=j;r++){letcost=optCost(freq,i,r-1,dp)+optCost(freq,r+1,j,dp);if(cost<minCost)minCost=cost;}// Return minimum valuedp[i][j]=minCost+fsum;returndp[i][j];}// The main function that calculates// minimum cost of a Binary Search Tree.functionminCost(keys,freq){constn=keys.length;constdp=Array.from({length:n},()=>Array(n).fill(-1));returnoptCost(freq,0,n-1,dp);}//Driver Code Starts//Driver Codeconstkeys=[10,12,20];constfreq=[34,8,50];console.log(minCost(keys,freq));//Driver Code Ends
Output
142
[Expected Approach 2] Using Bottom - Up Dp (Tabulation) - O(n^3) Time and O(n^2) Space
In the tabulation approach, we solve the problem step by step instead of recursion.
We make a table dp[i][j] where each cell stores the minimum cost of a BST for keys from index i to j. The base case is simple: if there is only one key, the cost is just its frequency. Then, we fill the table for bigger ranges of keys. For each range, we try every key as the root, calculate the cost of the left and right parts using the table, and add the sum of frequencies for that range. We keep the minimum cost in the table. At the end, dp[0][n-1] gives the minimum cost of the BST for all keys.
C++
//Driver Code Starts#include<iostream>#include<vector>usingnamespacestd;//Driver Code Ends// Function to calculate sum of frequencies from index i to jintsum(vector<int>&freq,inti,intj){ints=0;for(intk=i;k<=j;k++)s+=freq[k];returns;}// Function to calculate minimum cost of a Binary Search Tree using DP (tabulation)intminCost(vector<int>&keys,vector<int>&freq){intn=keys.size();// Create a 2D DP table to store minimum costs for subarrays of keysvector<vector<int>>dp(n,vector<int>(n,0));// Base case: cost of a single key is its frequencyfor(inti=0;i<n;i++){dp[i][i]=freq[i];}// Consider chains of length 2 to nfor(intl=2;l<=n;l++){for(inti=0;i<=n-l;i++){// j is the ending index of the chainintj=i+l-1;dp[i][j]=INT_MAX;// Total frequency sum of keys in current rangeintfsum=sum(freq,i,j);// Try each key in range [i..j] as rootfor(intr=i;r<=j;r++){// Cost when keys[r] is root:// cost of left subtree + cost of right subtree + sum of frequenciesintc=((r>i)?dp[i][r-1]:0)+((r<j)?dp[r+1][j]:0)+fsum;// Update minimum costif(c<dp[i][j]){dp[i][j]=c;}}}}// dp[0][n-1] stores minimum cost for all keysreturndp[0][n-1];}//Driver Code Startsintmain(){vector<int>keys={10,12,20};vector<int>freq={34,8,50};cout<<minCost(keys,freq);return0;}//Driver Code Ends
Java
//Driver Code Startsimportjava.util.Arrays;publicclassGFG{//Driver Code Ends// Function to calculate sum of frequencies from index i to jstaticintsum(intfreq[],inti,intj){ints=0;for(intk=i;k<=j;k++)s+=freq[k];returns;}// Function to calculate minimum cost of a Binary Search Tree using DP (tabulation)staticintminCost(intkeys[],intfreq[]){intn=keys.length;// Create a 2D DP table to store minimum costs for subarrays of keysintdp[][]=newint[n][n];// Base case: cost of a single key is its frequencyfor(inti=0;i<n;i++)dp[i][i]=freq[i];// Consider chains of length 2 to nfor(intl=2;l<=n;l++){for(inti=0;i<=n-l;i++){// j is the ending index of the chainintj=i+l-1;dp[i][j]=Integer.MAX_VALUE;// Total frequency sum of keys in current rangeintfsum=sum(freq,i,j);// Try each key in range [i..j] as rootfor(intr=i;r<=j;r++){// Cost when keys[r] is root:// cost of left subtree + cost of right subtree + sum of frequenciesintc=((r>i)?dp[i][r-1]:0)+((r<j)?dp[r+1][j]:0)+fsum;// Update minimum costif(c<dp[i][j])dp[i][j]=c;}}}// dp[0][n-1] stores minimum cost for all keysreturndp[0][n-1];}//Driver Code Startspublicstaticvoidmain(String[]args){intkeys[]={10,12,20};intfreq[]={34,8,50};System.out.println(minCost(keys,freq));}}//Driver Code Ends
Python
# Function to calculate sum of frequencies from index i to jdefsum(freq,i,j):s=0forkinrange(i,j+1):s+=freq[k]returns# Function to calculate minimum cost of a Binary Search Tree using DP (tabulation)defminCost(keys,freq):n=len(keys)# Create a 2D DP table to store minimum costs for subarrays of keysdp=[[0for_inrange(n)]for_inrange(n)]# Base case: cost of a single key is its frequencyforiinrange(n):dp[i][i]=freq[i]# Consider chains of length 2 to nforlinrange(2,n+1):foriinrange(0,n-l+1):# j is the ending index of the chainj=i+l-1dp[i][j]=float('inf')# Total frequency sum of keys in current rangefsum=sum(freq,i,j)# Try each key in range [i..j] as rootforrinrange(i,j+1):# Cost when keys[r] is root:# cost of left subtree + cost of right subtree + sum of frequenciesc=(dp[i][r-1]ifr>ielse0)+(dp[r+1][j]ifr<jelse0)+fsum# Update minimum costifc<dp[i][j]:dp[i][j]=c# dp[0][n-1] stores minimum cost for all keysreturndp[0][n-1]#Driver Code Startsif__name__=='__main__':keys=[10,12,20]freq=[34,8,50]print(minCost(keys,freq))#Driver Code Ends
C#
//Driver Code StartsusingSystem;classGFG{//Driver Code Ends// Function to calculate sum of frequencies from index i to jstaticintsum(int[]freq,inti,intj){ints=0;for(intk=i;k<=j;k++)s+=freq[k];returns;}// Function to calculate minimum cost of a Binary Search Tree using DP (tabulation)staticintminCost(int[]keys,int[]freq){intn=keys.Length;// Create a 2D DP table to store minimum costs for subarrays of keysint[,]dp=newint[n,n];// Base case: cost of a single key is its frequencyfor(inti=0;i<n;i++)dp[i,i]=freq[i];// Consider chains of length 2 to nfor(intl=2;l<=n;l++){for(inti=0;i<=n-l;i++){// j is the ending index of the chainintj=i+l-1;dp[i,j]=int.MaxValue;// Total frequency sum of keys in current rangeintfsum=sum(freq,i,j);// Try each key in range [i..j] as rootfor(intr=i;r<=j;r++){// Cost when keys[r] is root:// cost of left subtree + cost of right subtree + sum of frequenciesintc=((r>i)?dp[i,r-1]:0)+((r<j)?dp[r+1,j]:0)+fsum;// Update minimum costif(c<dp[i,j])dp[i,j]=c;}}}// dp[0,n-1] stores minimum cost for all keysreturndp[0,n-1];}//Driver Code StartsstaticvoidMain(){int[]keys={10,12,20};int[]freq={34,8,50};Console.WriteLine(minCost(keys,freq));}}//Driver Code Ends
JavaScript
// Function to calculate sum of frequencies from index i to jfunctionsum(freq,i,j){lets=0;for(letk=i;k<=j;k++)s+=freq[k];returns;}// Function to calculate minimum cost of a Binary Search Tree using DP (tabulation)functionminCost(keys,freq){constn=keys.length;// Create a 2D DP table to store minimum costs for subarrays of keysconstdp=Array.from({length:n},()=>Array(n).fill(0));// Base case: cost of a single key is its frequencyfor(leti=0;i<n;i++)dp[i][i]=freq[i];// Consider chains of length 2 to nfor(letl=2;l<=n;l++){for(leti=0;i<=n-l;i++){// j is the ending index of the chainconstj=i+l-1;dp[i][j]=Number.MAX_SAFE_INTEGER;// Total frequency sum of keys in current rangeconstfsum=sum(freq,i,j);// Try each key in range [i..j] as rootfor(letr=i;r<=j;r++){// Cost when keys[r] is root:// cost of left subtree + cost of right subtree + sum of frequenciesconstc=(r>i?dp[i][r-1]:0)+(r<j?dp[r+1][j]:0)+fsum;// Update minimum costif(c<dp[i][j])dp[i][j]=c;}}}// dp[0][n-1] stores minimum cost for all keysreturndp[0][n-1];}//Driver Code Starts// Driver codeconstkeys=[10,12,20];constfreq=[34,8,50];console.log(minCost(keys,freq));//Driver Code Ends