|
| 1 | +#!python |
| 2 | + |
| 3 | + |
| 4 | +class BinaryTreeNode(object): |
| 5 | + |
| 6 | + def __init__(self, data): |
| 7 | + """Initialize this binary tree node with the given data.""" |
| 8 | + self.data = data |
| 9 | + self.left = None |
| 10 | + self.right = None |
| 11 | + |
| 12 | + def __repr__(self): |
| 13 | + """Return a string representation of this binary tree node.""" |
| 14 | + return 'BinaryTreeNode({!r})'.format(self.data) |
| 15 | + |
| 16 | + def is_leaf(self): |
| 17 | + """Return True if this node is a leaf (has no children).""" |
| 18 | + # TODO: Check if both left child and right child have no value |
| 19 | + return ... and ... |
| 20 | + |
| 21 | + def is_branch(self): |
| 22 | + """Return True if this node is a branch (has at least one child).""" |
| 23 | + # TODO: Check if either left child or right child has a value |
| 24 | + return ... or ... |
| 25 | + |
| 26 | + def height(self): |
| 27 | + """Return the height of this node (the number of edges on the longest |
| 28 | + downward path from this node to a descendant leaf node). |
| 29 | + TODO: Best and worst case running time: ??? under what conditions?""" |
| 30 | + # TODO: Check if left child has a value and if so calculate its height |
| 31 | + ... |
| 32 | + # TODO: Check if right child has a value and if so calculate its height |
| 33 | + ... |
| 34 | + # Return one more than the greater of the left height and right height |
| 35 | + ... |
| 36 | + |
| 37 | + |
| 38 | +class BinarySearchTree(object): |
| 39 | + |
| 40 | + def __init__(self, items=None): |
| 41 | + """Initialize this binary search tree and insert the given items.""" |
| 42 | + self.root = None |
| 43 | + self.size = 0 |
| 44 | + if items is not None: |
| 45 | + for item in items: |
| 46 | + self.insert(item) |
| 47 | + |
| 48 | + def __repr__(self): |
| 49 | + """Return a string representation of this binary search tree.""" |
| 50 | + return 'BinarySearchTree({} nodes)'.format(self.size) |
| 51 | + |
| 52 | + def is_empty(self): |
| 53 | + """Return True if this binary search tree is empty (has no nodes).""" |
| 54 | + return self.root is None |
| 55 | + |
| 56 | + def height(self): |
| 57 | + """Return the height of this tree (the number of edges on the longest |
| 58 | + downward path from this tree's root node to a descendant leaf node). |
| 59 | + TODO: Best and worst case running time: ??? under what conditions?""" |
| 60 | + # TODO: Check if root node has a value and if so calculate its height |
| 61 | + ... |
| 62 | + |
| 63 | + def contains(self, item): |
| 64 | + """Return True if this binary search tree contains the given item. |
| 65 | + TODO: Best case running time: ??? under what conditions? |
| 66 | + TODO: Worst case running time: ??? under what conditions?""" |
| 67 | + # Find a node with the given item, if any |
| 68 | + node = self._find_node(item) |
| 69 | + # Return True if a node was found, or False |
| 70 | + return node is not None |
| 71 | + |
| 72 | + def search(self, item): |
| 73 | + """Return an item in this binary search tree matching the given item, |
| 74 | + or None if the given item is not found. |
| 75 | + TODO: Best case running time: ??? under what conditions? |
| 76 | + TODO: Worst case running time: ??? under what conditions?""" |
| 77 | + # Find a node with the given item, if any |
| 78 | + node = self._find_node(item) |
| 79 | + # TODO: Return the node's data if found, or None |
| 80 | + return node.data if ... else None |
| 81 | + |
| 82 | + def insert(self, item): |
| 83 | + """Insert the given item in order into this binary search tree. |
| 84 | + TODO: Best case running time: ??? under what conditions? |
| 85 | + TODO: Worst case running time: ??? under what conditions?""" |
| 86 | + # Handle the case where the tree is empty |
| 87 | + if self.is_empty(): |
| 88 | + # TODO: Create a new root node |
| 89 | + self.root = ... |
| 90 | + # TODO: Increase the tree size |
| 91 | + self.size ... |
| 92 | + return |
| 93 | + # Find the parent node of where the given item should be inserted |
| 94 | + parent = self._find_parent_node(item) |
| 95 | + # TODO: Check if the given item should be inserted left of parent node |
| 96 | + if ...: |
| 97 | + # TODO: Create a new node and set the parent's left child |
| 98 | + parent.left = ... |
| 99 | + # TODO: Check if the given item should be inserted right of parent node |
| 100 | + elif ...: |
| 101 | + # TODO: Create a new node and set the parent's right child |
| 102 | + parent.right = ... |
| 103 | + # TODO: Increase the tree size |
| 104 | + self.size ... |
| 105 | + |
| 106 | + def _find_node(self, item): |
| 107 | + """Return the node containing the given item in this binary search tree, |
| 108 | + or None if the given item is not found. |
| 109 | + TODO: Best case running time: ??? under what conditions? |
| 110 | + TODO: Worst case running time: ??? under what conditions?""" |
| 111 | + # Start with the root node |
| 112 | + node = self.root |
| 113 | + # Loop until we descend past the closest leaf node |
| 114 | + while node is not None: |
| 115 | + # TODO: Check if the given item matches the node's data |
| 116 | + if ...: |
| 117 | + # Return the found node |
| 118 | + return node |
| 119 | + # TODO: Check if the given item is less than the node's data |
| 120 | + elif ...: |
| 121 | + # TODO: Descend to the node's left child |
| 122 | + node = ... |
| 123 | + # TODO: Check if the given item is greater than the node's data |
| 124 | + elif ...: |
| 125 | + # TODO: Descend to the node's right child |
| 126 | + node = ... |
| 127 | + # Not found |
| 128 | + return None |
| 129 | + |
| 130 | + def _find_parent_node(self, item): |
| 131 | + """Return the parent node of the node containing the given item |
| 132 | + (or the parent node of where the given item would be if inserted) |
| 133 | + in this tree, or None if this tree is empty or has only a root node. |
| 134 | + TODO: Best case running time: ??? under what conditions? |
| 135 | + TODO: Worst case running time: ??? under what conditions?""" |
| 136 | + # Start with the root node and keep track of its parent |
| 137 | + node = self.root |
| 138 | + parent = None |
| 139 | + # Loop until we descend past the closest leaf node |
| 140 | + while node is not None: |
| 141 | + # TODO: Check if the given item matches the node's data |
| 142 | + if ...: |
| 143 | + # Return the parent of the found node |
| 144 | + return parent |
| 145 | + # TODO: Check if the given item is less than the node's data |
| 146 | + elif ...: |
| 147 | + # TODO: Update the parent and descend to the node's left child |
| 148 | + parent = node |
| 149 | + node = ... |
| 150 | + # TODO: Check if the given item is greater than the node's data |
| 151 | + elif ...: |
| 152 | + # TODO: Update the parent and descend to the node's right child |
| 153 | + parent = node |
| 154 | + node = ... |
| 155 | + # Not found |
| 156 | + return parent |
| 157 | + |
| 158 | + # This space intentionally left blank (please do not delete this comment) |
| 159 | + |
| 160 | + |
| 161 | +def test_binary_search_tree(): |
| 162 | + # Create a complete binary search tree of 3, 7, or 15 items in level-order |
| 163 | + # items = [2, 1, 3] |
| 164 | + items = [4, 2, 6, 1, 3, 5, 7] |
| 165 | + # items = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15] |
| 166 | + print('items: {}'.format(items)) |
| 167 | + |
| 168 | + tree = BinarySearchTree() |
| 169 | + print('tree: {}'.format(tree)) |
| 170 | + print('root: {}'.format(tree.root)) |
| 171 | + |
| 172 | + print('\nInserting items:') |
| 173 | + for item in items: |
| 174 | + tree.insert(item) |
| 175 | + print('insert({}), size: {}'.format(item, tree.size)) |
| 176 | + print('root: {}'.format(tree.root)) |
| 177 | + |
| 178 | + print('\nSearching for items:') |
| 179 | + for item in items: |
| 180 | + result = tree.search(item) |
| 181 | + print('search({}): {}'.format(item, result)) |
| 182 | + item = 123 |
| 183 | + result = tree.search(item) |
| 184 | + print('search({}): {}'.format(item, result)) |
| 185 | + |
| 186 | + |
| 187 | +if __name__ == '__main__': |
| 188 | + test_binary_search_tree() |
0 commit comments