-1

I've got code to implement a red-black tree in python. However, when I run my code, it seems all it does is insert the values as if it were a regular BST, then colours all of the internal vertices red. This is a practise assignment for homework, but I'm stuck and have no where else to turn to.

Here is my implementation:

class BinaryTreeVertex(object):
    def __init__(self, data=None, isALeaf=True, colour='r', left=None, right=None):
        # Assume user will pass a left & right ONLY IF isALeaf=False
        self.data = data
        self.isALeaf = isALeaf
        self.colour = colour
        self.left = left
        self.right = right

class BinarySearchTree(object):
    def __init__(self, root=None):
        self.root = BinaryTreeVertex()

    def insert(self, value):
        self.root = recInsert(self.root, value)
        self.root.colour = 'b'

    def searchPath(self, val):
        path = []
        node = self.root
        while node is not None:
            path.append(node.data)
            if val < node.data:
                node = node.left
            elif val > node.data:
                node = node.right
            else:
                node = None
        return path

    def totalDepth(self):
        if self.root.isALeaf:
            return 0
        else:
            return recTotalDepth(self.root)

# returns the depth of a given node
# plus it's children's depths
def recTotalDepth(node, currDepth=0):
    if node.isALeaf:
        return 0
    else:
        leftDepth = 0
        rightDepth = 0
        if node.left is not None:
            leftDepth = recTotalDepth(node.left, currDepth+1)
        if node.right is not None:
            rightDepth = recTotalDepth(node.right, currDepth+1)

        return leftDepth + currDepth + rightDepth

# print a sideways representation of the BinarySearchTree
# Up = right, down = left
def printTree(node, indent=0):
    if node.isALeaf:
        return
    else:
        if node.right is not None:
            printTree(node.right, indent+4)
        print(" "*indent + str(node.data) + node.colour)
        if node.left is not None:
            printTree(node.left, indent+4)


# Insert a value into the binary search tree
def recInsert(node, value):
        if node.isALeaf:
            # Set the data to value, the colour to red, give the vertex two
            # empty leaves coloured black
            return BinaryTreeVertex(value, False, 'r', BinaryTreeVertex(None, True, 'b'), BinaryTreeVertex(None, True, 'b'))
        elif value > node.data:
            node.right = recInsert(node.right, value)
            # check for balance
            if node.colour == 'r':
                # no balance check @ red vertices
                return node
            else: # node.colour is black
                # Grandparents/Parents will handle balance of red vertices
                # We must only perform balances at black vertices
                if node.right.colour == 'r':
                    # right child is red, possibility for red-red conflict
                    if node.right.right.colour == 'r':
                        # red-red conflict on the right-right children
                        return rightRightFix(node)
                    elif node.right.left.colour == 'r':
                        # red-red conflict on the right-left children
                        return rightLeftFix(node)
                    else:
                        # no red-red conflicts
                        return node
                else:
                    # right child is black, no fixing needed
                    return node
        else: # value < self.data
            node.left = recInsert(node.left, value)
            # check for balance
            if node.colour == 'r':
                # no balance checks @ red vertices
                return node
            else: # node.colour == 'b'
                # Grandparents/Parents will handle balance of red vertices
                # We must only perform balances at black vertices
                if node.left.colour == 'r':
                    # left child is red, possibility for red-red conflict
                    if node.left.left.colour == 'r':
                        # red-red conflict on the left-left children
                        return leftLeftFix(node)
                    elif node.left.right.colour == 'r':
                        # red-red conflict on the left-right children
                        return leftRightFix(node)
                    else:
                        # no red-red conflicts
                        return node
                else:
                    # left child is black, no fixing needed
                    return node

def rightRightFix(node):
    # red-red conflict on right-right children
    child = node.right
    sib = node.left
    if sib.colour == 'r':
        # no need for rotation, just recolour
        child.colour == 'b'
        sib.colour == 'b'
        node.colour == 'r'
        return node
    else:
        # sib's colour is black, single rot
        # fix pointers first
        node.right = child.left
        child.left = node
        # fix colours
        child.colour = 'b'
        node.colour = 'r'
        # make the new root, which is now child
        return child

def rightLeftFix(node):
    # red-red conflict on right-left children
    child = node.right
    sib = node.left
    if sib.colour == 'r':
        # no need for rotation, just recolour
        child.colour == 'b'
        sib.colour == 'b'
        node.colour == 'r'
        return node
    else:
        # sib's colour is black, double rot
        # fix the pointers
        grandchild = child.left
        child.left = grandchild.right
        node.right = grandchild.left
        grandchild.left = node
        grandchild.right = child
        # fix the colours
        grandchild.colour == 'b'
        node.colour = 'r'
        # return the new root, which is now grandchild
        return grandchild

def leftLeftFix(node):
    # red-red conflict on right-right children
    child = node.left
    sib = node.right
    if sib.colour == 'r':
        # no need for rotation, just recolour
        child.colour == 'b'
        sib.colour == 'b'
        node.colour == 'r'
        return node
    else:
        # sib's colour is black, single rot
        # fix pointers first
        node.left = child.right
        child.right = node
        # fix colours
        child.colour = 'b'
        node.colour = 'r'
        # make the new root, which is now child
        return child

def leftRightFix(node):
    # red-red conflict on left-right children
    child = node.left
    sib = node.right
    if sib.colour == 'r':
        # no need for rotation, just recolour
        child.colour == 'b'
        sib.colour == 'b'
        node.colour == 'r'
        return node
    else:
        # sib's colour is black, double rot
        # fix the pointers
        grandchild = child.right
        child.right = grandchild.left
        node.left = grandchild.right
        grandchild.right = node
        grandchild.left = child
        # fix the colours
        grandchild.colour == 'b'
        node.colour = 'r'
        # return the new root, which is now grandchild
        return grandchild


def main():
    myTree = BinarySearchTree()
    myList = [13,42,3,6,23,32,72,90,1,10,26,85,88,97,73,80,35,36,88,34,12,92,100,143,123,124,125,126,127,128]


    for v in myList:
        myTree.insert(v)

    printTree(myTree.root)
    print(myTree.searchPath(12))

    print(myTree.totalDepth())

    myTree2 = BinarySearchTree()
    myList2 = [6, 10, 20, 8, 3]


    for v in myList2:
        myTree2.insert(v)

    printTree(myTree2.root)
    print(myTree2.searchPath(6))

    print(myTree2.totalDepth())

main()
Yaman Jain
  • 1,254
  • 11
  • 16
rafro4
  • 65
  • 7
  • A great way to locate the bug is to write test cases for your code. Start by writing test the simplest case then once that works add more tests for more complex / corner cases – Brett Y Feb 27 '17 at 04:13
  • 1
    So from my simple testing, I'm gathering that my 'fix' functions don't actually recolour my vertices. – rafro4 Feb 27 '17 at 04:20

1 Answers1

1

The main problem in the provided source to explain why your algorithm "don't actually recolour my vertices" is due to a bad assignment.

In all functions rightRightFix(), rightLeftFix(), leftLeftFix() and leftRightFix(), mysteriously some colour assignment (child.colour = 'b') have been coded as comparison (child.colour == 'b').

Solution - just replace the 14 faulty copy/paste assignments error.

After correcting that, your Red-Black Tree algorithm works perfectly.

Minor Error - in the BinarySearchTree::searchPath() when value is not found.

When the searched value doesn't exists, you get:

TypeError: unorderable types: int() < NoneType()

It is due to the use of not initialize Leaf node.

def searchPath(self, val):
    path = []
    node = self.root
    while node is not None:
        path.append(node.data)
        # break the while when node is the leave
        if (node.isALeaf == True):
            break
        if val < node.data:
            node = node.left
        elif val > node.data:
            node = node.right
        else:
            node = None
    return path

In that case, the returned search path will be ended by 'None' => Not found

Input: print(myTree.searchPath(102))

Output: [72, 88, 100, 124, 123, None]

Community
  • 1
  • 1
J. Piquard
  • 1,665
  • 2
  • 12
  • 17