#!/usr/bin/env python3 # -*- coding: utf-8 -*- # author: bt3gl class SimpleTree: """Implementation of a simple tree""" def __init__(self, value=None, node=None): self.value = value self.node = node or [] def __repr__(self, level=0): repr = '\t' * level + self.value + '\n' for n in self.node: repr += n.__repr__(level + 1) return repr class Node(): """Implementation of a Node for a binary tree""" def __init__(self, value=None): self.value = value self.left = None self.right = None ############################ # Private Methods ############################ def __repr__(self): """Prints the node""" return f'{self.value}' def _repr_preorder(self): """Prints the tree in preorder traversal (root, left, right)""" print(self.value) if self.left: self.left._repr_preorder() if self.right: self.right._repr_preorder() def _add_node_binary_tree(self, value): """Adds a node to a simple binary tree""" if self.value is None: self.value = value else: if not self.left: self.left = Node(value) elif not self.right: self.right = Node(value) else: self.left = self.left._add_node_binary_tree(value) return self def _add_node_binary_search_tree(self, value) -> bool: """Adds a node to a binary search tree""" if not self.value: self.value = value else: new_node = Node(value) if value < self.value: self.left = self.left and self.left._add_node_binary_search_tree(value) or new_node elif value > self.value: self.right = self.right and self.right._add_node_binary_search_tree(value) or new_node else: print(f'āŒ Item {value} not added as BSTs do not support repetition.') return self def _search_node_preorder(self, query) -> bool: """Searches through preorder traversal (node, left, right)""" if self.value == query: return True found = False if self.left: # recursively search left found = self.left._search_node_preorder(query) if self.right: # recursively search right found = found or self.right._search_node_preorder(query) return found def _search_node_binary_search_tree(self, query) -> bool: """Searches the tree for a value, considering the BST property""" this_node_value = self.value if this_node_value is not None: if this_node_value == query: return True elif this_node_value > query: if self.left is not None: return self.left._search_node_binary_search_tree(query) elif this_node_value < query: if self.right is not None: return self.right._search_node_binary_search_tree(query) return False def _is_leaf(self) -> bool: """If node has no children, it is a leaf""" return bool(not self.right and not self.left) def _is_full(self) -> bool: """If node has two children, it is full""" return bool(self.right and self.left) class BinaryTreeInterface(): def __init__(self): self.root = Node() ############################ # Interface Methods ############################ def add_node(self, value): """Adds a new node to the tree""" pass def search_node(self, value): """Searches the tree for a value""" pass ############################ # Public Methods ############################ def is_leaf(self) -> bool: """Returns True if the node is a leaf""" if self.root is not None: return self.root._is_leaf() def is_full(self) -> bool: """Returns True if the node is full""" if self.root is not None: return self.root._is_full() def print_preorder(self): """Prints the BST in preorder""" if self.root is not None: self.root._repr_preorder() ############################ # Class Methods ############################ @classmethod def is_balanced(cls, node, left=0, right=0) -> bool: """Returns True if the tree is balanced""" if node is None: return (left - right) < 2 else: return cls.is_balanced(node.left, left + 1, right) and \ cls.is_balanced(node.right, left, right + 1) @classmethod def is_binary_search_tree(cls, node, min_node=None, max_node=None) -> bool: """Returns True if the tree is a BST""" min_node = min_node or float('-inf') max_node = max_node or float('inf') if not node: return True if node.value < min_node or node.value > max_node: return False return cls.is_binary_search_tree(node.left, min_node, node.value) and \ cls.is_binary_search_tree(node.right, node.value, max_node) class BinaryTree(BinaryTreeInterface): """Implementation of a binary tree""" def add_node(self, value): """Adds a new node to the tree""" if self.root is None: self.root = Node(value) else: self.root._add_node_binary_tree(value) def search_node(self, value): """Searches the tree for a value""" if self.root: return self.root._search_node_preorder(value) class BinarySearchTree(BinaryTreeInterface): def add_node(self, value): """Adds a new node to the tree""" if self.root is None: self.root = Node(value) else: self.root._add_node_binary_search_tree(value) def search_node(self, value): """Searches the tree for a value""" if self.root.value is not None: return self.root._search_node_binary_search_tree(value) @classmethod def largest_node(cls, node): """Returns the largest node in the tree""" if node.right: return cls.largest_node(node.right) else: return node @classmethod def smallest_node(cls, node): """Returns the smallest node in the tree""" if node.left: return cls.smallest_node(node.left) else: return node if __name__ == '__main__': ############################ # Test SimpleTree ############################ print("\n\n🌓🌓🌓 Testing SimpleTree 🌓🌓🌓") t = SimpleTree('a', [SimpleTree('b', [SimpleTree('d'), SimpleTree('e')]), \ SimpleTree('c', [SimpleTree('h'), SimpleTree('g')])]) print(t) ############################ # Test binary tree ############################ print("\n\n🌳🌳🌳 Testing BinaryTree 🌳🌳🌳") bt = BinaryTree() array1 = [4, 1, 4, 6, 7, 9, 10, 5, 11, 5] print(f'\n🟔 Adding {array1} to the tree...') for i in array1: bt.add_node(i) print("🟢 Printing the tree in preorder...") bt.print_preorder() print(f'\n🟢 Searching for node 5: {bt.search_node(5)}') print(f'āŒ Searching for node 15: {bt.search_node(15)}') print(f'āŒ Is root a leaf? {bt.is_leaf()}') print(f'🟢 Is root full? {bt.is_full()}') print(f'āŒ Is the tree balanced? {BinaryTree.is_balanced(bt.root)}') print(f'āŒ Is the tree a binary search tree? {BinaryTree.is_binary_search_tree(bt.root)}') ############################## # Test binary search tree ############################## print("\n\nšŸŽ„šŸŽ„šŸŽ„ Testing BinarySearchTree šŸŽ„šŸŽ„šŸŽ„") bst = BinarySearchTree() array1 = [4, 1, 4, 6, 7, 9, 10, 5, 11, 5] print(f'\n🟔 Adding {array1} to the tree...') for i in array1: bst.add_node(i) print("🟢 Printing the tree in preorder:") bst.print_preorder() print(f'\n🟢 Searching for node 5: {bst.search_node(5)}') print(f'āŒ Searching for node 15: {bst.search_node(15)}') print(f'āŒ Is root a leaf? {bst.is_leaf()}') print(f'🟢 Is root full? {bst.is_full()}') print(f'🟢 Largest node? {bst.largest_node(bst.root)}') print(f'🟢 Smallest node? {bst.smallest_node(bst.root)}') print(f'āŒ Is the tree balanced? {bst.is_balanced(bst.root)}') print(f'🟢 Is the tree a binary search tree? {bst.is_binary_search_tree(bst.root)}')