|
| 1 | +# Copyright (C) Deepali Srivastava - All Rights Reserved |
| 2 | +# This code is part of DSA course available on CourseGalaxy.com |
| 3 | + |
| 4 | +from math import ceil |
| 5 | + |
| 6 | +class Node: |
| 7 | + def __init__(self,size): |
| 8 | + self.numKeys = 0 |
| 9 | + self.key = [None] * size |
| 10 | + self.child = [None] * size |
| 11 | + |
| 12 | +class BTree: |
| 13 | + M = 5 |
| 14 | + MAX = M - 1 |
| 15 | + MIN = ceil(M/2) - 1 |
| 16 | + |
| 17 | + |
| 18 | + def __init__(self): |
| 19 | + self.root = None |
| 20 | + |
| 21 | + def isEmpty(self): |
| 22 | + return self.root == None |
| 23 | + |
| 24 | + def search(self, x): |
| 25 | + if self._search(x, self.root) == None: |
| 26 | + return False |
| 27 | + return True |
| 28 | + |
| 29 | + def _search(self, x, p): |
| 30 | + if p == None : # Key x not present in the tree |
| 31 | + return None |
| 32 | + |
| 33 | + flag, n = self._searchNode(x, p) |
| 34 | + if flag == True: # Key x found in node p |
| 35 | + return p |
| 36 | + |
| 37 | + return self._search(x, p.child[n]) # Search in node p.child[n] |
| 38 | + |
| 39 | + |
| 40 | + def _searchNode(self, x, p): |
| 41 | + if x < p.key[1] : # key x is less than leftmost key |
| 42 | + return False,0 |
| 43 | + |
| 44 | + n = p.numKeys |
| 45 | + while (x < p.key[n]) and n > 1 : |
| 46 | + n -= 1 |
| 47 | + |
| 48 | + if x == p.key[n]: |
| 49 | + return True,n |
| 50 | + else: |
| 51 | + return False,n |
| 52 | + |
| 53 | + |
| 54 | + def inorder(self): |
| 55 | + self._inorder(self.root) |
| 56 | + print() |
| 57 | + |
| 58 | + |
| 59 | + def _inorder(self, p): |
| 60 | + if p == None: |
| 61 | + return |
| 62 | + |
| 63 | + i = 0 |
| 64 | + while i < p.numKeys: |
| 65 | + self._inorder(p.child[i]) |
| 66 | + print(p.key[i+1] , end = " ") |
| 67 | + i += 1 |
| 68 | + |
| 69 | + self._inorder( p.child[i] ) |
| 70 | + |
| 71 | + |
| 72 | + def display(self): |
| 73 | + self._display(self.root, 0) |
| 74 | + |
| 75 | + |
| 76 | + def _display(self, p, blanks): |
| 77 | + if p != None : |
| 78 | + for i in range(blanks): |
| 79 | + print(end = " ") |
| 80 | + |
| 81 | + for i in range(1,p.numKeys+1): |
| 82 | + print(p.key[i] ,end=" ") |
| 83 | + print() |
| 84 | + |
| 85 | + for i in range(p.numKeys+1): |
| 86 | + self._display(p.child[i], blanks + 10) |
| 87 | + |
| 88 | + |
| 89 | + def insert(self, x): |
| 90 | + |
| 91 | + taller, iKey, iKeyRchild = self._insert(x, self.root) |
| 92 | + |
| 93 | + if taller: # Height increased by one, new root node has to be created |
| 94 | + temp = Node(BTree.M) |
| 95 | + temp.child[0] = self.root |
| 96 | + self.root = temp |
| 97 | + |
| 98 | + self.root.numKeys = 1 |
| 99 | + self.root.key[1] = iKey |
| 100 | + self.root.child[1] = iKeyRchild |
| 101 | + |
| 102 | + |
| 103 | + def _insert(self, x, p): |
| 104 | + |
| 105 | + if p == None: #First Base case : key not found |
| 106 | + return True, x, None |
| 107 | + |
| 108 | + flag, n = self._searchNode(x, p) |
| 109 | + |
| 110 | + if flag == True : #Second Base Case : key found |
| 111 | + print("Key already present in the tree") |
| 112 | + return False, 0 , None #No need to insert the key |
| 113 | + |
| 114 | + |
| 115 | + flag, iKey, iKeyRchild = self._insert(x, p.child[n]) |
| 116 | + |
| 117 | + if flag == True: |
| 118 | + if p.numKeys < BTree.MAX: |
| 119 | + self._insertByShift(p, n, iKey, iKeyRchild) |
| 120 | + return False,iKey, iKeyRchild #Insertion over |
| 121 | + else: |
| 122 | + iKey, iKeyRchild = self._split(p, n, iKey, iKeyRchild ) |
| 123 | + return True,iKey, iKeyRchild #Insertion not over : Median key yet to be inserted |
| 124 | + return False, iKey, iKeyRchild |
| 125 | + |
| 126 | + |
| 127 | + def _insertByShift(self, p, n, iKey, iKeyRchild): |
| 128 | + i = p.numKeys |
| 129 | + while i > n: |
| 130 | + p.key[i+1] = p.key[i] |
| 131 | + p.child[i+1] = p.child[i] |
| 132 | + i -= 1 |
| 133 | + |
| 134 | + p.key[n+1] = iKey |
| 135 | + p.child[n+1] = iKeyRchild |
| 136 | + p.numKeys += 1 |
| 137 | + |
| 138 | + |
| 139 | + def _split(self, p, n,iKey, iKeyRchild): |
| 140 | + if n == BTree.MAX: |
| 141 | + lastKey = iKey |
| 142 | + lastChild = iKeyRchild |
| 143 | + else: |
| 144 | + lastKey = p.key[BTree.MAX] |
| 145 | + lastChild = p.child[BTree.MAX] |
| 146 | + |
| 147 | + i = p.numKeys-1 |
| 148 | + while i>n : |
| 149 | + p.key[i + 1] = p.key[i] |
| 150 | + p.child[i + 1] = p.child[i] |
| 151 | + i -= 1 |
| 152 | + |
| 153 | + p.key[i+1] = iKey |
| 154 | + p.child[i+1] = iKeyRchild |
| 155 | + |
| 156 | + |
| 157 | + d = (BTree.M + 1) // 2 |
| 158 | + medianKey = p.key[d] |
| 159 | + newNode = Node(BTree.M) |
| 160 | + newNode.numKeys = BTree.M - d |
| 161 | + |
| 162 | + newNode.child[0] = p.child[d] |
| 163 | + i=1 |
| 164 | + j=d+1 |
| 165 | + |
| 166 | + while j <= BTree.MAX : |
| 167 | + newNode.key[i] = p.key[j] |
| 168 | + newNode.child[i] = p.child[j] |
| 169 | + i += 1 |
| 170 | + j += 1 |
| 171 | + |
| 172 | + newNode.key[i] = lastKey |
| 173 | + newNode.child[i] = lastChild |
| 174 | + |
| 175 | + p.numKeys = d - 1 |
| 176 | + |
| 177 | + iKey = medianKey |
| 178 | + iKeyRchild = newNode |
| 179 | + |
| 180 | + return iKey, iKeyRchild |
| 181 | + |
| 182 | + |
| 183 | + def delete(self, x): |
| 184 | + if self.root == None : |
| 185 | + print("Tree is empty") |
| 186 | + return |
| 187 | + |
| 188 | + self._delete(x, self.root) |
| 189 | + |
| 190 | + if self.root != None and self.root.numKeys == 0: #Height of tree decreased by 1 |
| 191 | + self.root = self.root.child[0] |
| 192 | + |
| 193 | + |
| 194 | + def _delete(self, x, p): |
| 195 | + |
| 196 | + flag, n = self._searchNode(x, p) |
| 197 | + |
| 198 | + if flag == True: # Key x found in node p |
| 199 | + if p.child[n] == None: # Node p is a leaf node |
| 200 | + self._deleteByShift(p, n) |
| 201 | + return |
| 202 | + else: # Node p is a non-leaf node |
| 203 | + s = p.child[n] |
| 204 | + while s.child[0] != None: |
| 205 | + s = s.child[0] |
| 206 | + p.key[n] = s.key[1] |
| 207 | + self._delete(s.key[1], p.child[n]) |
| 208 | + else: # Key x not found in node p |
| 209 | + if p.child[n] == None : # p is a leaf node |
| 210 | + print("Value", x , " not present in the tree") |
| 211 | + return |
| 212 | + else: # p is a non-leaf node |
| 213 | + self._delete(x, p.child[n]) |
| 214 | + |
| 215 | + if p.child[n].numKeys < BTree.MIN : |
| 216 | + self._restore(p,n) |
| 217 | + |
| 218 | + |
| 219 | + def _deleteByShift(self, p, n): |
| 220 | + for i in range(n+1,p.numKeys+1): |
| 221 | + p.key[i-1] = p.key[i] |
| 222 | + p.child[i-1] = p.child[i] |
| 223 | + p.numKeys -= 1 |
| 224 | + |
| 225 | + |
| 226 | + # Called when p.child[n] becomes underflow |
| 227 | + def _restore(self, p, n): |
| 228 | + if n!=0 and p.child[n-1].numKeys > BTree.MIN : |
| 229 | + self._borrowLeft(p, n) |
| 230 | + elif n!=p.numKeys and p.child[n+1].numKeys > BTree.MIN: |
| 231 | + self._borrowRight(p, n) |
| 232 | + else: |
| 233 | + if n!=0 : #if there is a left sibling |
| 234 | + self._combine(p, n) # combine with left sibling |
| 235 | + else: |
| 236 | + self._combine(p, n+1) # combine with right sibling |
| 237 | + |
| 238 | + |
| 239 | + def _borrowLeft(self, p, n): |
| 240 | + underflowNode = p.child[n] |
| 241 | + leftSibling = p.child[n - 1] |
| 242 | + |
| 243 | + underflowNode.numKeys += 1 |
| 244 | + |
| 245 | + #Shift all the keys and children in underflowNode one position right |
| 246 | + for i in range(underflowNode.numKeys, 0, -1): |
| 247 | + underflowNode.key[i+1] = underflowNode.key[i] |
| 248 | + underflowNode.child[i+1] = underflowNode.child[i] |
| 249 | + |
| 250 | + underflowNode.child[1] = underflowNode.child[0] |
| 251 | + |
| 252 | + # Move the separator key from parent node p to underflowNode |
| 253 | + underflowNode.key[1] = p.key[n] |
| 254 | + |
| 255 | + # Move the rightmost key of node leftSibling to the parent node p |
| 256 | + p.key[n] = leftSibling.key[leftSibling.numKeys] |
| 257 | + |
| 258 | + #Rightmost child of leftSibling becomes leftmost child of underflowNode |
| 259 | + underflowNode.child[0] = leftSibling.child[leftSibling.numKeys] |
| 260 | + |
| 261 | + leftSibling.numKeys -= 1 |
| 262 | + |
| 263 | + |
| 264 | + def _borrowRight(self, p, n): |
| 265 | + underflowNode = p.child[n] |
| 266 | + rightSibling = p.child[n+1] |
| 267 | + |
| 268 | + # Move the separator key from parent node p to underflowNode |
| 269 | + underflowNode.numKeys += 1 |
| 270 | + underflowNode.key[underflowNode.numKeys] = p.key[n+1] |
| 271 | + |
| 272 | + # Leftmost child of rightSibling becomes the rightmost child of underflowNode |
| 273 | + underflowNode.child[underflowNode.numKeys] = rightSibling.child[0] |
| 274 | + |
| 275 | + rightSibling.numKeys -= 1 |
| 276 | + |
| 277 | + # Move the leftmost key from rightSibling to parent node p |
| 278 | + p.key[n+1] = rightSibling.key[1] |
| 279 | + |
| 280 | + # Shift all the keys and children of rightSibling one position left |
| 281 | + rightSibling.child[0] = rightSibling.child[1] |
| 282 | + for i in range(1,rightSibling.numKeys+1): |
| 283 | + rightSibling.key[i] = rightSibling.key[i + 1] |
| 284 | + rightSibling.child[i] = rightSibling.child[i + 1] |
| 285 | + |
| 286 | + |
| 287 | + def _combine(self, p, m): |
| 288 | + nodeA = p.child[m-1] |
| 289 | + nodeB = p.child[m] |
| 290 | + |
| 291 | + nodeA.numKeys += 1 |
| 292 | + |
| 293 | + # Move the separator key from the parent node p to nodeA |
| 294 | + nodeA.key[nodeA.numKeys] = p.key[m] |
| 295 | + |
| 296 | + # Shift the keys and children that are after separator key in node p one position left |
| 297 | + for i in range(m, p.numKeys): |
| 298 | + p.key[i] = p.key[i+1] |
| 299 | + p.child[i] = p.child[i+1] |
| 300 | + |
| 301 | + p.numKeys -= 1 |
| 302 | + |
| 303 | + # Leftmost child of nodeB becomes rightmost child of nodeA |
| 304 | + nodeA.child[nodeA.numKeys] = nodeB.child[0] |
| 305 | + |
| 306 | + # Insert all the keys and children of nodeB at the end of nodeA |
| 307 | + for i in range(1, nodeB.numKeys+1): |
| 308 | + nodeA.numKeys += 1 |
| 309 | + nodeA.key[nodeA.numKeys] = nodeB.key[i] |
| 310 | + nodeA.child[nodeA.numKeys] = nodeB.child[i] |
| 311 | + |
| 312 | + |
| 313 | +############################################################################################## |
| 314 | + |
| 315 | +if __name__ == '__main__': |
| 316 | + |
| 317 | + tree = BTree() |
| 318 | + |
| 319 | + while True: |
| 320 | + |
| 321 | + print("1.Search") |
| 322 | + print("2.Insert") |
| 323 | + print("3.Delete") |
| 324 | + print("4.Display") |
| 325 | + print("5.Inorder Traversal") |
| 326 | + print("6.Quit") |
| 327 | + choice = int(input("Enter your choice : ")) |
| 328 | + |
| 329 | + if choice == 1: |
| 330 | + key = int(input("Enter the key to be searched : ")) |
| 331 | + if tree.search(key) == True: |
| 332 | + print("Key found") |
| 333 | + else: |
| 334 | + print("Key not found") |
| 335 | + elif choice == 2: |
| 336 | + key = int(input("Enter the key to be inserted : ")) |
| 337 | + tree.insert(key) |
| 338 | + elif choice == 3: |
| 339 | + key = int(input("Enter the element to be deleted : ")) |
| 340 | + tree.delete(key) |
| 341 | + elif choice == 4: |
| 342 | + tree.display() |
| 343 | + elif choice == 5: |
| 344 | + tree.inorder() |
| 345 | + elif choice == 6: |
| 346 | + break |
| 347 | + else: |
| 348 | + print("Wrong choice") |
| 349 | + print() |
| 350 | + |
0 commit comments