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.