Mendeteksi imbalance Tree pada Binary Search Tree

Kondisi tree yang balanced akan membuat proses searching pada tree lebih cepat.

Jadi apa yang dimaksud dengan imbalance tree?

Kondisi imbalance atau tidak seimbang dari tree adalah jika salah satu kaki memiliki kedalaman >= 2 dibanding kaki lainnya.

Kondisi unbalanced pada tree dilihat dari perspektif root node. Kita pun bisa melihat imbalance pada tingkat node.

Untuk mendeteksi apakah suatu node balanced atau tidak, kita tambahkan fungsi pada class Node, pada tutorial digunakan nama isBalanced()

Gunakan fungsi height yang sudah kita buat pada modul sebelumnya, untuk menghitung kedalaman node pada sisi kiri dan kanan. Lalu kembalikan nilai boolean dengan melakukan perbandingan abs(leftHeight-rightHeight) < 2

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

Kita implementasikan fungsi diatas pada fungsi print tree untuk menunjukan node mana saja yang unbalanced.

Pertama tambahkan fungsi helper pada Node class, untuk menambahkan tanda asterisk (*)

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

Lalu lakukan modifikasi pada fungsi _nodeToChar(self, n, space) dari class Tree menjadi seperti dibawah. Perhatikan bagian yang diubah adalah n.unbalanced_indikator.

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

Agar fungsi print berjalan baik, kita juga modifikasi fungsi getNodeAtDepth(self, depth, nodes=[]) menjadi seperti berikut:

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

Berikut isi lengkap program tree.py

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)

node = Node(55)

node.left = Node(25)
node.right = Node(70)

node.right.left = Node(60)
node.right.right = Node(90)

node.right.right.left = Node(80)
node.right.right.right = Node(120)

node.right.right.left.right = Node(85)

myTree = Tree(node, 'Basic BST')
myTree.printTree()
$ python .\tree.py
Basic BST 
                              55* 
               /                             \
              25                              70*
       /             \                 /             \
      _               _               60              90
   /     \         /     \         /     \         /     \
  _       _       _       _       _       _       80      120
 / \     / \     / \     / \     / \     / \     / \     / \
_   _   _   _   _   _   _   _   _   _   _   _   _   85  _   _

Dapat dilihat pada node 55 dan 70 ditambahkan tanda asterisk (*), menunjukan node tersebut imbalance.

Sharing is caring:

Leave a Comment