In: Computer Science
Lab 5: Binary Search Tree
Implement operations for a Binary Search Tree class starting from the template provided under the PolyLearn assignment, using the class TreeNode that is also provided. You may (should) implement helper methods that make your code easier to write, read, and understand. You will also need to write test cases of your own as you develop the methods. You may use iterative and/or recursive functions in your implementation.
The following starter files are available .
• binary_search_tree.py
• binary_search_tree_tests.py
Complete the implementations
. • binary_search_tree.py
• binary_search_tree_tests.py
• queue_array.py
The incomplete functions in binary_search_tree.py must be completed and tested
The following is raw code for each file
=========================
binary_search_tree.py
from queue_array import Queue class TreeNode: def __init__(self, key, data, left=None, right=None): self.key = key self.data = data self.left = left self.right = right def __eq__(self, other): return ((type(other) == TreeNode) and self.key == other.key and self.data == other.data and self.left == other.left and self.right == other.right ) def __repr__(self): return ("TreeNode({!r}, {!r}, {!r}, {!r})".format(self.key, self.data, self.left, self.right)) class BinarySearchTree: def __init__(self): # Returns empty BST self.root = None def is_empty(self): # returns True if tree is empty, else False pass def search(self, key): # returns True if key is in a node of the tree, else False pass def insert(self, key, data=None): # inserts new node w/ key and data # If an item with the given key is already in the BST, # the data in the tree will be replaced with the new data # Example creation of node: temp = TreeNode(key, data) pass def find_min(self): # returns a tuple with min key and data in the BST # returns None if the tree is empty pass def find_max(self): # returns a tuple with max key and data in the BST # returns None if the tree is empty pass def tree_height(self): # return the height of the tree # returns None if tree is empty pass def inorder_list(self): # return Python list of BST keys representing in-order traversal of BST pass def preorder_list(self): # return Python list of BST keys representing pre-order traversal of BST pass def level_order_list(self): # return Python list of BST keys representing level-order traversal of BST # You MUST use your queue_array data structure from lab 3 to implement this method q = Queue(25000) # Don't change this! pass
======================
binary_search_tree_tests.py
import unittest from binary_search_tree import * class TestLab4(unittest.TestCase): def test_simple(self): bst = BinarySearchTree() self.assertTrue(bst.is_empty()) bst.insert(10, 'stuff') self.assertTrue(bst.search(10)) self.assertEqual(bst.find_min(), (10, 'stuff')) bst.insert(10, 'other') self.assertEqual(bst.find_max(), (10, 'other')) self.assertEqual(bst.tree_height(), 0) self.assertEqual(bst.inorder_list(), [10]) self.assertEqual(bst.preorder_list(), [10]) self.assertEqual(bst.level_order_list(), [10]) if __name__ == '__main__': unittest.main()
==========================
queue_array.py
# Queue ADT - circular array implementation class Queue: """Implements an efficient first-in first-out Abstract Data Type using a Python List""" def __init__(self, capacity, init_items=None): """Creates a queue with a capacity and initializes with init_items""" self.capacity= capacity # capacity of queue self.items = [None]*capacity # array for queue self.num_items = 0 # number of items in queue self.front = 0 # front index of queue (items removed from front) self.rear = 0 # rear index of queue (items enter at rear) if init_items is not None: # if init_items is not None, initialize queue if len(init_items) > capacity: raise IndexError else: self.num_items = len(init_items) self.items[:self.num_items] = init_items self.rear = self.num_items % self.capacity # % capacity addresses length=capacity def __eq__(self, other): return ((type(other) == Queue) and self.capacity == other.capacity and self.get_items() == other.get_items() ) def __repr__(self): return ("Queue({!r}, {!r})".format(self.capacity, self.get_items())) # get_items returns array (Python list) of items in Queue # first item in the list will be front of queue, last item is rear of queue def get_items(self): if self.num_items == 0: return [] if self.front < self.rear: return self.items[self.front:self.rear] else: return self.items[self.front:] + self.items[:self.rear] def is_empty(self): """Returns true if the queue is empty and false otherwise Must be O(1)""" if self.num_items == 0: return True return False def is_full(self): """Returns true if the queue is full and false otherwise Must be O(1)""" if self.num_items == self.capacity: return True return False def enqueue(self, item): """enqueues item, raises IndexError if Queue is full Must be O(1)""" if self.is_full(): raise IndexError() self.items[self.rear] = item self.rear += 1 self.rear %= self.capacity + 1 self.num_items += 1 def dequeue(self): """dequeues and returns item, raises IndexError if Queue is empty Must be O(1)""" if self.is_empty(): raise IndexError() temp = self.items[self.front] # self.items[self.front] = None self.front += 1 self.front %= self.capacity + 1 self.num_items -= 1 return temp def size(self): """Returns the number of items in the queue Must be O(1)""" return self.num_items if __name__ == "__main__": q1 = Queue(10) print(q1) q1.enqueue(1) q1.enqueue(2) q1.enqueue(3) print(q1) q2 = Queue(10) q2.enqueue(1) q2.enqueue(2) q2.enqueue(3) print(q1==q2) print(q2.dequeue()) print(q2) print(q1 == q2) print(q2.dequeue()) print(q2.dequeue()) q2.enqueue(4) q2.enqueue(5) q2.enqueue(6) q2.enqueue(7) q2.enqueue(8) q2.enqueue(1) q2.enqueue(2) q2.enqueue(3) print(q2.items) print(q2.dequeue()) print(q2.dequeue()) print(q2.dequeue()) print(q2.dequeue()) print(q2.dequeue()) print(q2.items,q2.front,q2.rear,q2.get_items()) print(q2) print(q2==q1)
It took 2 hours to complete this so please upvote me for all my efforts # # binary_search_tree.py # import sys class bt_node: data = 0 left = None right = None class BinarySearchTree: def __init__(self): self.tree = None self.tree_list = [] self.tree_nodes = [] def init_node(self, data): """ Create and return a bt_node object that has been initialized with the given data and two None children. """ new_node = bt_node() new_node.data = data return new_node def insert(self, new_node): """ Insert the new_node into the tree at the correct location. """ if self.tree is None: self.tree = new_node else: cursor = self.tree while True: if new_node.data < cursor.data: if cursor.left is None: cursor.left = new_node break else: cursor = cursor.left if new_node.data >= cursor.data: if cursor.right is None: cursor.right = new_node break else: cursor = cursor.right def insert_data(self, data): """ Insert a new node that contains the given data into the tree at the correct location. """ new_node = self.init_node(data) self.insert(new_node) def remove(self, data): """ Removes a node from the tree whose data value is the same as the argument. """ if self.contains(data): cursor = self.tree parent = None while True: if cursor.data == data: break elif cursor.data > data: parent = cursor cursor = cursor.left elif cursor.data < data: parent = cursor cursor = cursor.right if cursor.right and cursor.left: successor, successor_parent = self.find_successor(cursor) predecessor, predecessor_parent = self.find_predecessor(cursor) self.to_array() if len(self.tree_list) % 2 == 0: if successor_parent: cursor.data = successor.data self.delete_node(successor, successor_parent) else: cursor.data = successor.data self.delete_node(successor, cursor) else: if predecessor_parent: cursor.data = predecessor.data self.delete_node(predecessor, predecessor_parent) else: cursor.data = predecessor.data self.delete_node(predecessor, cursor) else: if parent: self.delete_node(cursor, parent) else: try: successor, successor_parent = self.find_successor(cursor) except AttributeError: predecessor, predecessor_parent = self.find_predecessor(cursor) if successor: cursor.data = successor.data self.delete_node(successor, cursor) else: cursor.data = predecessor.data self.delete_node(predecessor, cursor) def delete_node(self, node, parent): ''' Deletes specified node with specified parent Should not be called ''' try: if parent.left == node: if node.left: parent.left = node.left elif node.right: parent.left = node.right else: parent.left = None elif parent.right == node: if node.left: parent.right = node.left elif node.right: parent.right = node.right else: parent.right = None else: if parent.left == node: parent.left = None if parent.right == node: parent.right = None except AttributeError: print sys.exc_info() print ' ', self.to_array() print ' ', node.data sys.exit(1) def find_successor(self, node): ''' Finds the in-order successor of the given node ''' successor_node = node.right successor_top = None while True: if successor_node.left == None: break else: successor_top = successor_node successor_node = successor_node.left return successor_node, successor_top def find_predecessor(self, node): ''' Finds the in-order predecessor of the given node ''' predecessor_node = node.left predecessor_top = None while True: if predecessor_node.right == None: break else: predecessor_top = predecessor_node predecessor_node = predecessor_node.right return predecessor_node, predecessor_top def contains(self, data): """ Return True or False depending on if this tree contains a node with the supplied data. """ if self.tree is None: return False else: cursor = self.tree while True: if data < cursor.data: if cursor.data == data: return True if cursor.left is None: return False else: cursor = cursor.left if data >= cursor.data: if cursor.data == data: return True if cursor.right is None: return False else: cursor = cursor.right def get_node(self, data): """ If the tree contains a node with the supplied data, return it. Otherwise return None. """ if self.contains(data): cursor = self.tree while True: if data < cursor.data: cursor = cursor.left if data >= cursor.data: if cursor.data == data: return cursor else: cursor = cursor.right else: return None def size(self): """ Return the size of this tree. If it is empty this returns 0. """ if self.tree is None: return 0 else: return len(self.to_array()) def to_array(self): """ Create and fill a list with the data contained in this tree. The elements of the returned list must be in the same order as they are found during an inorder traversal, which means the numbers should be in non-decreasing order. """ if self.tree is None: return [] else: self.tree_list = [] self.tree_nodes = [] self.search(self.tree) return self.tree_list def search(self, node): if node != None: self.tree_list.append(node.data) self.tree_nodes.append(node) self.search(node.left) self.search(node.right)
binary_search_tree_testcases.py
import unittest
from binary_search_tree import BinarySearchTree
class TestBinarySearchTree(unittest.TestCase):
def __init__(self, *args, **kwargs):
unittest.TestCase.__init__(self, *args, **kwargs)
def setUp(self):
self.bst = BinarySearchTree()
self.node_5 = self.bst.init_node(-5)
self.node_4 = self.bst.init_node(-4)
self.node_3 = self.bst.init_node(-3)
self.node_2 = self.bst.init_node(-2)
self.node_1 = self.bst.init_node(-1)
self.node0 = self.bst.init_node(0)
self.node1 = self.bst.init_node(1)
self.node2 = self.bst.init_node(2)
self.node3 = self.bst.init_node(3)
self.node4 = self.bst.init_node(4)
self.node5 = self.bst.init_node(5)
self.mytree = BinarySearchTree()
self.mytree.insert_data(5)
self.mytree.insert_data(2)
self.mytree.insert_data(3)
self.mytree.insert_data(1)
self.mytree.insert_data(4)
self.mytree.insert_data(0)
self.mytree.insert_data(2.5)
self.mytree.insert_data(8)
self.mytree.insert_data(9)
self.mytree.insert_data(6)
self.mytree.insert_data(4.5)
def test_init_node(self):
node1 = self.bst.init_node(5)
node2 = self.bst.init_node(4)
assert(node1.data == 5)
assert(node1.left == None)
assert(node1.right == None)
assert(node2.data == 4)
assert(node2.left == None)
assert(node2.right == None)
def test_insert(self):
assert(self.bst.tree == None)
self.bst.insert(self.node0)
assert(self.bst.tree.data == self.node0.data)
self.bst.insert(self.node3)
assert(self.bst.tree.right.data == self.node3.data)
self.bst.insert(self.node5)
assert(self.bst.tree.right.right.data == self.node5.data)
self.bst.insert(self.node4)
assert(self.bst.tree.right.right.left.data ==
self.node4.data)
self.bst.insert(self.node2)
assert(self.bst.tree.right.left.data == self.node2.data)
self.bst.insert(self.node1)
assert(self.bst.tree.right.left.left.data == self.node1.data)
self.bst.insert(self.node_4)
assert(self.bst.tree.left.data == self.node_4.data)
self.bst.insert(self.node_5)
assert(self.bst.tree.left.left.data == self.node_5.data)
self.bst.insert(self.node_2)
assert(self.bst.tree.left.right.data == self.node_2.data)
self.bst.insert(self.node_3)
assert(self.bst.tree.left.right.left.data ==
self.node_3.data)
self.bst.insert(self.node_1)
assert(self.bst.tree.left.right.right.data == self.node_1.data)
def test_remove(self):
assert(self.mytree.contains(0) == True)
assert(self.mytree.to_array() == [5, 2, 1, 0, 3, 2.5, 4, 4.5, 8, 6,
9])
self.mytree.remove(0)
assert(self.mytree.contains(0) == False)
assert(self.mytree.to_array() == [5, 2, 1, 3, 2.5, 4, 4.5, 8, 6, 9])
assert(self.mytree.contains(4.5) == True)
self.mytree.remove(4.5)
assert(self.mytree.contains(4.5) == False)
assert(self.mytree.to_array() == [5, 2, 1, 3, 2.5, 4, 8, 6, 9])
self.mytree.insert_data(0)
assert(self.mytree.to_array() == [5, 2, 1, 0, 3, 2.5, 4, 8, 6,
9])
assert(self.mytree.contains(1) == True)
self.mytree.remove(1)
assert(self.mytree.contains(1) == False)
assert(self.mytree.contains(0) == True)
assert(self.mytree.to_array() == [5, 2, 0, 3, 2.5, 4, 8, 6, 9])
# RESET
self.setUp()
assert(self.mytree.to_array() == [5, 2, 1, 0, 3, 2.5, 4, 4.5, 8, 6,
9])
self.mytree.remove(5)
assert(self.mytree.to_array() == [6, 2, 1, 0, 3, 2.5, 4, 4.5, 8, 9]
or
self.mytree.to_array() == [4.5, 2, 1, 0, 3, 2.5, 4, 8, 6, 9])
self.mytree.remove(6)
assert(self.mytree.to_array() == [4.5, 2, 1, 0, 3, 2.5, 4, 8,
9])
self.mytree.remove(4.5)
assert(self.mytree.to_array() == [4, 2, 1, 0, 3, 2.5, 8, 9])
self.mytree.remove(4)
assert(self.mytree.to_array() == [3, 2, 1, 0, 2.5, 8, 9] or
self.mytree.to_array() == [8, 2, 1, 0, 3, 2.5, 9])
self.mytree.remove(3)
assert(self.mytree.to_array() == [2.5, 2, 1, 0, 8, 9] or
self.mytree.to_array() == [8, 2, 1, 0, 2.5, 9])
self.mytree.remove(2.5)
assert(self.mytree.to_array() == [8, 2, 1, 0, 9])
self.mytree.remove(8)
assert(self.mytree.to_array() == [9, 2, 1, 0] or
self.mytree.to_array() == [2, 1, 0, 9])
self.mytree.remove(2)
assert(self.mytree.to_array() == [9, 1, 0] or
self.mytree.to_array() == [1, 0, 9])
self.mytree.remove(1)
assert(self.mytree.to_array() == [9, 0])
self.mytree.remove(0)
assert(self.mytree.to_array() == [9])
# RESET
self.setUp()
self.mytree.remove(4.5)
self.mytree.remove(4)
self.mytree.remove(2.5)
self.mytree.remove(3)
self.mytree.remove(0)
self.mytree.remove(1)
self.mytree.remove(2)
self.mytree.remove(6)
assert(self.mytree.to_array() == [5, 8, 9])
self.mytree.remove(5)
assert(self.mytree.to_array() == [8, 9])
self.mytree.remove(8)
assert(self.mytree.to_array() == [9])
def test_contains(self):
assert(self.mytree.contains(5) == True)
assert(self.mytree.contains(4) == True)
assert(self.mytree.contains(3) == True)
assert(self.mytree.contains(2) == True)
assert(self.mytree.contains(1) == True)
assert(self.mytree.contains(0) == True)
assert(self.mytree.contains(9) == True)
assert(self.mytree.contains(80) == False)
assert(self.mytree.contains(-5) == False)
assert(self.mytree.contains(-10) == False)
assert(self.mytree.contains(230) == False)
assert(self.mytree.contains(340) == False)
def test_get_node(self):
acquired_node0 = self.mytree.get_node(2)
acquired_node1 = self.mytree.get_node(8)
acquired_node2 = self.mytree.get_node(4.5)
acquired_node3 = self.mytree.get_node(45)
assert(acquired_node0.data == 2)
assert(acquired_node0.left.data == 1)
assert(acquired_node0.right.data == 3)
assert(acquired_node1.data == 8)
assert(acquired_node1.left.data == 6)
assert(acquired_node1.right.data == 9)
assert(acquired_node2.data == 4.5)
assert(acquired_node2.left == None)
assert(acquired_node2.right == None)
assert(acquired_node3 == None)
def test_to_array(self):
assert(self.bst.to_array() == [])
self.bst.insert_data(5)
self.bst.insert_data(2)
self.bst.insert_data(8)
self.bst.insert_data(6)
self.bst.insert_data(1)
self.bst.insert_data(3)
self.bst.insert_data(0)
self.bst.insert_data(4)
self.bst.insert_data(9)
assert(self.bst.to_array() == [5, 2, 1, 0, 3, 4, 8, 6, 9])
assert(self.mytree.to_array() == [5, 2, 1, 0, 3, 2.5, 4, 4.5, 8, 6,
9])
def test_size(self):
assert(self.mytree.size() == 11)
self.mytree.remove(4.5)
assert(self.mytree.size() == 10)
self.mytree.remove(4)
assert(self.mytree.size() == 9)
self.mytree.insert_data(30)
assert(self.mytree.size() == 10)
if __name__ == "__main__":
unittest.main()