From bc6b868f319d12fbd9ab471922dee1adb511a45d Mon Sep 17 00:00:00 2001 From: Jimmy Y Date: Sun, 9 Feb 2020 12:47:25 -0800 Subject: [PATCH 01/11] Added code to dynamic_programming directory --- dynamic_programming/optimal_bst.py | 102 +++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 dynamic_programming/optimal_bst.py diff --git a/dynamic_programming/optimal_bst.py b/dynamic_programming/optimal_bst.py new file mode 100644 index 000000000000..cc4dad68f469 --- /dev/null +++ b/dynamic_programming/optimal_bst.py @@ -0,0 +1,102 @@ +#!/usr/bin/python3 + +# This Python program provides O(n^2) dynamic programming solution +# to an optimal BST problem. +# +# The goal of the optimal BST problem is to build a low-cost BST for a +# given set of nodes, each with its own key and frequency. The frequency +# of the node is defined as how many time the node is being searched. +# The characteristic of low-cost BSTs is having a faster overall search +# time than other BSTs. The reason for their fast search time is that +# the nodes with high frequencies will be placed near the root of the +# tree while the nodes with low frequencies will be placed near the tree +# leaves thus reducing the search time. + +import sys + +from random import randint + + +class Node: + """BST Node""" + + def __init__(self, key, freq): + self.key = key + self.freq = freq + + +def print_BST(root, key, i, j, parent, is_left): + """Recursive function to print a BST from a root table.""" + if i > j or i < 0 or j > len(root) - 1: + return + + if parent == -1: + print( + f"{key[root[i][j]]} is the root of the BST." + ) # root does not have a parent + elif is_left: + print(f"{key[root[i][j]]} is the left child of key {parent}.") + else: + print(f"{key[root[i][j]]} is the right child of key {parent}.") + + print_BST( + root, key, i, root[i][j] - 1, key[root[i][j]], True + ) # recur to left child + print_BST( + root, key, root[i][j] + 1, j, key[root[i][j]], False + ) # recur to right child + + +def find_optimal_BST(nodes): + """ + This function calculates and prints the optimal BST. + The dynamic programming algorithm below runs in O(n^2) time + """ + n = len(nodes) + + key = [nodes[i].key for i in range(n)] + freq = [nodes[i].freq for i in range(n)] + + # This 2D array stores the overall tree cost (which's as minimized as possible); for a single key, cost is equal to frequency of the key. + dp = [[freq[i] if i == j else 0 for j in range(n)] for i in range(n)] + # sum[i][j] stores the sum of key frequencies between i and j inclusive in nodes array + sum = [[freq[i] if i == j else 0 for j in range(n)] for i in range(n)] + # stores tree roots used for constructing BST later + root = [[i if i == j else 0 for j in range(n)] for i in range(n)] + + for l in range(2, n + 1): # l is an interval length + for i in range(n - l + 1): + j = i + l - 1 + + dp[i][j] = sys.maxsize # set the value to "infinity" + sum[i][j] = ( + sum[i][j - 1] + freq[j] + ) # (sum in range [i...j]) = (sum in range [i...j - 1]) + freq[j] + + # Apply Knuth's optimization + # Loop without optimization: for r in range(i, j + 1): + for r in range(root[i][j - 1], root[i + 1][j] + 1): # r is a temporal root + left = dp[i][r - 1] if r != i else 0 # optimal cost for left subtree + right = dp[r + 1][j] if r != j else 0 # optimal cost for right subtree + cost = left + sum[i][j] + right + + if dp[i][j] > cost: + dp[i][j] = cost + root[i][j] = r + + print_BST(root, key, 0, n - 1, -1, False) + + +def main(): + # A sample BST + nodes = [Node(i, randint(1, 50)) for i in range(10, 0, -1)] + + # Tree nodes must be sorted first, the code below sorts the keys in + # increasing order and rearrange its frequencies accordingly. + nodes.sort(key=lambda node: node.key) + + find_optimal_BST(nodes) + + +if __name__ == "__main__": + main() From fa025c3ccdf25ca75afc7c2fef6a961a6cbaa583 Mon Sep 17 00:00:00 2001 From: Jimmy Y Date: Sun, 9 Feb 2020 13:19:34 -0800 Subject: [PATCH 02/11] Added doctest --- dynamic_programming/optimal_bst.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/dynamic_programming/optimal_bst.py b/dynamic_programming/optimal_bst.py index cc4dad68f469..48af68e07e0a 100644 --- a/dynamic_programming/optimal_bst.py +++ b/dynamic_programming/optimal_bst.py @@ -49,8 +49,22 @@ def print_BST(root, key, i, j, parent, is_left): def find_optimal_BST(nodes): """ + Precondition: Node keys are sorted in an increasing order. + This function calculates and prints the optimal BST. - The dynamic programming algorithm below runs in O(n^2) time + The dynamic programming algorithm below runs in O(n^2) time. + Implemented from CLRS book. + + >>> nodes = [Node(12, 8), Node(10, 34), Node(20, 50), Node(42, 3), Node(25, 40), Node(37, 30)] + >>> nodes.sort(key=lambda node: node.key) + >>> find_optimal_BST(nodes) + The cost of optimal BST is 324. + 20 is the root of the BST. + 10 is the left child of key 20. + 12 is the right child of key 10. + 25 is the right child of key 20. + 37 is the right child of key 25. + 42 is the right child of key 37. """ n = len(nodes) @@ -69,9 +83,7 @@ def find_optimal_BST(nodes): j = i + l - 1 dp[i][j] = sys.maxsize # set the value to "infinity" - sum[i][j] = ( - sum[i][j - 1] + freq[j] - ) # (sum in range [i...j]) = (sum in range [i...j - 1]) + freq[j] + sum[i][j] = sum[i][j - 1] + freq[j] # (sum in range [i...j]) = (sum in range [i...j - 1]) + freq[j] # Apply Knuth's optimization # Loop without optimization: for r in range(i, j + 1): @@ -84,6 +96,7 @@ def find_optimal_BST(nodes): dp[i][j] = cost root[i][j] = r + print(f"The cost of optimal BST is {dp[0][n - 1]}.") print_BST(root, key, 0, n - 1, -1, False) @@ -99,4 +112,6 @@ def main(): if __name__ == "__main__": + # import doctest + # doctest.testmod() main() From 3376b8b4ffea14d8dd969b75479edbd290324718 Mon Sep 17 00:00:00 2001 From: Jimmy Y Date: Sun, 9 Feb 2020 13:37:52 -0800 Subject: [PATCH 03/11] Elaborated BST --- dynamic_programming/optimal_bst.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dynamic_programming/optimal_bst.py b/dynamic_programming/optimal_bst.py index 48af68e07e0a..1bf36675252e 100644 --- a/dynamic_programming/optimal_bst.py +++ b/dynamic_programming/optimal_bst.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # This Python program provides O(n^2) dynamic programming solution -# to an optimal BST problem. +# to an optimal binary search tree (abbreviated BST) problem. # # The goal of the optimal BST problem is to build a low-cost BST for a # given set of nodes, each with its own key and frequency. The frequency @@ -25,7 +25,7 @@ def __init__(self, key, freq): self.freq = freq -def print_BST(root, key, i, j, parent, is_left): +def print_binary_search_tree(root, key, i, j, parent, is_left): """Recursive function to print a BST from a root table.""" if i > j or i < 0 or j > len(root) - 1: return @@ -39,15 +39,15 @@ def print_BST(root, key, i, j, parent, is_left): else: print(f"{key[root[i][j]]} is the right child of key {parent}.") - print_BST( + print_binary_search_tree( root, key, i, root[i][j] - 1, key[root[i][j]], True ) # recur to left child - print_BST( + print_binary_search_tree( root, key, root[i][j] + 1, j, key[root[i][j]], False ) # recur to right child -def find_optimal_BST(nodes): +def find_optimal_binary_search_tree(nodes): """ Precondition: Node keys are sorted in an increasing order. @@ -57,7 +57,7 @@ def find_optimal_BST(nodes): >>> nodes = [Node(12, 8), Node(10, 34), Node(20, 50), Node(42, 3), Node(25, 40), Node(37, 30)] >>> nodes.sort(key=lambda node: node.key) - >>> find_optimal_BST(nodes) + >>> find_optimal_binary_search_tree(nodes) The cost of optimal BST is 324. 20 is the root of the BST. 10 is the left child of key 20. @@ -97,7 +97,7 @@ def find_optimal_BST(nodes): root[i][j] = r print(f"The cost of optimal BST is {dp[0][n - 1]}.") - print_BST(root, key, 0, n - 1, -1, False) + print_binary_search_tree(root, key, 0, n - 1, -1, False) def main(): @@ -108,7 +108,7 @@ def main(): # increasing order and rearrange its frequencies accordingly. nodes.sort(key=lambda node: node.key) - find_optimal_BST(nodes) + find_optimal_binary_search_tree(nodes) if __name__ == "__main__": From c00a3fbc063ccbdcd7e7573ca1961492bebb200f Mon Sep 17 00:00:00 2001 From: Jimmy Y Date: Sun, 9 Feb 2020 14:16:26 -0800 Subject: [PATCH 04/11] Small tweaks --- dynamic_programming/optimal_bst.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/dynamic_programming/optimal_bst.py b/dynamic_programming/optimal_bst.py index 1bf36675252e..0a787f702380 100644 --- a/dynamic_programming/optimal_bst.py +++ b/dynamic_programming/optimal_bst.py @@ -49,15 +49,11 @@ def print_binary_search_tree(root, key, i, j, parent, is_left): def find_optimal_binary_search_tree(nodes): """ - Precondition: Node keys are sorted in an increasing order. - This function calculates and prints the optimal BST. The dynamic programming algorithm below runs in O(n^2) time. Implemented from CLRS book. - >>> nodes = [Node(12, 8), Node(10, 34), Node(20, 50), Node(42, 3), Node(25, 40), Node(37, 30)] - >>> nodes.sort(key=lambda node: node.key) - >>> find_optimal_binary_search_tree(nodes) + >>> find_optimal_binary_search_tree([Node(12, 8), Node(10, 34), Node(20, 50), Node(42, 3), Node(25, 40), Node(37, 30)]) The cost of optimal BST is 324. 20 is the root of the BST. 10 is the left child of key 20. @@ -66,6 +62,10 @@ def find_optimal_binary_search_tree(nodes): 37 is the right child of key 25. 42 is the right child of key 37. """ + # Tree nodes must be sorted first, the code below sorts the keys in + # increasing order and rearrange its frequencies accordingly. + nodes.sort(key=lambda node: node.key) + n = len(nodes) key = [nodes[i].key for i in range(n)] @@ -103,15 +103,8 @@ def find_optimal_binary_search_tree(nodes): def main(): # A sample BST nodes = [Node(i, randint(1, 50)) for i in range(10, 0, -1)] - - # Tree nodes must be sorted first, the code below sorts the keys in - # increasing order and rearrange its frequencies accordingly. - nodes.sort(key=lambda node: node.key) - find_optimal_binary_search_tree(nodes) if __name__ == "__main__": - # import doctest - # doctest.testmod() main() From c20c5e53c9fad619818af2b08c7d61a0220c0e03 Mon Sep 17 00:00:00 2001 From: Jimmy Y Date: Mon, 10 Feb 2020 08:05:49 -0800 Subject: [PATCH 05/11] Update optimal_bst.py --- dynamic_programming/optimal_bst.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/dynamic_programming/optimal_bst.py b/dynamic_programming/optimal_bst.py index 0a787f702380..bbb7af68157d 100644 --- a/dynamic_programming/optimal_bst.py +++ b/dynamic_programming/optimal_bst.py @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 # This Python program provides O(n^2) dynamic programming solution # to an optimal binary search tree (abbreviated BST) problem. @@ -18,7 +18,7 @@ class Node: - """BST Node""" + """Binary Search Tree Node""" def __init__(self, key, freq): self.key = key @@ -26,7 +26,7 @@ def __init__(self, key, freq): def print_binary_search_tree(root, key, i, j, parent, is_left): - """Recursive function to print a BST from a root table.""" + """Recursive function to print a BST from a root table. (TODO: doctest)""" if i > j or i < 0 or j > len(root) - 1: return @@ -41,19 +41,20 @@ def print_binary_search_tree(root, key, i, j, parent, is_left): print_binary_search_tree( root, key, i, root[i][j] - 1, key[root[i][j]], True - ) # recur to left child + ) print_binary_search_tree( root, key, root[i][j] + 1, j, key[root[i][j]], False - ) # recur to right child + ) def find_optimal_binary_search_tree(nodes): """ This function calculates and prints the optimal BST. The dynamic programming algorithm below runs in O(n^2) time. - Implemented from CLRS book. + Implemented from CLRS (Introduction to Algorithms) book. - >>> find_optimal_binary_search_tree([Node(12, 8), Node(10, 34), Node(20, 50), Node(42, 3), Node(25, 40), Node(37, 30)]) + >>> find_optimal_binary_search_tree([Node(12, 8), Node(10, 34), Node(20, 50), \ + Node(42, 3), Node(25, 40), Node(37, 30)]) The cost of optimal BST is 324. 20 is the root of the BST. 10 is the left child of key 20. @@ -71,7 +72,8 @@ def find_optimal_binary_search_tree(nodes): key = [nodes[i].key for i in range(n)] freq = [nodes[i].freq for i in range(n)] - # This 2D array stores the overall tree cost (which's as minimized as possible); for a single key, cost is equal to frequency of the key. + # This 2D array stores the overall tree cost (which's as minimized as possible); + # for a single key, cost is equal to frequency of the key. dp = [[freq[i] if i == j else 0 for j in range(n)] for i in range(n)] # sum[i][j] stores the sum of key frequencies between i and j inclusive in nodes array sum = [[freq[i] if i == j else 0 for j in range(n)] for i in range(n)] @@ -83,7 +85,7 @@ def find_optimal_binary_search_tree(nodes): j = i + l - 1 dp[i][j] = sys.maxsize # set the value to "infinity" - sum[i][j] = sum[i][j - 1] + freq[j] # (sum in range [i...j]) = (sum in range [i...j - 1]) + freq[j] + sum[i][j] = sum[i][j - 1] + freq[j] # Apply Knuth's optimization # Loop without optimization: for r in range(i, j + 1): From d578beca584af3cea7f4f2a57784876a9981b20e Mon Sep 17 00:00:00 2001 From: Jimmy Y Date: Mon, 10 Feb 2020 17:10:45 -0800 Subject: [PATCH 06/11] Some touchups --- dynamic_programming/optimal_bst.py | 46 +++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/dynamic_programming/optimal_bst.py b/dynamic_programming/optimal_bst.py index bbb7af68157d..20860e989df8 100644 --- a/dynamic_programming/optimal_bst.py +++ b/dynamic_programming/optimal_bst.py @@ -6,11 +6,15 @@ # The goal of the optimal BST problem is to build a low-cost BST for a # given set of nodes, each with its own key and frequency. The frequency # of the node is defined as how many time the node is being searched. -# The characteristic of low-cost BSTs is having a faster overall search -# time than other BSTs. The reason for their fast search time is that -# the nodes with high frequencies will be placed near the root of the -# tree while the nodes with low frequencies will be placed near the tree -# leaves thus reducing the search time. +# The search cost of BST is given by this formula: +# +# cost(1, n) = sum{i = 1 to n}((depth(node_i) + 1) * node_i_freq) +# +# where n is number of nodes in the BST. The characteristic of low-cost +# BSTs is having a faster overall search time than other BSTs. The reason +# for their fast search time is that the nodes with high frequencies will +# be placed near the root of the tree while the nodes with low frequencies +# will be placed near the tree leaves thus reducing the search time. import sys @@ -24,9 +28,25 @@ def __init__(self, key, freq): self.key = key self.freq = freq + def __str__(self): + return f"Node(key={self.key}, freq={self.freq})" + def print_binary_search_tree(root, key, i, j, parent, is_left): - """Recursive function to print a BST from a root table. (TODO: doctest)""" + """ + Recursive function to print a BST from a root table. + + >>> key = [3, 8, 9, 10, 17, 21] + >>> root = [[0, 1, 1, 1, 1, 1], [0, 1, 1, 1, 1, 3], [0, 0, 2, 3, 3, 3], \ + [0, 0, 0, 3, 3, 3], [0, 0, 0, 0, 4, 5], [0, 0, 0, 0, 0, 5]] + >>> print_binary_search_tree(root, key, 0, 5, -1, False) + 8 is the root of the BST. + 3 is the left child of key 8. + 10 is the right child of key 8. + 9 is the left child of key 10. + 21 is the right child of key 10. + 17 is the left child of key 21. + """ if i > j or i < 0 or j > len(root) - 1: return @@ -39,12 +59,8 @@ def print_binary_search_tree(root, key, i, j, parent, is_left): else: print(f"{key[root[i][j]]} is the right child of key {parent}.") - print_binary_search_tree( - root, key, i, root[i][j] - 1, key[root[i][j]], True - ) - print_binary_search_tree( - root, key, root[i][j] + 1, j, key[root[i][j]], False - ) + print_binary_search_tree(root, key, i, root[i][j] - 1, key[root[i][j]], True) + print_binary_search_tree(root, key, root[i][j] + 1, j, key[root[i][j]], False) def find_optimal_binary_search_tree(nodes): @@ -98,7 +114,11 @@ def find_optimal_binary_search_tree(nodes): dp[i][j] = cost root[i][j] = r - print(f"The cost of optimal BST is {dp[0][n - 1]}.") + print("BST Nodes:") + for node in nodes: + print(node) + + print(f"\nThe cost of optimal BST for given tree nodes is {dp[0][n - 1]}.") print_binary_search_tree(root, key, 0, n - 1, -1, False) From 63cf4ec21ca62777db67d8dbed756180113d494e Mon Sep 17 00:00:00 2001 From: Jimmy Y Date: Mon, 10 Feb 2020 17:32:17 -0800 Subject: [PATCH 07/11] Fixed doctest --- dynamic_programming/optimal_bst.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/dynamic_programming/optimal_bst.py b/dynamic_programming/optimal_bst.py index 20860e989df8..b16adb5e9082 100644 --- a/dynamic_programming/optimal_bst.py +++ b/dynamic_programming/optimal_bst.py @@ -71,7 +71,15 @@ def find_optimal_binary_search_tree(nodes): >>> find_optimal_binary_search_tree([Node(12, 8), Node(10, 34), Node(20, 50), \ Node(42, 3), Node(25, 40), Node(37, 30)]) - The cost of optimal BST is 324. + BST Nodes: + Node(key=10, freq=34) + Node(key=12, freq=8) + Node(key=20, freq=50) + Node(key=25, freq=40) + Node(key=37, freq=30) + Node(key=42, freq=3) + + The cost of optimal BST for given tree nodes is 324. 20 is the root of the BST. 10 is the left child of key 20. 12 is the right child of key 10. From f0476933be0016b05bff1d8f2637be084283aa13 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 11 Feb 2020 05:37:26 +0100 Subject: [PATCH 08/11] Update optimal_bst.py --- dynamic_programming/optimal_bst.py | 60 ++++++++++++++++-------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/dynamic_programming/optimal_bst.py b/dynamic_programming/optimal_bst.py index b16adb5e9082..f9765cccbd34 100644 --- a/dynamic_programming/optimal_bst.py +++ b/dynamic_programming/optimal_bst.py @@ -1,20 +1,21 @@ #!/usr/bin/env python3 -# This Python program provides O(n^2) dynamic programming solution -# to an optimal binary search tree (abbreviated BST) problem. +# This Python program implements a binary search tree (abbreviated BST) +# dynamic programming algorithm that delivers O(n^2) performance. # # The goal of the optimal BST problem is to build a low-cost BST for a # given set of nodes, each with its own key and frequency. The frequency # of the node is defined as how many time the node is being searched. -# The search cost of BST is given by this formula: +# The search cost of binary search tree is given by this formula: # # cost(1, n) = sum{i = 1 to n}((depth(node_i) + 1) * node_i_freq) # # where n is number of nodes in the BST. The characteristic of low-cost -# BSTs is having a faster overall search time than other BSTs. The reason -# for their fast search time is that the nodes with high frequencies will -# be placed near the root of the tree while the nodes with low frequencies -# will be placed near the tree leaves thus reducing the search time. +# BSTs is having a faster overall search time than other implementations. +# The reason for their fast search time is that the nodes with high +# frequencies will be placed near the root of the tree while the nodes +# with low frequencies will be placed near the leaves of the tree thus +# reducing search time in the most frequent instances. import sys @@ -23,12 +24,15 @@ class Node: """Binary Search Tree Node""" - def __init__(self, key, freq): self.key = key self.freq = freq def __str__(self): + """ + >>> str(Node(1, 2)) + Node(key=1, freq=2) + """ return f"Node(key={self.key}, freq={self.freq})" @@ -50,28 +54,28 @@ def print_binary_search_tree(root, key, i, j, parent, is_left): if i > j or i < 0 or j > len(root) - 1: return - if parent == -1: - print( - f"{key[root[i][j]]} is the root of the BST." - ) # root does not have a parent + node = root[i][j] + if parent == -1: # root does not have a parent + print(f"{key[node]} is the root of the binary search tree.") elif is_left: - print(f"{key[root[i][j]]} is the left child of key {parent}.") + print(f"{key[node]} is the left child of key {parent}.") else: - print(f"{key[root[i][j]]} is the right child of key {parent}.") + print(f"{key[node]} is the right child of key {parent}.") - print_binary_search_tree(root, key, i, root[i][j] - 1, key[root[i][j]], True) - print_binary_search_tree(root, key, root[i][j] + 1, j, key[root[i][j]], False) + print_binary_search_tree(root, key, i, node - 1, key[node], True) + print_binary_search_tree(root, key, node + 1, j, key[node], False) def find_optimal_binary_search_tree(nodes): """ - This function calculates and prints the optimal BST. + This function calculates and prints the optimal binary search tree. The dynamic programming algorithm below runs in O(n^2) time. Implemented from CLRS (Introduction to Algorithms) book. + https://en.wikipedia.org/wiki/Introduction_to_Algorithms >>> find_optimal_binary_search_tree([Node(12, 8), Node(10, 34), Node(20, 50), \ Node(42, 3), Node(25, 40), Node(37, 30)]) - BST Nodes: + Binary search tree nodes: Node(key=10, freq=34) Node(key=12, freq=8) Node(key=20, freq=50) @@ -80,7 +84,7 @@ def find_optimal_binary_search_tree(nodes): Node(key=42, freq=3) The cost of optimal BST for given tree nodes is 324. - 20 is the root of the BST. + 20 is the root of the binary search tree. 10 is the left child of key 20. 12 is the right child of key 10. 25 is the right child of key 20. @@ -93,15 +97,15 @@ def find_optimal_binary_search_tree(nodes): n = len(nodes) - key = [nodes[i].key for i in range(n)] - freq = [nodes[i].freq for i in range(n)] + keys = [nodes[i].key for i in range(n)] + freqs = [nodes[i].freq for i in range(n)] # This 2D array stores the overall tree cost (which's as minimized as possible); # for a single key, cost is equal to frequency of the key. - dp = [[freq[i] if i == j else 0 for j in range(n)] for i in range(n)] + dp = [[freqs[i] if i == j else 0 for j in range(n)] for i in range(n)] # sum[i][j] stores the sum of key frequencies between i and j inclusive in nodes array - sum = [[freq[i] if i == j else 0 for j in range(n)] for i in range(n)] - # stores tree roots used for constructing BST later + sum = [[freqs[i] if i == j else 0 for j in range(n)] for i in range(n)] + # stores tree roots that will be used later for constructing binary search tree root = [[i if i == j else 0 for j in range(n)] for i in range(n)] for l in range(2, n + 1): # l is an interval length @@ -109,7 +113,7 @@ def find_optimal_binary_search_tree(nodes): j = i + l - 1 dp[i][j] = sys.maxsize # set the value to "infinity" - sum[i][j] = sum[i][j - 1] + freq[j] + sum[i][j] = sum[i][j - 1] + freqs[j] # Apply Knuth's optimization # Loop without optimization: for r in range(i, j + 1): @@ -122,16 +126,16 @@ def find_optimal_binary_search_tree(nodes): dp[i][j] = cost root[i][j] = r - print("BST Nodes:") + print("Binary search tree nodes:") for node in nodes: print(node) print(f"\nThe cost of optimal BST for given tree nodes is {dp[0][n - 1]}.") - print_binary_search_tree(root, key, 0, n - 1, -1, False) + print_binary_search_tree(root, keys, 0, n - 1, -1, False) def main(): - # A sample BST + # A sample binary search tree nodes = [Node(i, randint(1, 50)) for i in range(10, 0, -1)] find_optimal_binary_search_tree(nodes) From 0a0373c8b084309792eee82a310f87bf16bc1075 Mon Sep 17 00:00:00 2001 From: Jimmy Y Date: Mon, 10 Feb 2020 20:43:27 -0800 Subject: [PATCH 09/11] Update optimal_bst.py --- dynamic_programming/optimal_bst.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dynamic_programming/optimal_bst.py b/dynamic_programming/optimal_bst.py index f9765cccbd34..372e9472efa8 100644 --- a/dynamic_programming/optimal_bst.py +++ b/dynamic_programming/optimal_bst.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 -# This Python program implements a binary search tree (abbreviated BST) -# dynamic programming algorithm that delivers O(n^2) performance. +# This Python program implements an optimal binary search tree (abbreviated BST) +# building dynamic programming algorithm that delivers O(n^2) performance. # # The goal of the optimal BST problem is to build a low-cost BST for a # given set of nodes, each with its own key and frequency. The frequency From d212ed85e7a49bbc9438fb26e9787e1bc8e35220 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 11 Feb 2020 05:45:19 +0100 Subject: [PATCH 10/11] Update optimal_bst.py --- dynamic_programming/optimal_bst.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dynamic_programming/optimal_bst.py b/dynamic_programming/optimal_bst.py index 372e9472efa8..b0f248acf35c 100644 --- a/dynamic_programming/optimal_bst.py +++ b/dynamic_programming/optimal_bst.py @@ -31,7 +31,7 @@ def __init__(self, key, freq): def __str__(self): """ >>> str(Node(1, 2)) - Node(key=1, freq=2) + 'Node(key=1, freq=2)' """ return f"Node(key={self.key}, freq={self.freq})" @@ -44,7 +44,7 @@ def print_binary_search_tree(root, key, i, j, parent, is_left): >>> root = [[0, 1, 1, 1, 1, 1], [0, 1, 1, 1, 1, 3], [0, 0, 2, 3, 3, 3], \ [0, 0, 0, 3, 3, 3], [0, 0, 0, 0, 4, 5], [0, 0, 0, 0, 0, 5]] >>> print_binary_search_tree(root, key, 0, 5, -1, False) - 8 is the root of the BST. + 8 is the root of the binary search tree. 3 is the left child of key 8. 10 is the right child of key 8. 9 is the left child of key 10. From 5baf95d2f84399d430dbfb120994049f13e04a79 Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Tue, 11 Feb 2020 05:53:13 +0100 Subject: [PATCH 11/11] Rename optimal_bst.py to optimal_binary_search_tree.py --- .../{optimal_bst.py => optimal_binary_search_tree.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename dynamic_programming/{optimal_bst.py => optimal_binary_search_tree.py} (100%) diff --git a/dynamic_programming/optimal_bst.py b/dynamic_programming/optimal_binary_search_tree.py similarity index 100% rename from dynamic_programming/optimal_bst.py rename to dynamic_programming/optimal_binary_search_tree.py