Binary Search Tree

The tricky one is deletion. The idea is the following:

Case #1: node is a leaf:
    if parent:
        link parent.left or parent.right to None
     else:
        delete root key

Case #2: node has one child:
     find the successor
     if parent:
         link parent with successor
     else:
         root node key = successor.key
         set root child to None

Case #3: node has two children:
     find successor (smallest node bigger than node)
     set node.key = successor.key
     (successor's parent).left/right = successor.right

And here we go with the code.

def preorder(tree):
    if tree:
        print tree.key,
        preorder(tree.left)
        preorder(tree.right)


class Node(object):
    def __init__(self, value):
        self.key = value
        self.left = None
        self.right = None


    def insert(self, value):
        if value < self.key:
            if self.left is None:
                self.left = Node(value)
            else:
                self.left.insert(value)
                
        elif value > self.key:
            if self.right is None:
                self.right = Node(value)
            else:
                self.right.insert(value)


    # the straightforward version
    def lookup(self, value):
        if self.key == value:
            return self
        elif value < self.key:
            if self.left is None:
                return None
            return self.left.lookup(value)
        else:
            if self.right is None:
                return None
            return self.right.lookup(value)


    # returns both node and parent node; used for delete()
    def lookupParent(self, value, parent=None):
        if self.key == value:
            return self, parent
        elif value < self.key:
            if self.left is None:
                return None, None
            return self.left.lookupParent(value, self)
        elif value > self.key:
            if self.right is None:
                return None, None
            return self.right.lookupParent(value, self)


    # recursive version
    def findMin(self):
        if self.left is None:
            return self

        return self.left.findMin()

    
    # iterative version
    def findMax(self):
        maxNode = self
        while maxNode.right is not None:
            maxNode = maxNode.right

        return maxNode


    def delete(self, value):
        node, parent = self.lookupParent(value)

        # key does not exist
        if node is None:
            return

        # CASE #1, node is a leaf
        if node.right is None and node.left is None:
            if parent:
                if parent.left is node:
                    parent.left = None
                else:
                    parent.right = None
            # key is in the root node
            else:                
                self.key = None

        # CASE #2, node has one child
        elif node.right is None or node.left is None:
            # find the successor
            if node.left:
                successor = node.left
            else:
                successor = node.right
            # if there is a parent, link parent with successor
            if parent:
                if parent.left is node:
                    parent.left = successor
                else:
                    parent.right = successor
            # key is in the root node
            else:
                node.key = successor.key
                node.left = None
                node.right = None

        # CASE #3, node has two childs
        else:
            parent = node
            successor = node.right
            while successor.left:
                parent = successor
                successor = successor.left

            node.key = successor.key

            # fix successor's parent's child
            if parent.left == successor:
                parent.left = successor.right
            else:
                parent.right = successor.right



root = Node(10)
root.insert(3)
root.insert(44)
root.insert(1)
root.insert(5)
root.insert(4)
root.insert(6)
root.insert(99)

preorder(root)
print
root.delete(3)
print
preorder(root)