| | """ |
| | Min-heaps. |
| | """ |
| |
|
| | from heapq import heappop, heappush |
| | from itertools import count |
| |
|
| | import networkx as nx |
| |
|
| | __all__ = ["MinHeap", "PairingHeap", "BinaryHeap"] |
| |
|
| |
|
| | class MinHeap: |
| | """Base class for min-heaps. |
| | |
| | A MinHeap stores a collection of key-value pairs ordered by their values. |
| | It supports querying the minimum pair, inserting a new pair, decreasing the |
| | value in an existing pair and deleting the minimum pair. |
| | """ |
| |
|
| | class _Item: |
| | """Used by subclassess to represent a key-value pair.""" |
| |
|
| | __slots__ = ("key", "value") |
| |
|
| | def __init__(self, key, value): |
| | self.key = key |
| | self.value = value |
| |
|
| | def __repr__(self): |
| | return repr((self.key, self.value)) |
| |
|
| | def __init__(self): |
| | """Initialize a new min-heap.""" |
| | self._dict = {} |
| |
|
| | def min(self): |
| | """Query the minimum key-value pair. |
| | |
| | Returns |
| | ------- |
| | key, value : tuple |
| | The key-value pair with the minimum value in the heap. |
| | |
| | Raises |
| | ------ |
| | NetworkXError |
| | If the heap is empty. |
| | """ |
| | raise NotImplementedError |
| |
|
| | def pop(self): |
| | """Delete the minimum pair in the heap. |
| | |
| | Returns |
| | ------- |
| | key, value : tuple |
| | The key-value pair with the minimum value in the heap. |
| | |
| | Raises |
| | ------ |
| | NetworkXError |
| | If the heap is empty. |
| | """ |
| | raise NotImplementedError |
| |
|
| | def get(self, key, default=None): |
| | """Returns the value associated with a key. |
| | |
| | Parameters |
| | ---------- |
| | key : hashable object |
| | The key to be looked up. |
| | |
| | default : object |
| | Default value to return if the key is not present in the heap. |
| | Default value: None. |
| | |
| | Returns |
| | ------- |
| | value : object. |
| | The value associated with the key. |
| | """ |
| | raise NotImplementedError |
| |
|
| | def insert(self, key, value, allow_increase=False): |
| | """Insert a new key-value pair or modify the value in an existing |
| | pair. |
| | |
| | Parameters |
| | ---------- |
| | key : hashable object |
| | The key. |
| | |
| | value : object comparable with existing values. |
| | The value. |
| | |
| | allow_increase : bool |
| | Whether the value is allowed to increase. If False, attempts to |
| | increase an existing value have no effect. Default value: False. |
| | |
| | Returns |
| | ------- |
| | decreased : bool |
| | True if a pair is inserted or the existing value is decreased. |
| | """ |
| | raise NotImplementedError |
| |
|
| | def __nonzero__(self): |
| | """Returns whether the heap if empty.""" |
| | return bool(self._dict) |
| |
|
| | def __bool__(self): |
| | """Returns whether the heap if empty.""" |
| | return bool(self._dict) |
| |
|
| | def __len__(self): |
| | """Returns the number of key-value pairs in the heap.""" |
| | return len(self._dict) |
| |
|
| | def __contains__(self, key): |
| | """Returns whether a key exists in the heap. |
| | |
| | Parameters |
| | ---------- |
| | key : any hashable object. |
| | The key to be looked up. |
| | """ |
| | return key in self._dict |
| |
|
| |
|
| | class PairingHeap(MinHeap): |
| | """A pairing heap.""" |
| |
|
| | class _Node(MinHeap._Item): |
| | """A node in a pairing heap. |
| | |
| | A tree in a pairing heap is stored using the left-child, right-sibling |
| | representation. |
| | """ |
| |
|
| | __slots__ = ("left", "next", "prev", "parent") |
| |
|
| | def __init__(self, key, value): |
| | super().__init__(key, value) |
| | |
| | self.left = None |
| | |
| | self.next = None |
| | |
| | self.prev = None |
| | |
| | self.parent = None |
| |
|
| | def __init__(self): |
| | """Initialize a pairing heap.""" |
| | super().__init__() |
| | self._root = None |
| |
|
| | def min(self): |
| | if self._root is None: |
| | raise nx.NetworkXError("heap is empty.") |
| | return (self._root.key, self._root.value) |
| |
|
| | def pop(self): |
| | if self._root is None: |
| | raise nx.NetworkXError("heap is empty.") |
| | min_node = self._root |
| | self._root = self._merge_children(self._root) |
| | del self._dict[min_node.key] |
| | return (min_node.key, min_node.value) |
| |
|
| | def get(self, key, default=None): |
| | node = self._dict.get(key) |
| | return node.value if node is not None else default |
| |
|
| | def insert(self, key, value, allow_increase=False): |
| | node = self._dict.get(key) |
| | root = self._root |
| | if node is not None: |
| | if value < node.value: |
| | node.value = value |
| | if node is not root and value < node.parent.value: |
| | self._cut(node) |
| | self._root = self._link(root, node) |
| | return True |
| | elif allow_increase and value > node.value: |
| | node.value = value |
| | child = self._merge_children(node) |
| | |
| | |
| | if child is not None: |
| | self._root = self._link(self._root, child) |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | return False |
| | else: |
| | |
| | node = self._Node(key, value) |
| | self._dict[key] = node |
| | self._root = self._link(root, node) if root is not None else node |
| | return True |
| |
|
| | def _link(self, root, other): |
| | """Link two nodes, making the one with the smaller value the parent of |
| | the other. |
| | """ |
| | if other.value < root.value: |
| | root, other = other, root |
| | next = root.left |
| | other.next = next |
| | if next is not None: |
| | next.prev = other |
| | other.prev = None |
| | root.left = other |
| | other.parent = root |
| | return root |
| |
|
| | def _merge_children(self, root): |
| | """Merge the subtrees of the root using the standard two-pass method. |
| | The resulting subtree is detached from the root. |
| | """ |
| | node = root.left |
| | root.left = None |
| | if node is not None: |
| | link = self._link |
| | |
| | |
| | |
| | |
| | prev = None |
| | while True: |
| | next = node.next |
| | if next is None: |
| | node.prev = prev |
| | break |
| | next_next = next.next |
| | node = link(node, next) |
| | node.prev = prev |
| | prev = node |
| | if next_next is None: |
| | break |
| | node = next_next |
| | |
| | |
| | prev = node.prev |
| | while prev is not None: |
| | prev_prev = prev.prev |
| | node = link(prev, node) |
| | prev = prev_prev |
| | |
| | node.prev = None |
| | node.next = None |
| | node.parent = None |
| | return node |
| |
|
| | def _cut(self, node): |
| | """Cut a node from its parent.""" |
| | prev = node.prev |
| | next = node.next |
| | if prev is not None: |
| | prev.next = next |
| | else: |
| | node.parent.left = next |
| | node.prev = None |
| | if next is not None: |
| | next.prev = prev |
| | node.next = None |
| | node.parent = None |
| |
|
| |
|
| | class BinaryHeap(MinHeap): |
| | """A binary heap.""" |
| |
|
| | def __init__(self): |
| | """Initialize a binary heap.""" |
| | super().__init__() |
| | self._heap = [] |
| | self._count = count() |
| |
|
| | def min(self): |
| | dict = self._dict |
| | if not dict: |
| | raise nx.NetworkXError("heap is empty") |
| | heap = self._heap |
| | |
| | |
| | while True: |
| | value, _, key = heap[0] |
| | if key in dict and value == dict[key]: |
| | break |
| | heappop(heap) |
| | return (key, value) |
| |
|
| | def pop(self): |
| | dict = self._dict |
| | if not dict: |
| | raise nx.NetworkXError("heap is empty") |
| | heap = self._heap |
| | |
| | |
| | while True: |
| | value, _, key = heap[0] |
| | heappop(heap) |
| | if key in dict and value == dict[key]: |
| | break |
| | del dict[key] |
| | return (key, value) |
| |
|
| | def get(self, key, default=None): |
| | return self._dict.get(key, default) |
| |
|
| | def insert(self, key, value, allow_increase=False): |
| | dict = self._dict |
| | if key in dict: |
| | old_value = dict[key] |
| | if value < old_value or (allow_increase and value > old_value): |
| | |
| | |
| | |
| | |
| | dict[key] = value |
| | heappush(self._heap, (value, next(self._count), key)) |
| | return value < old_value |
| | return False |
| | else: |
| | dict[key] = value |
| | heappush(self._heap, (value, next(self._count), key)) |
| | return True |
| |
|