Rebalancing Binary Search Tree Menggunakan Python

Melanjutkan dari modul sebelumnya, pada modul ini akan diimplementasikan dalam Python.

Buat fungsi baru diluar class dengan nama rotateRight dan rotateLeft.

def rotateRight(root):
    pivot = root.left
    reattachNode = pivot.right
    root.left = reattachNode
    pivot.right = root
    return pivot

def rotateLeft(root):
    pivot = root.right
    reattachNode = pivot.left
    root.right = reattachNode
    pivot.left = root
    return pivot

Berikut isi lengkap program tree.

class Node:
    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None
    
    def search(self, target):
        if self.data == target:
            print("Node found")
            return self
        
        if self.left and self.data > target:
            return self.left.search(target)

        if self.right and self.data < target:
            return self.right.search(target)

        print("No Node Found")

    def traversePreorder(self):
        print(self.data)
        if self.left:
            self.left.traversePreorder()
        
        if self.right:
            self.right.traversePreorder()

    def traverseInorder(self):
        if self.left:
            self.left.traverseInorder()
        print(self.data)
        if self.right:
            self.right.traverseInorder()

    def traversePostorder(self):
        if self.left:
            self.left.traversePostorder()
        
        if self.right:
            self.right.traversePostorder()
        
        print(self.data)

    def height(self, h=0):
        leftHeight = self.left.height(h+1) if self.left else h
        rightHeight = self.right.height(h+1) if self.right else h
        return max(leftHeight, rightHeight)

    def getNodeAtDepth(self, depth, nodes=[]):
        if depth==0:
            nodes.append(self)
            return nodes

        if self.left:
            self.left.getNodeAtDepth(depth-1, nodes)
        else:
            nodes.extend([None]*2**(depth-1))

        if self.right:
            self.right.getNodeAtDepth(depth-1, nodes)
        else:
            nodes.extend([None]*2**(depth-1))

        return nodes

    def addNode(self, val):
        if self.data == val:
            return
        
        if val < self.data:
            if self.left is None:
                self.left = Node(val)
            else:
                self.left.addNode(val)
        
        if val > self.data:
            if self.right is None:
                self.right = Node(val)
            else:
                self.right.addNode(val)

    def findMinNode(self):
        if self.left:
            return self.left.findMinNode()
            
        return self.data

    def delNode(self, target):
        if self.data == target:
            if self.left and self.right:
                #do RTFM
                minNode = self.right.findMinNode()
                self.data = minNode
                self.right = self.right.delNode(minNode)
                return self
            else:
                return self.left or self.right

        if self.right and target > self.data:
            self.right = self.right.delNode(target)
        
        if self.left and target < self.data:
            self.left = self.left.delNode(target)

        return self

    def isBalanced(self):
        leftHeight = self.left.height()+1 if self.left else 0
        rightHeight = self.right.height()+1 if self.right else 0
        return abs(leftHeight-rightHeight) < 2

    def unbalanced_indikator(self):
        if not self.isBalanced():
            return str(self.data) + "*"
        
        return str(self.data)

class Tree:
    def __init__(self, root, name=''):
        self.root = root
        self.name = name

    def search(self, target):
        return self.root.search(target)

    def traversePreorder(self):
        self.root.traversePreorder()

    def traverseInorder(self):
        self.root.traverseInorder()

    def traversePostorder(self):
        self.root.traversePostorder()

    def height(self):
        return self.root.height()

    def getNodeAtDepth(self, depth):
        return self.root.getNodeAtDepth(depth)

    def _nodeToChar(self, n, space):
        if n is None:
            return '_' + (' '*space)
        space = space - len(n.unbalanced_indikator())+1
        return n.unbalanced_indikator()+(' '*space)

    def printTree(self, label=''):
        print(self.name + ' ' + label)
        height = self.root.height()
        space = 3
        width = int((2**height-1) * (space+1) +1)

        offset = int((width-1)/2)
        for depth in range(0, height+1):
            if depth>0:
                print(' '*(offset+1) + (' '*(space+2)).join(['/' + (' '*(space-2)) + '\\']*(2**(depth-1))))
            row = self.root.getNodeAtDepth(depth, [])
            print ((' '*offset)+ ''.join([self._nodeToChar(n,space) for n in row]))
            space = offset + 1
            offset = int(offset/2)-1
        print ('')

    def addNode(self, val):
        self.root.addNode(val)

    def delNode(self, target):
        self.root = self.root.delNode(target)

def rotateRight(root):
    pivot = root.left
    reattachNode = pivot.right
    root.left = reattachNode
    pivot.right = root
    return pivot

def rotateLeft(root):
    pivot = root.right
    reattachNode = pivot.left
    root.right = reattachNode
    pivot.left = root
    return pivot

unbalancedLL = Tree(Node(30), 'unbalanced left-left')
unbalancedLL.root.left = Node(20)
unbalancedLL.root.right = Node(40)

unbalancedLL.root.left.left = Node(10)
unbalancedLL.root.left.right = Node(25)

unbalancedLL.root.left.left.left = Node(5)
unbalancedLL.root.left.left.right = Node(15)

unbalancedLL.printTree()

unbalancedLL.root = rotateRight(unbalancedLL.root)
unbalancedLL.printTree()


unbalancedRR = Tree(Node(30), 'unbalanced right-right')
unbalancedRR.root.left = Node(20)
unbalancedRR.root.right = Node(40)

unbalancedRR.root.right.left = Node(35)
unbalancedRR.root.right.right = Node(60)

unbalancedRR.root.right.right.left = Node(55)
unbalancedRR.root.right.right.right = Node(75)

unbalancedRR.printTree()

unbalancedRR.root = rotateLeft(unbalancedRR.root)
unbalancedRR.printTree()

Jika dijalankan maka sesuai ekspektasi program diatas berhasil melakukan rebalancing.

$ python tree.py
unbalanced left-left 
              30*
       /             \
      20              40
   /     \         /     \
  10      25      _       _
 / \     / \     / \     / \
5   15  _   _   _   _   _   _

unbalanced left-left
      20
   /     \
  10      30
 / \     / \
5   15  25  40

unbalanced right-right
              30*
       /             \
      20              40
   /     \         /     \
  _       _       35      60
 / \     / \     / \     / \
_   _   _   _   _   _   55  75

unbalanced right-right
      40
   /     \
  30      60
 / \     / \
20  35  55  75

Bagaimana dengan left-right unbalanced tree? Seperti yang sudah dibahas dimodul sebelumnya, kita cukup melakukan rotasi left rotation yang kemudian diikut right rotation.

Perhatikan code dibawah melakukan dua kali rotasi yaitu, unbalancedLR.root.left = rotateLeft(unbalancedLR.root.left) dan unbalancedLR.root = rotateRight(unbalancedLR.root)

unbalancedLR = Tree(Node(30), 'unbalanced left-right')
unbalancedLR.root.left = Node(10)
unbalancedLR.root.right = Node(40)

unbalancedLR.root.left.left = Node(5)
unbalancedLR.root.left.right = Node(20)

unbalancedLR.root.left.right.left = Node(15)
unbalancedLR.root.left.right.right = Node(25)

unbalancedLR.printTree()

unbalancedLR.root.left = rotateLeft(unbalancedLR.root.left)
unbalancedLR.root = rotateRight(unbalancedLR.root)
unbalancedLR.printTree()
PS F:\Project\bst> python tree.py
unbalanced left-right 
              30*
       /             \
      10              40
   /     \         /     \
  5       20      _       _
 / \     / \     / \     / \
_   _   15  25  _   _   _   _

unbalanced left-right
      20
   /     \
  10      30
 / \     / \
5   15  25  40

Untuk Latihan, silakan mencoba untuk melakukan rebalancing right-left unbalanced tree.

Sharing is caring:

Leave a Comment