|
| 1 | +/* |
| 2 | + * Pairing heap |
| 3 | + * |
| 4 | + * Implements a pairing heap data structure as described in [1]. See also [2] |
| 5 | + * for more details. |
| 6 | + * |
| 7 | + * This implementation is templated by the type TI of items and the type TV of |
| 8 | + * the value associated with an item. The type TI must be either a standard type |
| 9 | + * (int, size_t, etc.) or a type equipped with a has function as supported by |
| 10 | + * std::unordered_map. The top of the heap is the item with smallest value, |
| 11 | + * i.e., this is a min heap data structure. The number of items in the heap is |
| 12 | + * not fixed. It supports the following operations: |
| 13 | + * |
| 14 | + * - empty(): return true if the heap is empty, and false otherwise. |
| 15 | + * |
| 16 | + * - push(item, value): push an item to the heap with specified value. |
| 17 | + * |
| 18 | + * - top(): access the pair (item, value) at the top of the heap, i.e., with |
| 19 | + * smallest value in time O(1). |
| 20 | + * This operation assumes that the heap is not empty. |
| 21 | + * |
| 22 | + * - top_item(): access the item at the top of the heap in time O(1). |
| 23 | + * This operation assumes that the heap is not empty. |
| 24 | + * |
| 25 | + * - top_value(): access the value of the item at the top of the heap in O(1). |
| 26 | + * This operation assumes that the heap is not empty. |
| 27 | + * |
| 28 | + * - pop(): remove top item from the heap in amortize time O(log(n)). |
| 29 | + * |
| 30 | + * - decrease(item, new_value): change the value associated with the item to the |
| 31 | + * specified value ``new_value`` in time o(log(n)). The new value must be |
| 32 | + * smaller than the previous one. Otherwise the structure of the heap is no |
| 33 | + * longer guaranteed. |
| 34 | + * If the item is not already in the heap, this method calls method ``push``. |
| 35 | + * |
| 36 | + * - contains(item): check whether specified item is in the heap in time O(1). |
| 37 | + * |
| 38 | + * - value(item): return the value associated with the item in the heap. |
| 39 | + * This operation assumes that the item is already in the heap. |
| 40 | + * |
| 41 | + * References: |
| 42 | + * |
| 43 | + * [1] M. L. Fredman, R. Sedgewick, D. D. Sleator, and R. E. Tarjan. |
| 44 | + * "The pairing heap: a new form of self-adjusting heap". |
| 45 | + * Algorithmica. 1 (1): 111-129, 1986. doi:10.1007/BF01840439. |
| 46 | + * |
| 47 | + * [2] https://en.wikipedia.org/wiki/Pairing_heap |
| 48 | + * |
| 49 | + * Author: |
| 50 | + * - David Coudert <[email protected]> |
| 51 | + * |
| 52 | + */ |
| 53 | + |
| 54 | +#ifndef PAIRING_HEAP_H |
| 55 | +#define PAIRING_HEAP_H |
| 56 | + |
| 57 | +#include <stdexcept> |
| 58 | +#include <unordered_map> |
| 59 | + |
| 60 | +namespace pairing_heap { |
| 61 | + |
| 62 | + template< |
| 63 | + typename TV, // type of values |
| 64 | + typename T // type of the child class |
| 65 | + > |
| 66 | + struct PairingHeapNodeBase { |
| 67 | + public: |
| 68 | + |
| 69 | + bool operator<=(PairingHeapNodeBase const& other) const { |
| 70 | + return static_cast<T const*>(this)->le_implem(static_cast<T const&>(other)); |
| 71 | + } |
| 72 | + |
| 73 | + // Pair list of heaps and return pointer to the top of resulting heap |
| 74 | + static T *_pair(T *p) { |
| 75 | + if (p == nullptr) { |
| 76 | + return nullptr; |
| 77 | + } |
| 78 | + |
| 79 | + /* |
| 80 | + * Move toward the end of the list, counting elements along the way. |
| 81 | + * This is done in order to: |
| 82 | + * - know whether the list has odd or even number of nodes |
| 83 | + * - speed up going-back through the list |
| 84 | + */ |
| 85 | + size_t children = 1; |
| 86 | + T *it = p; |
| 87 | + while (it->next != nullptr) { |
| 88 | + it = it->next; |
| 89 | + children++; |
| 90 | + } |
| 91 | + |
| 92 | + T *result; |
| 93 | + |
| 94 | + if (children % 2 == 1) { |
| 95 | + T *a = it; |
| 96 | + it = it->prev; |
| 97 | + a->prev = a->next = nullptr; |
| 98 | + result = a; |
| 99 | + } else { |
| 100 | + T *a = it; |
| 101 | + T *b = it->prev; |
| 102 | + it = it->prev->prev; |
| 103 | + a->prev = a->next = b->prev = b->next = nullptr; |
| 104 | + result = _merge(a, b); |
| 105 | + } |
| 106 | + |
| 107 | + for (size_t i = 0; i < (children - 1) / 2; i++) { |
| 108 | + T *a = it; |
| 109 | + T *b = it->prev; |
| 110 | + it = it->prev->prev; |
| 111 | + a->prev = a->next = b->prev = b->next = nullptr; |
| 112 | + result = _merge(_merge(a, b), result); |
| 113 | + } |
| 114 | + |
| 115 | + return result; |
| 116 | + } // end _pair |
| 117 | + |
| 118 | + |
| 119 | + // Merge 2 heaps and return pointer to the top of resulting heap |
| 120 | + static T *_merge(T *a, T *b) { |
| 121 | + if (*a <= *b) { // Use comparison method of PairingHeapNodeBase |
| 122 | + _link(a, b); |
| 123 | + return a; |
| 124 | + } else { |
| 125 | + _link(b, a); |
| 126 | + return b; |
| 127 | + } |
| 128 | + } // end _merge |
| 129 | + |
| 130 | + |
| 131 | + // Make b a child of a |
| 132 | + static void _link(T *a, T *b) { |
| 133 | + if (a->child != nullptr) { |
| 134 | + b->next = a->child; |
| 135 | + a->child->prev = b; |
| 136 | + } |
| 137 | + b->prev = a; |
| 138 | + a->child = b; |
| 139 | + } // end _link |
| 140 | + |
| 141 | + |
| 142 | + // Remove p from its parent children list |
| 143 | + static void _unlink(T *p) { |
| 144 | + if (p->prev->child == p) { |
| 145 | + p->prev->child = p->next; |
| 146 | + } else { |
| 147 | + p->prev->next = p->next; |
| 148 | + } |
| 149 | + if (p->next != nullptr) { |
| 150 | + p->next->prev = p->prev; |
| 151 | + } |
| 152 | + p->prev = nullptr; |
| 153 | + p->next = nullptr; |
| 154 | + } // end _unlink |
| 155 | + |
| 156 | + |
| 157 | + TV value; // value associated to the node |
| 158 | + T * prev; // Previous sibling of the node or parent |
| 159 | + T * next; // Next sibling of the node |
| 160 | + T * child; // First child of the node |
| 161 | + |
| 162 | + protected: |
| 163 | + // Only derived class can build a PairingHeapNodeBase |
| 164 | + explicit PairingHeapNodeBase(const TV &some_value) |
| 165 | + : value{some_value}, prev{nullptr}, next{nullptr}, child{nullptr} { |
| 166 | + } |
| 167 | + }; // end struct PairingHeapNodeBase |
| 168 | + |
| 169 | + |
| 170 | + template< |
| 171 | + typename TI, // type of items stored in the node |
| 172 | + typename TV // type of values associated with the stored item |
| 173 | + // Assumes TV is a comparable type |
| 174 | + > |
| 175 | + class PairingHeapNode |
| 176 | + : public PairingHeapNodeBase<TV, PairingHeapNode<TI, TV>> { |
| 177 | + |
| 178 | + public: |
| 179 | + PairingHeapNode(TI const& some_item, TV const& some_value) |
| 180 | + : Base_(some_value), item(some_item) { |
| 181 | + } |
| 182 | + |
| 183 | + bool le_implem(PairingHeapNode const& other) const { |
| 184 | + return this->value <= other.value; |
| 185 | + } |
| 186 | + |
| 187 | + TI item; // item contained in the node |
| 188 | + |
| 189 | + private: |
| 190 | + using Base_ = PairingHeapNodeBase<TV, PairingHeapNode<TI, TV>>; |
| 191 | + }; |
| 192 | + |
| 193 | + |
| 194 | + class PairingHeapNodePy |
| 195 | + : public PairingHeapNodeBase<PyObject *, PairingHeapNodePy> { |
| 196 | + public: |
| 197 | + PairingHeapNodePy(PyObject *some_value) |
| 198 | + : Base_(some_value) { |
| 199 | + } |
| 200 | + |
| 201 | + bool le_implem(PairingHeapNodePy const& other) const { |
| 202 | + return PyObject_RichCompareBool(this->value, other.value, Py_LE); |
| 203 | + } |
| 204 | + |
| 205 | + private: |
| 206 | + using Base_ = PairingHeapNodeBase<PyObject *, PairingHeapNodePy>; |
| 207 | + }; |
| 208 | + |
| 209 | + |
| 210 | + |
| 211 | + template< |
| 212 | + typename TI, // type of items stored in the node |
| 213 | + typename TV // type of values associated with the stored item |
| 214 | + // Assume TV is a comparable type |
| 215 | + > |
| 216 | + class PairingHeap |
| 217 | + { |
| 218 | + public: |
| 219 | + using HeapNodeType = PairingHeapNode<TI, TV>; |
| 220 | + |
| 221 | + // Constructor |
| 222 | + explicit PairingHeap() |
| 223 | + : root(nullptr) { |
| 224 | + } |
| 225 | + |
| 226 | + // Copy constructor |
| 227 | + PairingHeap(PairingHeap<TI, TV> const *other) |
| 228 | + : root(nullptr) { |
| 229 | + for (auto const& it: other->nodes) { |
| 230 | + push(it.first, it.second->value); |
| 231 | + } |
| 232 | + } |
| 233 | + |
| 234 | + // Destructor |
| 235 | + virtual ~PairingHeap() { |
| 236 | + for (auto const& it: nodes) { |
| 237 | + delete it.second; |
| 238 | + } |
| 239 | + } |
| 240 | + |
| 241 | + // Return true if the heap is empty, else false |
| 242 | + bool empty() const { |
| 243 | + return root == nullptr; |
| 244 | + } |
| 245 | + |
| 246 | + // Return true if the heap is not empty, else false |
| 247 | + explicit operator bool() const { |
| 248 | + return root != nullptr; |
| 249 | + } |
| 250 | + |
| 251 | + // Insert an item into the heap with specified value (priority) |
| 252 | + void push(const TI &some_item, const TV &some_value) { |
| 253 | + if (contains(some_item)) { |
| 254 | + throw std::invalid_argument("item already in the heap"); |
| 255 | + } |
| 256 | + PairingHeapNode<TI, TV> *p = new PairingHeapNode<TI, TV>(some_item, some_value); |
| 257 | + nodes[some_item] = p; |
| 258 | + root = empty() ? p : HeapNodeType::_merge(root, p); |
| 259 | + } |
| 260 | + |
| 261 | + // Return the top pair (item, value) of the heap |
| 262 | + std::pair<TI, TV> top() const { |
| 263 | + if (empty()) { |
| 264 | + throw std::domain_error("trying to access the top of an empty heap"); |
| 265 | + } |
| 266 | + return std::make_pair(root->item, root->value); |
| 267 | + } |
| 268 | + |
| 269 | + // Return the top item of the heap |
| 270 | + TI top_item() const { |
| 271 | + if (empty()) { |
| 272 | + throw std::domain_error("trying to access the top of an empty heap"); |
| 273 | + } |
| 274 | + return root->item; |
| 275 | + } |
| 276 | + |
| 277 | + // Return the top value of the heap |
| 278 | + TV top_value() const { |
| 279 | + if (empty()) { |
| 280 | + throw std::domain_error("trying to access the top of an empty heap"); |
| 281 | + } |
| 282 | + return root->value; |
| 283 | + } |
| 284 | + |
| 285 | + // Remove the top element from the heap. Do nothing if empty |
| 286 | + void pop() { |
| 287 | + if (not empty()) { |
| 288 | + PairingHeapNode<TI, TV> *p = root->child; |
| 289 | + nodes.erase(root->item); |
| 290 | + delete root; |
| 291 | + root = HeapNodeType::_pair(p); |
| 292 | + } |
| 293 | + } |
| 294 | + |
| 295 | + // Decrease the value of specified item |
| 296 | + // If the item is not in the heap, push it |
| 297 | + void decrease(const TI &some_item, const TV &new_value) { |
| 298 | + if (contains(some_item)) { |
| 299 | + PairingHeapNode<TI, TV> *p = nodes[some_item]; |
| 300 | + if (p->value <= new_value) { |
| 301 | + throw std::invalid_argument("the new value must be less than the current value"); |
| 302 | + } |
| 303 | + p->value = new_value; |
| 304 | + if (p->prev != nullptr) { |
| 305 | + HeapNodeType::_unlink(p); |
| 306 | + root = HeapNodeType::_merge(root, p); |
| 307 | + } |
| 308 | + } else { |
| 309 | + push(some_item, new_value); |
| 310 | + } |
| 311 | + } |
| 312 | + |
| 313 | + // Check if specified item is in the heap |
| 314 | + bool contains(TI const& some_item) const { |
| 315 | + return nodes.find(some_item) != nodes.end(); |
| 316 | + } |
| 317 | + |
| 318 | + // Return the value associated with the item |
| 319 | + TV value(const TI &some_item) const { |
| 320 | + auto it = nodes.find(some_item); |
| 321 | + if (it == nodes.end()) { |
| 322 | + throw std::invalid_argument("the specified item is not in the heap"); |
| 323 | + } |
| 324 | + return it->second->value; |
| 325 | + } |
| 326 | + |
| 327 | + // Return the number of items in the heap |
| 328 | + size_t size() const { |
| 329 | + return nodes.size(); |
| 330 | + } |
| 331 | + |
| 332 | + private: |
| 333 | + |
| 334 | + // Pointer to the top of the heap |
| 335 | + PairingHeapNode<TI, TV> *root; |
| 336 | + |
| 337 | + // Map used to access stored items |
| 338 | + std::unordered_map<TI, PairingHeapNode<TI, TV> *> nodes; |
| 339 | + |
| 340 | + }; // end class PairingHeap |
| 341 | + |
| 342 | +} // end namespace pairing_heap |
| 343 | + |
| 344 | +#endif |
0 commit comments