|
1 |
| -from pydatastructs.utils.misc_util import _check_type, NoneType, TreeNode |
2 |
| -from pydatastructs.linear_data_structures.arrays import ArrayForTrees |
| 1 | +from pydatastructs.utils.misc_util import _check_type, NoneType, TreeNode, BinomialTreeNode |
| 2 | +from pydatastructs.linear_data_structures.arrays import (ArrayForTrees, |
| 3 | + DynamicOneDimensionalArray) |
| 4 | +from pydatastructs.miscellaneous_data_structures.binomial_trees import BinomialTree |
3 | 5 |
|
4 | 6 | __all__ = [
|
5 |
| - 'BinaryHeap' |
| 7 | + 'BinaryHeap', |
| 8 | + 'BinomialHeap' |
6 | 9 | ]
|
7 | 10 |
|
8 |
| -class Heap: |
| 11 | +class Heap(object): |
9 | 12 | """
|
10 | 13 | Abstract class for representing heaps.
|
11 | 14 | """
|
@@ -181,3 +184,217 @@ def __str__(self):
|
181 | 184 | node.key, node.data,
|
182 | 185 | node.right if node.right <= self._last_pos_filled else None)
|
183 | 186 | return str(to_be_printed)
|
| 187 | + |
| 188 | + |
| 189 | +class BinomialHeap(Heap): |
| 190 | + """ |
| 191 | + Represents binomial heap. |
| 192 | +
|
| 193 | + Parameters |
| 194 | + ========== |
| 195 | +
|
| 196 | + root_list: list/tuple |
| 197 | + By default, [] |
| 198 | + The list of BinomialTree object references |
| 199 | + in sorted order. |
| 200 | +
|
| 201 | + Examples |
| 202 | + ======== |
| 203 | +
|
| 204 | + >>> from pydatastructs import BinomialHeap |
| 205 | + >>> b = BinomialHeap() |
| 206 | + >>> b.insert(1, 1) |
| 207 | + >>> b.insert(2, 2) |
| 208 | + >>> b.find_minimum().key |
| 209 | + 1 |
| 210 | + >>> b.find_minimum().children[0].key |
| 211 | + 2 |
| 212 | +
|
| 213 | + References |
| 214 | + ========== |
| 215 | +
|
| 216 | + .. [1] https://en.wikipedia.org/wiki/Binomial_heap |
| 217 | + """ |
| 218 | + __slots__ = ['root_list'] |
| 219 | + |
| 220 | + def __new__(cls, root_list=[]): |
| 221 | + if not all((_check_type(root, BinomialTree)) |
| 222 | + for root in root_list): |
| 223 | + raise TypeError("The root_list should contain " |
| 224 | + "references to objects of BinomialTree.") |
| 225 | + obj = Heap.__new__(cls) |
| 226 | + obj.root_list = DynamicOneDimensionalArray(BinomialTree, root_list) |
| 227 | + return obj |
| 228 | + |
| 229 | + def merge_tree(self, tree1, tree2): |
| 230 | + """ |
| 231 | + Merges two BinomialTree objects. |
| 232 | +
|
| 233 | + Parameters |
| 234 | + ========== |
| 235 | +
|
| 236 | + tree1: BinomialTree |
| 237 | +
|
| 238 | + tree2: BinomialTree |
| 239 | + """ |
| 240 | + if (not _check_type(tree1, BinomialTree)) or \ |
| 241 | + (not _check_type(tree2, BinomialTree)): |
| 242 | + raise TypeError("Both the trees should be of type " |
| 243 | + "BinomalTree.") |
| 244 | + ret_value = None |
| 245 | + if tree1.root.key <= tree2.root.key: |
| 246 | + tree1.add_sub_tree(tree2) |
| 247 | + ret_value = tree1 |
| 248 | + else: |
| 249 | + tree2.add_sub_tree(tree1) |
| 250 | + ret_value = tree2 |
| 251 | + return ret_value |
| 252 | + |
| 253 | + def _merge_heap_last_new_tree(self, new_root_list, new_tree): |
| 254 | + """ |
| 255 | + Merges last tree node in root list with the incoming tree. |
| 256 | + """ |
| 257 | + pos = new_root_list._last_pos_filled |
| 258 | + if (new_root_list.size != 0) and new_root_list[pos].order == new_tree.order: |
| 259 | + new_root_list[pos] = self.merge_tree(new_root_list[pos], new_tree) |
| 260 | + else: |
| 261 | + new_root_list.append(new_tree) |
| 262 | + |
| 263 | + def merge(self, other_heap): |
| 264 | + """ |
| 265 | + Merges current binomial heap with the given binomial heap. |
| 266 | +
|
| 267 | + Parameters |
| 268 | + ========== |
| 269 | +
|
| 270 | + other_heap: BinomialHeap |
| 271 | + """ |
| 272 | + if not _check_type(other_heap, BinomialHeap): |
| 273 | + raise TypeError("Other heap is not of type BinomialHeap.") |
| 274 | + new_root_list = DynamicOneDimensionalArray(BinomialTree, 0) |
| 275 | + i, j = 0, 0 |
| 276 | + while ((i <= self.root_list._last_pos_filled) and |
| 277 | + (j <= other_heap.root_list._last_pos_filled)): |
| 278 | + new_tree = None |
| 279 | + while self.root_list[i] is None: |
| 280 | + i += 1 |
| 281 | + while other_heap.root_list[j] is None: |
| 282 | + j += 1 |
| 283 | + if self.root_list[i].order == other_heap.root_list[j].order: |
| 284 | + new_tree = self.merge_tree(self.root_list[i], |
| 285 | + other_heap.root_list[j]) |
| 286 | + i += 1 |
| 287 | + j += 1 |
| 288 | + else: |
| 289 | + if self.root_list[i].order < other_heap.root_list[j].order: |
| 290 | + new_tree = self.root_list[i] |
| 291 | + i += 1 |
| 292 | + else: |
| 293 | + new_tree = other_heap.root_list[j] |
| 294 | + j += 1 |
| 295 | + self._merge_heap_last_new_tree(new_root_list, new_tree) |
| 296 | + |
| 297 | + while i <= self.root_list._last_pos_filled: |
| 298 | + new_tree = self.root_list[i] |
| 299 | + self._merge_heap_last_new_tree(new_root_list, new_tree) |
| 300 | + i += 1 |
| 301 | + while j <= other_heap.root_list._last_pos_filled: |
| 302 | + new_tree = other_heap.root_list[j] |
| 303 | + self._merge_heap_last_new_tree(new_root_list, new_tree) |
| 304 | + j += 1 |
| 305 | + self.root_list = new_root_list |
| 306 | + |
| 307 | + def insert(self, key, data): |
| 308 | + """ |
| 309 | + Inserts new node with the given key and data. |
| 310 | +
|
| 311 | + key |
| 312 | + The key of the node which can be operated |
| 313 | + upon by relational operators. |
| 314 | +
|
| 315 | + data |
| 316 | + The data to be stored in the new node. |
| 317 | + """ |
| 318 | + new_node = BinomialTreeNode(key, data) |
| 319 | + new_tree = BinomialTree(root=new_node, order=0) |
| 320 | + new_heap = BinomialHeap(root_list=[new_tree]) |
| 321 | + self.merge(new_heap) |
| 322 | + |
| 323 | + def find_minimum(self, **kwargs): |
| 324 | + """ |
| 325 | + Finds the node with the minimum key. |
| 326 | +
|
| 327 | + Returns |
| 328 | + ======= |
| 329 | +
|
| 330 | + min_node: BinomialTreeNode |
| 331 | + """ |
| 332 | + if self.is_empty: |
| 333 | + raise ValueError("Binomial heap is empty.") |
| 334 | + min_node = None |
| 335 | + idx, min_idx = 0, None |
| 336 | + for tree in self.root_list: |
| 337 | + if ((min_node is None) or |
| 338 | + (tree is not None and tree.root is not None and |
| 339 | + min_node.key > tree.root.key)): |
| 340 | + min_node = tree.root |
| 341 | + min_idx = idx |
| 342 | + idx += 1 |
| 343 | + if kwargs.get('get_index', None) is not None: |
| 344 | + return min_node, min_idx |
| 345 | + return min_node |
| 346 | + |
| 347 | + def delete_minimum(self): |
| 348 | + """ |
| 349 | + Deletes the node with minimum key. |
| 350 | + """ |
| 351 | + min_node, min_idx = self.find_minimum(get_index=True) |
| 352 | + child_root_list = [] |
| 353 | + for k, child in enumerate(min_node.children): |
| 354 | + if child is not None: |
| 355 | + child_root_list.append(BinomialTree(root=child, order=k)) |
| 356 | + self.root_list.delete(min_idx) |
| 357 | + child_heap = BinomialHeap(root_list=child_root_list) |
| 358 | + self.merge(child_heap) |
| 359 | + |
| 360 | + @property |
| 361 | + def is_empty(self): |
| 362 | + return self.root_list._last_pos_filled == -1 |
| 363 | + |
| 364 | + def decrease_key(self, node, new_key): |
| 365 | + """ |
| 366 | + Decreases the key of the given node. |
| 367 | +
|
| 368 | + Parameters |
| 369 | + ========== |
| 370 | +
|
| 371 | + node: BinomialTreeNode |
| 372 | + The node whose key is to be reduced. |
| 373 | + new_key |
| 374 | + The new key of the given node, |
| 375 | + should be less than the current key. |
| 376 | + """ |
| 377 | + if node.key <= new_key: |
| 378 | + raise ValueError("The new key " |
| 379 | + "should be less than current node's key.") |
| 380 | + node.key = new_key |
| 381 | + while ((not node.is_root) and |
| 382 | + (node.parent.key > node.key)): |
| 383 | + node.parent.key, node.key = \ |
| 384 | + node.key, node.parent.key |
| 385 | + node.parent.data, node.data = \ |
| 386 | + node.data, node.parent.data |
| 387 | + node = node.parent |
| 388 | + |
| 389 | + def delete(self, node): |
| 390 | + """ |
| 391 | + Deletes the given node. |
| 392 | +
|
| 393 | + Parameters |
| 394 | + ========== |
| 395 | +
|
| 396 | + node: BinomialTreeNode |
| 397 | + The node which is to be deleted. |
| 398 | + """ |
| 399 | + self.decrease_key(node, self.find_minimum().key - 1) |
| 400 | + self.delete_minimum() |
0 commit comments