pythondata-structurestreebinary-treerecursive-datastructures

Inserting a node in a complete binary tree with python


How to insert a node in a complete binary tree without using queue DS? I tried the following code:

class TreeNode:
    def __init__(self, value=None) -> None:
        self.left = None
        self.value = value
        self.right = None

class Tree:
    def __init__(self, root=None) -> None:
        self.__root = root if not root else TreeNode(root)
        self.__len = 1 if root else 0

    def append(self, data, root="_"):
        if self.__root.value is None:
            self.__root = TreeNode(data)
            self.__len += 1
            return
        root = self.__root if root == "_" else root
        if not root:
            return False
        if root.left is None and root.right is None:
            root.left = TreeNode(data)
            self.__len += 1
            return True
        elif root.left is not None and root.right is None:
            root.right = TreeNode(data)
            self.__len += 1
            return True
        elif root.left is not None and root.right is not None:
            if self.append(data, root.left):
                return
            else:
                self.append(data, root.right)
                return

the recursive call of that function always add the new node on the left side of the tree, so what should I do to make it recursively checks the right side too?


Solution

  • First of all, the first line in your append code seems to give a special meaning to the value in the root node. When it is None it is not considered a real node, but to represent an empty tree. This is not a good approach. An empty tree is represented by a __root that is None -- nothing else. I would also suggest to remove the optional data argument from the constructor. Either the constructor should allow an arbitrary number of values or none at all. To allow one is odd and strengthens the idea that a tree could have a special None in its root node.

    To the core of your question. There is nice attribute to complete binary trees. The paths from the root to a leaf can be represented by a bit pattern, where a 0 means "go left" and a 1 means "go right". And the path to the "slot" where a new node should be injected has a relationship with the size of the tree once that node has been added:

    new size binary representation path to new node
    1 1 []
    2 10 [left]
    3 11 [right]
    4 100 [left,left]
    5 101 [left,right]

    In general the path to the new node is defined by the bits in the binary representation of the new tree size, ignoring the leftmost 1.

    This leads to the following code:

    class Tree:
        def __init__(self) -> None:
            self.__root = None
            self.__len = 0
    
        def append(self, data):
            self.__len += 1
            if self.__len == 1:  # First node
                self.__root = TreeNode(data)
                return
            node = self.__root
            # Iterate all the bits except the leftmost 1 and the final bit
            for bit in bin(self.__len)[3:-1]:
                node = [node.left, node.right][int(bit)]  # Choose side
            if self.__len & 1:  # Use final bit to determine where child goes:
                node.right = TreeNode(data)
            else:
                node.left = TreeNode(data)