Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add a pairing heap data structure #39046

Merged
merged 31 commits into from
Dec 22, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7b67376
add a pairing heap data structure
dcoudert Nov 27, 2024
aaf0502
fix lint errors
dcoudert Nov 27, 2024
747cc75
fix error in src/doc/en/reference/references/index.rst
dcoudert Nov 27, 2024
f3a88f5
remove some trailing whitespaces
dcoudert Nov 27, 2024
ac0ef3e
try to fix references
dcoudert Nov 27, 2024
bd63d91
meson build
dcoudert Nov 27, 2024
7d2982e
remove useless reference
dcoudert Nov 27, 2024
f0d2c3a
detail in pairing_heap.pyx
dcoudert Nov 28, 2024
fe3751d
use unordered_map
dcoudert Nov 28, 2024
2804a77
#39046: improvements in pairing_heap.h
dcoudert Nov 29, 2024
780dc33
#39046: improve pairing_heap.h
dcoudert Nov 30, 2024
9365926
Merge branch 'develop' into data_structures/pairing_heap
dcoudert Nov 30, 2024
a2524d3
Merge branch 'develop' into data_structures/pairing_heap
dcoudert Dec 7, 2024
1f5c0a5
#39046: fix coding style
dcoudert Dec 7, 2024
e6f6095
#39046: mode method top_value to parent class
dcoudert Dec 7, 2024
a229754
#39046: avoid storing the name of the class
dcoudert Dec 7, 2024
7627405
#39046: avoid calling bitset_clear
dcoudert Dec 7, 2024
4f08a26
#39046: avoid th use of new for PairingHeap in _test_PairingHeap_from_C
dcoudert Dec 7, 2024
eaa5fe5
#39046: mode declaration of top and pop to parent class
dcoudert Dec 7, 2024
982e53e
#39046: minor changes in pairing_heap.h
dcoudert Dec 7, 2024
8aa716d
#39046: alternative to bitset_first_in_complement
dcoudert Dec 8, 2024
b17bd70
#39046: missing commit
dcoudert Dec 8, 2024
a51794f
Merge branch 'develop' into data_structures/pairing_heap
dcoudert Dec 9, 2024
94bf13e
#39046: all suggested improvements
dcoudert Dec 10, 2024
aa016b7
#39046: simplify __repr__
dcoudert Dec 11, 2024
47ec705
#39046: try to make meson / conda happy
dcoudert Dec 11, 2024
6dfd7ab
#39046: suggested improvements
dcoudert Dec 12, 2024
7682e5b
#39046: suggested changes
dcoudert Dec 12, 2024
abafad9
#39046: review comments
dcoudert Dec 13, 2024
fc81b14
#39046: typos
dcoudert Dec 13, 2024
e15825a
Merge branch 'develop' into data_structures/pairing_heap
dcoudert Dec 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/doc/en/reference/data_structures/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ Data Structures
sage/data_structures/bounded_integer_sequences
sage/data_structures/stream
sage/data_structures/mutable_poset
sage/data_structures/pairing_heap

.. include:: ../footer.txt
5 changes: 5 additions & 0 deletions src/doc/en/reference/references/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2762,6 +2762,11 @@ REFERENCES:
Cambridge University Press, Cambridge, 2009.
See also the `Errata list <http://ac.cs.princeton.edu/errata/>`_.

.. [FSST1986] Michael L. Fredman, Robert Sedgewick, Daniel D. Sleator,
and Robert E. Tarjan. *The pairing heap: A new form of
self-adjusting heap*, Algorithmica, 1:111-129, 1986.
:doi:`10.1007/BF01840439`

.. [FST2012] \A. Felikson, \M. Shapiro, and \P. Tumarkin, *Cluster Algebras of
Finite Mutation Type Via Unfoldings*, Int Math Res Notices (2012)
2012 (8): 1768-1804.
Expand Down
3 changes: 3 additions & 0 deletions src/sage/data_structures/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ py.install_sources(
'bounded_integer_sequences.pxd',
'list_of_pairs.pxd',
'mutable_poset.py',
'pairing_heap.h',
'pairing_heap.pxd',
'sparse_bitset.pxd',
'stream.py',
subdir: 'sage/data_structures',
Expand All @@ -21,6 +23,7 @@ extension_data = {
'blas_dict' : files('blas_dict.pyx'),
'bounded_integer_sequences' : files('bounded_integer_sequences.pyx'),
'list_of_pairs' : files('list_of_pairs.pyx'),
'pairing_heap' : files('pairing_heap.pyx'),
}

foreach name, pyx : extension_data
Expand Down
308 changes: 308 additions & 0 deletions src/sage/data_structures/pairing_heap.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
/*
* Pairing heap
*
* Implements a pairing heap data structure as described in [1]. See also [2]
* for more details.
*
* This implementation is templated by the type TI of items and the type TV of
* the value associated with an item. The type TI must be either a standard type
* (int, size_t, etc.) or a type equipped with a has function as supported by
* std::unordered_map. The top of the heap is the item with smallest value,
* i.e., this is a min heap data structure. The number of items in the heap is
* not fixed. It supports the following operations:
*
* - empty(): return true if the heap is empty, and false otherwise.
*
* - push(item, value): push an item to the heap with specified value.
*
* - top(): access the pair (item, value) at the top of the heap, i.e., with
* smallest value in time O(1).
* This operation assumes that the heap is not empty.
*
* - top_item(): access the item at the top of the heap in time O(1).
* This operation assumes that the heap is not empty.
*
* - top_value(): access the value of the item at the top of the heap in O(1).
* This operation assumes that the heap is not empty.
*
* - pop(): remove top item from the heap in amortize time O(log(n))
dcoudert marked this conversation as resolved.
Show resolved Hide resolved
*
* - decrease(item, new_value): change the value associated with the item to the
* specified value ``new_value`` in time o(log(n)). The new value must be
* smaller than the previous one. Otherwise the structure of the heap is no
* longer guaranteed.
* If the item is not already in the heap, this method calls method ``push``.
*
* - contains(item): check whether specified item is in the heap in time O(1).
*
* - value(item): return the value associated with the item in the heap.
* This operation assumes that the item is already in the heap.
*
* References:
*
* [1] M. L. Fredman, R. Sedgewick, D. D. Sleator, and R. E. Tarjan.
* "The pairing heap: a new form of self-adjusting heap".
* Algorithmica. 1 (1): 111-129, 1986. doi:10.1007/BF01840439.
*
* [2] https://en.wikipedia.org/wiki/Pairing_heap
*
* Author:
* - David Coudert <david.coudert@inria.fr>
*
*/

#ifndef PAIRING_HEAP_H
#define PAIRING_HEAP_H

#include <iostream>
#include <unordered_map>
#include <stdexcept>


namespace pairing_heap {

template<
typename TI, // type of items stored in the node
typename TV // type of values associated with the stored item
>
struct PairingHeapNode {
TI item; // item contained in the node
TV value; // value associated with the item

PairingHeapNode<TI, TV> * prev; // Previous sibling of the node or parent
PairingHeapNode<TI, TV> * next; // Next sibling of the node
PairingHeapNode<TI, TV> * child; // First child of the node

explicit PairingHeapNode(const TI &some_item, const TV &some_value)
: item(some_item), value(some_value),
prev(nullptr), next(nullptr), child(nullptr) {
}

bool operator<(PairingHeapNode const& other) const {
return value < other.value;
}

bool operator<=(PairingHeapNode const& other) const {
return value <= other.value;
}
}; // end struct PairingHeapNode


template<
typename TI, // type of items stored in the node
typename TV // type of values associated with the stored item
>
class PairingHeap
{
public:

// Constructor
explicit PairingHeap()
: root(nullptr) {
}

// Copy constructor
PairingHeap(PairingHeap<TI, TV> const *other)
: root(nullptr) {
for (auto const& it: other->nodes) {
push(it.first, it.second->value);
}
}

// Destructor
virtual ~PairingHeap() {
for (auto const& it: nodes) {
delete it.second;
}
}

// Return true if the heap is empty, else false
bool empty() const {
return root == nullptr;
}

// Return true if the heap is not empty, else false
explicit operator bool() const {
return root != nullptr;
}

// Insert an item into the heap with specified value (priority)
void push(const TI &some_item, const TV &some_value) {
if (nodes.find(some_item) != nodes.end()) {
throw std::invalid_argument("item already in the heap");
}
PairingHeapNode<TI, TV> *p = new PairingHeapNode<TI, TV>(some_item, some_value);
nodes[some_item] = p;
root = root == nullptr ? p : _merge(root, p);
}

// Return the top pair (item, value) of the heap
std::pair<TI, TV> top() const {
if (root == nullptr) {
throw std::domain_error("trying to access the top of an empty heap");
}
return std::make_pair(root->item, root->value);
}

// Return the top item of the heap
TI top_item() const {
if (root == nullptr) {
throw std::domain_error("trying to access the top of an empty heap");
}
return root->item;
}

// Return the top value of the heap
TV top_value() const {
if (root == nullptr) {
throw std::domain_error("trying to access the top of an empty heap");
}
return root->value;
}

// Remove the top element from the heap. Do nothing if empty
void pop() {
if (root != nullptr) {
PairingHeapNode<TI, TV> *p = root->child;
nodes.erase(root->item);
delete root;
root = _pair(p);
}
}

// Decrease the value of specified item
// If the item is not in the heap, push it
void decrease(const TI &some_item, const TV &new_value) {
if (contains(some_item)) {
PairingHeapNode<TI, TV> *p = nodes[some_item];
if (p->value <= new_value) {
throw std::invalid_argument("the new value must be less than the current value");
}
p->value = new_value;
if (p->prev != nullptr) {
_unlink(p);
root = _merge(root, p);
}
} else {
push(some_item, new_value);
}
}

// Check if specified item is in the heap
bool contains(TI const& some_item) const {
return nodes.find(some_item) != nodes.end();
}

// Return the value associated with the item
TV value(const TI &some_item) const {
auto it = nodes.find(some_item);
if (it == nodes.end()) {
throw std::invalid_argument("the specified item is not in the heap");
}
return it->second->value;
}

// Return the number of items in the heap
size_t size() const {
return nodes.size();
}

private:

// Pointer to the top of the heap
PairingHeapNode<TI, TV> *root;

// Map used to access stored items
std::unordered_map<TI, PairingHeapNode<TI, TV> *> nodes;


// Pair list of heaps and return pointer to the top of resulting heap
static PairingHeapNode<TI, TV> *_pair(PairingHeapNode<TI, TV> *p) {
if (p == nullptr) {
return nullptr;
}

/*
* Move toward the end of the list, counting elements along the way.
* This is done in order to:
* - know whether the list has odd or even number of nodes
* - speed up going-back through the list
*/
size_t children = 1;
PairingHeapNode<TI, TV> *it = p;
while (it->next != nullptr) {
it = it->next;
children++;
}

PairingHeapNode<TI, TV> *result;

if (children % 2 == 1) {
PairingHeapNode<TI, TV> *a = it;
it = it->prev;
a->prev = a->next = nullptr;
result = a;
} else {
PairingHeapNode<TI, TV> *a = it;
PairingHeapNode<TI, TV> *b = it->prev;
it = it->prev->prev;
a->prev = a->next = b->prev = b->next = nullptr;
result = _merge(a, b);
}

for (size_t i = 0; i < (children - 1) / 2; i++) {
PairingHeapNode<TI, TV> *a = it;
PairingHeapNode<TI, TV> *b = it->prev;
it = it->prev->prev;
a->prev = a->next = b->prev = b->next = nullptr;
result = _merge(_merge(a, b), result);
}

return result;
} // end _pair


// Merge 2 heaps and return pointer to the top of resulting heap
static PairingHeapNode<TI, TV> *_merge(PairingHeapNode<TI, TV> *a,
PairingHeapNode<TI, TV> *b) {
if (*a <= *b) { // Use comparison method of PairingHeapNode
_link(a, b);
return a;
} else {
_link(b, a);
return b;
}
} // end _merge


// Make b a child of a
static void _link(PairingHeapNode<TI, TV> *a,
PairingHeapNode<TI, TV> *b) {
if (a->child != nullptr) {
b->next = a->child;
a->child->prev = b;
}
b->prev = a;
a->child = b;
} // end _link


// Remove p from its parent children list
static void _unlink(PairingHeapNode<TI, TV> *p) {
if (p->prev->child == p) {
p->prev->child = p->next;
} else {
p->prev->next = p->next;
}
if (p->next != nullptr) {
p->next->prev = p->prev;
}
p->prev = nullptr;
p->next = nullptr;
} // end _unlink

}; // end class PairingHeap

} // end namespace pairing_heap

#endif
Loading
Loading