[Coding Made Simple] Optimal Binary Search Tree

Given keys and frequency at which these keys are searched, how would you create a binary search tree from these keys

such that the cost of searching is minimum.

The cost of searching is defined as the sum of all node‘s search frequency * its depth; Root node‘s depth is 1.

Solution 1. Recursion

Algorithm:

1. first sort the input keys in ascending order based on their values;

2. from left to right of the sorted keys, pick keys[i] as the root node, then recursively calculate the search min cost of

the left subtree and right subtree;

3. repeat step 2 for all keys and upate the depths of each node that gives the min search cost.

4. reconstruct the optimal BST using the minCostDepths[] that contains the depth of each node, this BST gives the min search cost.

This recursive solution is not efficient in that it has overlapping subprolems. This is demonstrated in the following example.

Given keys {10, 11, 12, 13, 14}:

if 11 is used as root, then the subproblems of {10} and {12, 13, 14} are solved recursively;

if 12 is used as root, then the subproblems of {10, 11} and {13, 14} are solved recursively;

When solving subproblem of {12, 13, 14} in the case of 11 as root, we need to solve subproblem of {13, 14} after selecting 12 as root of subproblem {12, 13, 14};

This subproblem of {13, 14} is later solved again in the case of 12 as root; This is redundant work!

 1 import java.util.Arrays;
 2 import java.util.Comparator;
 3
 4 class TreeNode{
 5     int key;
 6     int freq;
 7     TreeNode left;
 8     TreeNode right;
 9     TreeNode(int key, int freq){
10         this.key = key;
11         this.freq = freq;
12         this.left = null;
13         this.right = null;
14     }
15 }
16 public class OptimalBST {
17     public TreeNode getOptimalBST(TreeNode[] nodes){
18         if(nodes == null || nodes.length == 0){
19             return null;
20         }
21         Comparator<TreeNode> comp = new Comparator<TreeNode>(){
22             public int compare(TreeNode node1, TreeNode node2){
23                 return node1.key - node2.key;
24             }
25         };
26         Arrays.sort(nodes, comp);
27         int[] depths = new int[nodes.length];
28         int[] minCostDepths = new int[nodes.length];
29         getOptimalBSTRecursive(nodes, minCostDepths, depths, 0, nodes.length - 1, 1);
30         return reconstructOptimalBST(nodes, minCostDepths, 0, nodes.length - 1);
31     }
32     private int getOptimalBSTRecursive(TreeNode[] nodes, int[] minCostDepths, int[] depths, int start, int end, int depth){
33         if(start > end){
34             return 0;
35         }
36         int minCost = Integer.MAX_VALUE;
37         int cost = 0;
38         for(int i = start; i <= end; i++){
39             depths[i] = depth;
40             cost += nodes[i].freq * depth;
41             cost += getOptimalBSTRecursive(nodes, minCostDepths, depths, start, i - 1, depth + 1);
42             cost += getOptimalBSTRecursive(nodes, minCostDepths, depths, i + 1, end, depth + 1);
43             if(cost < minCost){
44                 minCost = cost;
45                 for(int j = start; j <= end; j++){
46                     minCostDepths[j] = depths[j];
47                 }
48             }
49         }
50         return minCost;
51     }
52     private TreeNode reconstructOptimalBST(TreeNode[] nodes, int[] minCostDepths, int start, int end){
53         if(start > end){
54             return null;
55         }
56         int minDepth = Integer.MAX_VALUE;
57         int minDepthIdx = 0;
58         for(int i = start; i <= end; i++){
59             if(minCostDepths[i] < minDepth){
60                 minDepth = minCostDepths[i];
61                 minDepthIdx = i;
62             }
63         }
64         nodes[minDepthIdx].left = reconstructOptimalBST(nodes, minCostDepths, start, minDepthIdx - 1);
65         nodes[minDepthIdx].right = reconstructOptimalBST(nodes, minCostDepths, minDepthIdx + 1, end);
66         return nodes[minDepthIdx];
67     }
68 }

Solution 2. Dynamic Programming

Given a list of n nodes that are sorted in ascending order using their keys,  nodes[0..... n - 1],

State: T[i][j] contains the min cost of the bst using nodes[i....j] and the index of the root of this optimal bst.

Function:  For a given range of nodes, its min cost is the sum of each nodes‘ key

T[i][j].cost = sum of nodes‘ freq in nodes[i....j] + min of {T[i][k - 1] + T[k + 1][j],  for k is from i to j}

Initialization: T[i][i].cost = nodes[i].freq; T[i][i].idx = i;

Answer: T[0][n - 1] contains the min cost the optimal bst and its root idx.

The whole optimal bst can be constructed recursively base on the DP 2D array infos.

The DP solution avoids the duplicated work in solution 1 at the cost of O(n^2) extra memory.

 1 import java.util.Arrays;
 2 import java.util.Comparator;
 3
 4 class BstNode {
 5     int key;
 6     int freq;
 7     BstNode left;
 8     BstNode right;
 9     BstNode(int key, int freq) {
10         this.key = key;
11         this.freq = freq;
12         this.left = null;
13         this.right = null;
14     }
15 }
16 public class OptimalBST {
17     class optimalBstEntry {
18         int cost;
19         int idx;
20         optimalBstEntry(int c, int i) {
21             this.cost = c;
22             this.idx = i;
23         }
24     }
25     public BstNode getOptimalBST(BstNode[] nodes) {
26         if(nodes == null || nodes.length == 0) {
27             return null;
28         }
29         Comparator<BstNode> comp = new Comparator<BstNode>() {
30             public int compare(BstNode node1, BstNode node2) {
31                 return node1.key - node2.key;
32             }
33         };
34         Arrays.sort(nodes, comp);
35         optimalBstEntry[][] T = new optimalBstEntry[nodes.length][nodes.length];
36         for(int i = 0; i < nodes.length; i++) {
37             for(int j = i; j < nodes.length; j++)
38             T[i][j] = new optimalBstEntry(Integer.MAX_VALUE, -1);
39         }
40         for(int i = 0; i < nodes.length; i++) {
41             T[i][i].cost = nodes[i].freq;
42             T[i][i].idx = i;
43         }
44         for(int len = 2; len <= nodes.length; len++) {
45             for(int startIdx = 0; startIdx <= nodes.length - len; startIdx++) {
46                 int sum = 0; int min = Integer.MAX_VALUE;
47                 for(int i = startIdx; i < (startIdx + len); i++) {
48                     sum += nodes[i].freq;
49                 }
50                 for(int rootIdx = startIdx; rootIdx <= (startIdx + len - 1); rootIdx++) {
51                     int leftCost = 0, rightCost = 0;
52                     if(rootIdx - 1 >= startIdx) {
53                         leftCost = T[startIdx][rootIdx - 1].cost;
54                     }
55                     if(rootIdx + 1 <= startIdx + len - 1) {
56                         rightCost = T[rootIdx + 1][startIdx + len - 1].cost;
57                     }
58                     if(leftCost + rightCost < min) {
59                         T[startIdx][startIdx + len - 1].cost = sum + leftCost + rightCost;
60                         T[startIdx][startIdx + len - 1].idx = rootIdx;
61                         min = leftCost + rightCost;
62                     }
63                 }
64             }
65         }
66         //construct the optimal BST
67         return constructOptimalBst(nodes, T, 0, nodes.length - 1);
68     }
69     private BstNode constructOptimalBst(BstNode[] nodes, optimalBstEntry[][] T, int start, int end) {
70         if(start > end) {
71             return null;
72         }
73         int rootIdx = T[start][end].idx;
74         BstNode root = nodes[rootIdx];
75         root.left = constructOptimalBst(nodes, T, start, rootIdx - 1);
76         root.right = constructOptimalBst(nodes, T, rootIdx + 1, end);
77         return root;
78     }
79 }

Further optimization

When calculating the sum of keys in a given range,  the above solution always start from stratch for a given range.

For example, when calculating the sum of keys of the node 0, 1, 2, 3, it adds all 4 nodes‘ keys. Since we‘ve already

calculated the key sums of 3 nodes, we can use prefix sum to memorize the previous calculation results. Doing this

makes this sum operation from O(n) to O(1) since we only need to do one add operation for the newly included node.

However, this optimize does not change the asymptotic complexity, it only changes the constant factor.

The runtime is still O(n^3).

Related Topics

Prefix Sum

时间: 2024-08-04 20:32:40

[Coding Made Simple] Optimal Binary Search Tree的相关文章

uva 10304 Optimal Binary Search Tree (区间DP)

uva 10304 Optimal Binary Search Tree 题目大意:给出N个结点(已知每个结点的权值)来建树,建树时要满足以下规则:左子树的节点的值要全小于父节点,右子树的节点的值要全大于父节点.要求最后建出的树总权值最小.总权值=各结点乘以层数(从0层开始)之后相加的和. 解题思路:dp[i][j]代表区间第i个结点到第j个结点组成的树最小的总权值.dp[j][i]=min(dp[j][i],dp[j][k?1]+dp[k+1][i]+sum[i]?sum[j?1]?num[k

uva 10304 - Optimal Binary Search Tree 区间dp

题目链接 给n个数, 这n个数的值是从小到大的, 给出个n个数的出现次数. 然后用他们组成一个bst.访问每一个数的代价是这个点的深度*这个点访问的次数. 问你代价最小值是多少. 区间dp的时候, 如果l >= r, 那么返回0, l == r-1, 返回两个数中小的一个. 其他情况的话枚举分界点进行状态转移. #include <bits/stdc++.h> using namespace std; #define mem1(a) memset(a, -1, sizeof(a)) co

UVA 10304 Optimal Binary Search Tree

简单区间DP. #include<cstdio> #include<cstring> #include<cmath> #include<vector> #include<map> #include<queue> #include<stack> #include<algorithm> using namespace std; const int maxn = 300; int n; int a[maxn], su

ITA 15.5 Optimal binary search trees

p400 页最后一段 When j >= i , we need to select a root kr from among ki ... kj and then make an optimal binary search tree with keys ki ... kr-1 as its left subtree and an optimal binary search tree with keys kr+1 ... kj as its right subtree.What happens

Binary search tree system and method

A binary search tree is provided for efficiently organizing values for a set of items, even when values are duplicated. In generating the binary search tree, the value of each item in a set of values is determined. If a particular value is unique and

UVA1264 &amp;&amp; UVAlive4847 Binary Search Tree

A binary search tree is a binary tree. It may be empty. If it is not empty, it satisfies the following properties: (1) Every node has a key, and no two nodes have the same key. (2) The keys in a nonempty left subtree must be smaller than the key in t

Binary search tree or not

https://github.com/Premiumlab/Python-for-Algorithms--Data-Structures--and-Interviews/blob/master/Mock%20Interviews/Ride%20Share%20Start-Up%20Company/Ride%20Share%20Company%20-%20Interview%20Questions%20-%20SOLUTIONS/On-Site%20Question%203%20-%20SOLUT

2018.3.5-6 knapsack problem, sequence alignment and optimal binary search trees

这周继续dynamic programming,这三个算法都是dynamic programming的. knapsack problem有一种greedy的解法,虽然简单但是不保证正确,这里光头哥讲的是dynamic的解法.其实和上次那个max weight independent set的算法差不多,同样是每个物件都判断一遍有这个物件和没这个物件两种情况,用bottom-up的方法来解,然后得到一个最大的value值,这时因为没有得到具体的选择方案,所以最后还需要一部重构的步骤得到具体方案.

[LeetCode] Find Mode in Binary Search Tree 找二分搜索数的众数

Given a binary search tree (BST) with duplicates, find all the mode(s) (the most frequently occurred element) in the given BST. Assume a BST is defined as follows: The left subtree of a node contains only nodes with keys less than or equal to the nod