btree_8.py

#! /usr/bin/python3
# ==================================================================
# create a binary tree
#
# The node class in this code has only one data item. It is the
# node's data and also is the node's unique key. In the
# "real world" a node would contain much more data and perhaps
# have a unique key that is seperate from the node's data.
#
# Note: A node's unique key is used to position a node's place
#       in the tree.
# ==================================================================

import numpy as np
from random import randint

class Node:
    def __init__(self, data):
        self.data    = data
        self.left    = None
        self.right   = None
        self.parent  = None

    def PrintNode(self,single_line = True):
        d = 'None' if self.data   == None else self.data
        l = 'None' if self.left   == None else self.left.data
        r = 'None' if self.right  == None else self.right.data
        p = 'None' if self.parent == None else self.parent.data
        if single_line:
            print('(d={},l={},r={},p={})'.format(d,l,r,p))
        else:
            print('data   = {}'.format(d))
            print('left   = {}'.format(l))
            print('right  = {}'.format(r))
            print('parent = {}'.format(p))

class BinaryTree:

    # ---- init object ------------------------------------

    def __init__(self):
        self.root = None

    # ---- find maximum tree height -----------------------

    def height(self):
        return self._height(self.root,0)

    def _height(self,curnode,curheight):
        if curnode == None: return curheight
        left_height = self._height(curnode.left,curheight+1)
        right_height = self._height(curnode.right,curheight+1)
        return max(left_height,right_height)

    # ---- insert object into tree ------------------------

    def insert(self,newnode):
        if self.root == None:
            self.root = newnode
            return True
        else:
            return self._insert(newnode,self.root)

    def _insert(self,newnode,curnode):
        if newnode.data < curnode.data:
            if curnode.left == None:
                newnode.parent = curnode
                curnode.left = newnode
                return True
            else:
                return self._insert(newnode,curnode.left)
        elif newnode.data > curnode.data:
            if curnode.right == None:
                newnode.parent = curnode
                curnode.right = newnode
                return True
            else:
                return self._insert(newnode,curnode.right)
        else:
            print("Tree node {} already in tree".format(curnode.data))
            return False

    # ---- print tree -------------------------------------

    def PrintTree(self):
        if self.root == None:
            print("tree empty")
        else:
            self._PrintTree(self.root)

    def _PrintTreeNode(self,node):
        print("{}".format(str(node.data)))

    def _PrintTree(self,curnode):
        if curnode.left:
            self._PrintTree(curnode.left)
        print("{}".format(curnode.data))
        if curnode.right:
            self._PrintTree(curnode.right)

    # ---- print tree structure ---------------------------

    def PrintTreeStructure(self):
        if self.root == None:
            print("tree empty")
        else:
            self._PrintTreeStructure(self.root,1)

    def _PrintTreeStructure(self,curnode,n):
        if curnode.right:
            self._PrintTreeStructure(curnode.right,n+1)
        pd = 'None' if curnode.parent == None else curnode.parent.data
        print('{} {}  (p={})'.format('---'*n,curnode.data,pd))
        if curnode.left:
            self._PrintTreeStructure(curnode.left,n+1)

    # ---- count all tree nodes ---------------------------

    def count_nodes(self):
        return self._count_nodes(self.root)

    def _count_nodes(self,curnode):
        if curnode == None:
            return 0
        return 1 + self._count_nodes(curnode.left) + \
                   self._count_nodes(curnode.right)


# ------------------------------------------------------------------
# main testing
# ------------------------------------------------------------------

if __name__ == '__main__':

    # ---- create a worst case tree
    def worst_case_tree(tree):
        values = [10,9,7,5,4,3,2,1,11,12,13,14,15,16,17]
        print(values)
        for v in values:
            tree.insert(Node(int(v)))
        return tree

    # ---- create the tree from my documentation
    def my_doc_tree(tree):
        values = [30,40,50,55]
        print(values)
        for v in values:
            tree.insert(Node(int(v)))
        return tree

    # ---- create a tree with random nodes
    def random_nodes(tree,elms=20,maxint=99):
        for _ in range(elms):
            i = randint(1,maxint)
            ##print('RandomInt = {}'.format(i))
            tree.insert(Node(i))

    # ---- create a tree with no root node
    def no_root_node(tree):
        pass

    # ---- create a tree wih only a root node
    def only_root_node(tree):
        tree.insert(Node(100))

    # ---- create a tree wih two nodes
    def root_node_plus_one(tree):
        tree.insert(Node(100))
        tree.insert(Node(200))

    # ---- print all tree nodes
    def print_all_nodes(tree):
        if tree.root == None:
            print("tree empty")
        else:
            _print_all_nodes(tree.root)

    def _print_all_nodes(curnode):
        if curnode.right:
            _print_all_nodes(curnode.right)
        curnode.PrintNode()
        if curnode.left:
            _print_all_nodes(curnode.left)

    print("\n----create-tree--------------------------------------")

    tree = BinaryTree()

    ##no_root_node(tree)
    ##only_root_node(tree)
    ##root_node_plus_one(tree)
    #my_doc_tree(tree)
    worst_case_tree(tree)
    ##random_nodes(tree,20,99)

    print("\n----print-tree-(in-order)----------------------------")

    tree.PrintTree()

    print("\n----max-tree-height----------------------------------")

    print("Maximum height of tree is {}".format(tree.height()))

    print("\n----print-tree-structure-(view-it-sideways)----------")

    tree.PrintTreeStructure()

    print("\n----print-all-nodes----------------------------------")

    print_all_nodes(tree)

    print("\n----count-nodes--------------------------------------")

    print('number of tree nodes = {}'.format(tree.count_nodes()))