1
0
Fork 0
maximum-weight-matching/cpp/datastruct.h

1093 lines
31 KiB
C
Raw Normal View History

2023-06-16 19:55:43 +02:00
/*
* Data structures for matching.
*/
#include <algorithm>
#include <cassert>
#include <limits>
2024-11-08 18:38:20 +01:00
#include <tuple>
#include <vector>
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
/* **************************************************
* ** class ConcatenableQueue **
* ************************************************** */
2023-06-16 19:55:43 +02:00
/**
2024-11-08 18:38:20 +01:00
* Priority queue supporting efficient merge and split operations.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* Conceptually, this is a combination of a disjoint set and a priority queue.
* Each queue has a "name".
* Each element has associated "data".
* Each element has a priority.
* Each element is contained in at most one queue at any time.
2023-06-16 19:55:43 +02:00
*
* The following operations can be done efficiently:
2024-11-08 18:38:20 +01:00
* - Find the name of the queue that contains a given element.
* - Change the priority of a given element.
* - Find the element with lowest priority in a given queue.
* - Merge two or more queues.
2023-06-16 19:55:43 +02:00
* - Undo a previous merge step.
*
2024-11-08 18:38:20 +01:00
* A ConcatenableQueue instance may be destructed if it is empty and not
* currently merged into a larger queue. Alternatively, a group of related
* ConcatenableQueue instances and their Node instances may be destructed
* together, even if non-empty. In this case, the objects may be destructed
* in any order. No other interactions with the objects are allowed once
* destruction has started.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* This data structure is implemented as an AVL tree, with minimum-priority
* tracking added to it.
* See also
* https://en.wikipedia.org/wiki/Avl_tree
* and
* G. Blelloch, D. Ferizovic, Y. Sun, Parallel Ordered Sets Using Join,
* https://arxiv.org/abs/1602.02120
2023-06-16 19:55:43 +02:00
*/
2024-11-08 18:38:20 +01:00
template <typename PrioType,
typename NameType,
typename DataType>
class ConcatenableQueue
2023-06-16 19:55:43 +02:00
{
public:
2024-11-08 18:38:20 +01:00
2023-06-16 19:55:43 +02:00
/**
2024-11-08 18:38:20 +01:00
* A Node instance represents an element in a concatenable queue.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* A Node instance must remain valid while it is contained in a queue.
* The containing queue holds a pointer to the Node.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* A Node instance may be destructed if it is not contained in any queue.
* Alternatively, a Node instance may be destructed just before or after
* destructing the containing queue. In this case, no intermediate
* interactions with the node or queue are allowed.
2023-06-16 19:55:43 +02:00
*/
2024-11-08 18:38:20 +01:00
class Node
{
public:
/** Construct an unused node, not yet contained in any queue. */
Node()
: owner_(nullptr),
parent_(nullptr),
left_child_(nullptr),
right_child_(nullptr),
height_(0)
{ }
// Prevent copying.
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
/**
* Return the name of the queue that contains this element.
*
* The node must be contained in a queue.
* This function takes time O(log(n)).
*/
NameType find() const
2024-11-08 18:38:20 +01:00
{
const Node* node = this;
2024-11-08 18:38:20 +01:00
while (node->parent_) {
node = node->parent_;
}
assert(node->owner_);
return node->owner_->name();
}
/**
* Return the priority of this element.
*
* The node must be contained in a queue.
* This function takes time O(1).
*/
PrioType prio() const
{
assert(height_ != 0);
return prio_;
}
/**
* Change the priority of this element.
*
* The node must be contained in a queue.
* This function takes time O(log(n)).
*/
void set_prio(PrioType new_prio)
{
assert(height_ != 0);
prio_ = new_prio;
Node* node = this;
while (node) {
PrioType min_prio = node->prio_;
DataType min_data = node->data_;
for (Node* child : { node->left_child_,
node->right_child_ }) {
if (child && child->min_prio_ < min_prio) {
min_prio = child->min_prio_;
min_data = child->min_data_;
}
}
node->min_prio_ = min_prio;
node->min_data_ = min_data;
node = node->parent_;
}
}
private:
PrioType prio_;
DataType data_;
PrioType min_prio_;
DataType min_data_;
ConcatenableQueue* owner_;
Node* parent_;
Node* left_child_;
Node* right_child_;
unsigned int height_;
friend class ConcatenableQueue;
};
/** Construct an empty queue. */
explicit ConcatenableQueue(const NameType& name)
: name_(name)
, tree_(nullptr)
2024-11-08 18:38:20 +01:00
, first_node_(nullptr)
, first_subqueue_(nullptr)
, next_subqueue_(nullptr)
2023-06-16 19:55:43 +02:00
{ }
// Prevent copying.
2024-11-08 18:38:20 +01:00
ConcatenableQueue(const ConcatenableQueue&) = delete;
ConcatenableQueue& operator=(const ConcatenableQueue&) = delete;
/** Return the name of this queue. */
NameType name() const
{
return name_;
}
2023-06-16 19:55:43 +02:00
/**
2024-11-08 18:38:20 +01:00
* Insert the specified Node into the queue.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* The Node instance must be unused (not yet contained in any queue).
*
* The queue must be empty. Only one element can be inserted into
* a queue in this way. Larger queues can only result from merging.
*
* The queue stores a pointer to the Node instance. The Node instance must
* remain valid for as long as it is contained in any queue.
*
* This function takes time O(1).
2023-06-16 19:55:43 +02:00
*/
2024-11-08 18:38:20 +01:00
void insert(Node* node, PrioType prio, const DataType& data)
2023-06-16 19:55:43 +02:00
{
2024-11-08 18:38:20 +01:00
assert(! tree_);
assert(! first_node_);
assert(node->height_ == 0);
assert(! node->parent_);
assert(! node->left_child_);
assert(! node->right_child_);
node->prio_ = prio;
node->data_ = data;
node->min_prio_ = prio;
node->min_data_ = data;
node->owner_ = this;
node->height_ = 1;
tree_ = node;
first_node_ = node;
2023-06-16 19:55:43 +02:00
}
/**
2024-11-08 18:38:20 +01:00
* Return the minimum priority of any element in the queue.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* The queue must be non-empty.
2023-06-16 19:55:43 +02:00
* This function takes time O(1).
*/
2024-11-08 18:38:20 +01:00
PrioType min_prio() const
2023-06-16 19:55:43 +02:00
{
2024-11-08 18:38:20 +01:00
assert(tree_);
return tree_->min_prio_;
2023-06-16 19:55:43 +02:00
}
/**
2024-11-08 18:38:20 +01:00
* Return the element with minimum priority.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* The queue must be non-empty.
2023-06-16 19:55:43 +02:00
* This function takes time O(1).
*/
2024-11-08 18:38:20 +01:00
DataType min_elem() const
2023-06-16 19:55:43 +02:00
{
2024-11-08 18:38:20 +01:00
assert(tree_);
return tree_->min_data_;
2023-06-16 19:55:43 +02:00
}
/**
2024-11-08 18:38:20 +01:00
* Merge the specified queues.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* This queue instance must inititially be empty.
* All specified sub-queues must initially be non-empty.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* This function removes all elements from the specified sub-queues
* and adds them to this queue.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* After merging, this queue retains references to the sub-queues.
* This may be used later to split (undo the merge step).
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* This function takes time O(len(sub_queues) * log(n)).
2023-06-16 19:55:43 +02:00
*/
2024-11-08 18:38:20 +01:00
template <typename QueueIterator>
void merge(QueueIterator sub_queues_begin, QueueIterator sub_queues_end)
2023-06-16 19:55:43 +02:00
{
2024-11-08 18:38:20 +01:00
assert(! tree_);
assert(! first_node_);
assert(sub_queues_begin != sub_queues_end);
// Pull the root node from the first sub-queue.
ConcatenableQueue* sub = *sub_queues_begin;
assert(sub->tree_);
Node* merged_tree = sub->tree_;
sub->tree_ = nullptr;
// Clear owner pointer from tree.
assert(merged_tree->owner_ == sub);
merged_tree->owner_ = nullptr;
// Copy first node to this queue.
assert(sub->first_node_);
first_node_ = sub->first_node_;
// Build linked list of sub-queues, starting with the first sub-queue.
assert(! sub->next_subqueue_);
first_subqueue_ = sub;
// Merge remaining sub-queues.
QueueIterator it = sub_queues_begin;
++it;
while (it != sub_queues_end) {
ConcatenableQueue* prev_sub = sub;
sub = *it;
// Clear owner pointer and root node from the sub-queue.
assert(sub->tree_);
assert(sub->tree_->owner_ == sub);
sub->tree_->owner_ = nullptr;
// Merge sub-queue tree into our current tree.
merged_tree = merge_tree(merged_tree, sub->tree_);
// Clear root pointer from sub-queue.
sub->tree_ = nullptr;
// Add sub-queue to linked list of sub-queues.
assert(! sub->next_subqueue_);
prev_sub->next_subqueue_ = sub;
++it;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
// Put owner pointer in the root node of the tree.
merged_tree->owner_ = this;
tree_ = merged_tree;
}
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
/**
* Undo the merge step that created this queue.
*
* Remove all elements from this queue and put them back in
* the sub-queues from which they came.
*
* After splitting, this queue will be empty.
*
* This function takes time O(k * log(n)).
*/
void split()
2023-06-16 19:55:43 +02:00
{
2024-11-08 18:38:20 +01:00
assert(tree_);
assert(first_subqueue_);
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
// Clear the owner pointer from the root node.
assert(tree_->owner_ == this);
tree_->owner_ = nullptr;
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
Node* rtree = tree_;
ConcatenableQueue* sub = first_subqueue_;
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
// Repeatedly split the tree to reconstruct each sub-queue.
while (sub->next_subqueue_) {
ConcatenableQueue* next_sub = sub->next_subqueue_;
sub->next_subqueue_ = nullptr;
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
// Split the tree on the first node of the next sub-queue.
assert(next_sub->first_node_);
Node* ltree;
std::tie(ltree, rtree) = split_tree(next_sub->first_node_);
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
// Put the left half of the tree in the current sub-queue.
sub->tree_ = ltree;
ltree->owner_ = sub;
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
// Continue to next sub-queue.
sub = next_sub;
}
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
// Put the remaining part of the tree in the last sub-queue.
rtree->owner_ = sub;
sub->tree_ = rtree;
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
// Clean up this instance.
tree_ = nullptr;
first_node_ = nullptr;
first_subqueue_ = nullptr;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
private:
2023-06-16 19:55:43 +02:00
/**
2024-11-08 18:38:20 +01:00
* Merge two trees and rebalance.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* This function takes time O(log(n)).
2023-06-16 19:55:43 +02:00
*/
2024-11-08 18:38:20 +01:00
static Node* merge_tree(Node* ltree, Node* rtree)
2023-06-16 19:55:43 +02:00
{
2024-11-08 18:38:20 +01:00
// Remove the last node from the left tree.
Node* node;
std::tie(ltree, node) = split_last_node(ltree);
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
// Join the trees.
return join(ltree, node, rtree);
2023-06-16 19:55:43 +02:00
}
/**
2024-11-08 18:38:20 +01:00
* Remove the last node from the tree and rebalance.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* This function takes time O(log(n)).
2023-06-16 19:55:43 +02:00
*/
2024-11-08 18:38:20 +01:00
static std::tuple<Node*, Node*> split_last_node(Node* tree)
2023-06-16 19:55:43 +02:00
{
2024-11-08 18:38:20 +01:00
assert(tree);
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
/*
* Descend down the right spine of the tree to find the last node.
*
* tree
* / \
* X
* / \
* X <-- parent
* / \
* last_node
* /
* X <-- ltree
*/
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
// Find last node.
Node* last_node = tree;
while (last_node->right_child_) {
last_node = last_node->right_child_;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
// Detach its left subtree.
Node* ltree = last_node->left_child_;
last_node->left_child_ = nullptr;
if (ltree) {
ltree->parent_ = nullptr;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
// Detach from the parent.
Node* parent = last_node->parent_;
last_node->parent_ = nullptr;
if (parent) {
parent->right_child_ = nullptr;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
// Reconstruct along the right spine of the original tree.
while (parent) {
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
// Step to parent.
Node* cur = parent;
parent = cur->parent_;
// Detach from its own parent.
cur->parent_ = nullptr;
if (parent) {
parent->right_child_ = nullptr;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
// Join the current node to the reconstructed tree.
ltree = join(cur->left_child_, cur, ltree);
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
return std::make_tuple(ltree, last_node);
2023-06-16 19:55:43 +02:00
}
/**
2024-11-08 18:38:20 +01:00
* Split a tree on a specified node.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* Two new trees will be constructed.
* Leaf nodes to the left of "split_node" will go to the left tree.
* Leaf nodes to the right of "split_node", and "split_node" itself,
* will go to the right tree.
2023-06-16 19:55:43 +02:00
*
* This function takes time O(log(n)).
*/
2024-11-08 18:38:20 +01:00
static std::tuple<Node*, Node*> split_tree(Node* split_node)
2023-06-16 19:55:43 +02:00
{
2024-11-08 18:38:20 +01:00
assert(split_node);
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
// All left-descendants of "split_node" belong in the left tree.
// Detach it from "split_node".
Node* ltree = split_node->left_child_;
split_node->left_child_ = nullptr;
if (ltree) {
ltree->parent_ = nullptr;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
// Start with an empty right tree.
Node* rtree = nullptr;
// Start at "split_node".
// Take note of the fact that the "cut" between the two halves of
// the tree runs down its left branch, since "split_node" itself
// belongs to the right tree.
Node* node = split_node;
bool left_branch = true;
// Work upwards through the tree, assigning each node either to
// the new left tree or the new right tree.
//
// This loop runs for O(log(n)) iterations.
// Each iteration calls join() once, taking time proportional
// to the difference in height between the intermediate trees.
// The total run time of all join() calls together is O(log(n)).
while (node) {
// Detach the current node from its parent.
// Remember to which branch of the parent it was attached.
Node* parent = node->parent_;
node->parent_ = nullptr;
bool parent_left_branch = true;
if (parent) {
parent_left_branch = (parent->left_child_ == node);
if (parent_left_branch) {
parent->left_child_ = nullptr;
} else {
assert(parent->right_child_ == node);
parent->right_child_ = nullptr;
2023-06-16 19:55:43 +02:00
}
}
2024-11-08 18:38:20 +01:00
// Join the current node and its remaining descendents either
// to the left tree or to the right tree.
if (left_branch) {
2023-06-16 19:55:43 +02:00
/*
2024-11-08 18:38:20 +01:00
* "node" belongs to the right tree.
* Join like this:
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* (node) <--- new rtree
* / \
* (rtree) (node->right_child)
2023-06-16 19:55:43 +02:00
*/
2024-11-08 18:38:20 +01:00
assert(! node->left_child_);
rtree = join(rtree, node, node->right_child_);
2023-06-16 19:55:43 +02:00
} else {
/*
2024-11-08 18:38:20 +01:00
* "node" belongs to the left tree.
* Join like this:
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* (node) <--- new ltree
* / \
* (node->left_child) (ltree)
2023-06-16 19:55:43 +02:00
*/
2024-11-08 18:38:20 +01:00
assert(! node->right_child_);
ltree = join(node->left_child_, node, ltree);
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
// Continue with the parent node.
node = parent;
left_branch = parent_left_branch;
}
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
// Done. All that remains of the old tree is the two new halves.
return std::make_tuple(ltree, rtree);
2023-06-16 19:55:43 +02:00
}
/** Return node height, or 0 if node == nullptr. */
static unsigned int get_node_height(const Node* node)
{
2024-11-08 18:38:20 +01:00
return node ? node->height_ : 0;
2023-06-16 19:55:43 +02:00
}
/**
2024-11-08 18:38:20 +01:00
* Repair the height and min-priority information of a node
2023-06-16 19:55:43 +02:00
* after modifying its children.
*
* After repairing a node, it is typically necessary to also repair
* its ancestors.
*/
static void repair_node(Node* node)
{
2024-11-08 18:38:20 +01:00
Node* lchild = node->left_child_;
Node* rchild = node->right_child_;
2023-06-16 19:55:43 +02:00
// Repair node height.
2024-11-08 18:38:20 +01:00
node->height_ = 1 + std::max(get_node_height(lchild),
get_node_height(rchild));
// Repair min-priority.
PrioType min_prio = node->prio_;
DataType min_data = node->data_;
for (Node* child : { lchild, rchild }) {
if (child && child->min_prio_ < min_prio) {
min_prio = child->min_prio_;
min_data = child->min_data_;
}
}
node->min_prio_ = min_prio;
node->min_data_ = min_data;
2023-06-16 19:55:43 +02:00
}
/** Rotate the subtree to the left and return the new root of the subtree. */
static Node* rotate_left(Node* node)
{
/*
* N C
* / \ / \
* A C ---> N D
* / \ / \
* B D A B
*/
2024-11-08 18:38:20 +01:00
Node* parent = node->parent_;
Node* new_top = node->right_child_;
2023-06-16 19:55:43 +02:00
assert(new_top);
2024-11-08 18:38:20 +01:00
Node* nb = new_top->left_child_;
node->right_child_ = nb;
2023-06-16 19:55:43 +02:00
if (nb) {
2024-11-08 18:38:20 +01:00
nb->parent_ = node;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
new_top->left_child_ = node;
node->parent_ = new_top;
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
new_top->parent_ = parent;
2023-06-16 19:55:43 +02:00
if (parent) {
2024-11-08 18:38:20 +01:00
if (parent->left_child_ == node) {
parent->left_child_ = new_top;
2023-06-16 19:55:43 +02:00
} else {
2024-11-08 18:38:20 +01:00
assert(parent->right_child_ == node);
parent->right_child_ = new_top;
2023-06-16 19:55:43 +02:00
}
}
repair_node(node);
repair_node(new_top);
return new_top;
}
/** Rotate the subtree to the right and return the new root of the subtree. */
2024-11-08 18:38:20 +01:00
static Node* rotate_right(Node* node)
2023-06-16 19:55:43 +02:00
{
/*
* N B
* / \ / \
* B D ---> A N
* / \ / \
* A C C D
*/
2024-11-08 18:38:20 +01:00
Node* parent = node->parent_;
Node* new_top = node->left_child_;
2023-06-16 19:55:43 +02:00
assert(new_top);
2024-11-08 18:38:20 +01:00
Node* nc = new_top->right_child_;
node->left_child_ = nc;
2023-06-16 19:55:43 +02:00
if (nc) {
2024-11-08 18:38:20 +01:00
nc->parent_ = node;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
new_top->right_child_ = node;
node->parent_ = new_top;
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
new_top->parent_ = parent;
2023-06-16 19:55:43 +02:00
if (parent) {
2024-11-08 18:38:20 +01:00
if (parent->left_child_ == node) {
parent->left_child_ = new_top;
2023-06-16 19:55:43 +02:00
} else {
2024-11-08 18:38:20 +01:00
assert(parent->right_child_ == node);
parent->right_child_ = new_top;
2023-06-16 19:55:43 +02:00
}
}
repair_node(node);
repair_node(new_top);
return new_top;
}
/**
* Join a left subtree, middle node and right subtree together.
*
* The left subtree is higher than the right subtree.
*/
2024-11-08 18:38:20 +01:00
static Node* join_right(Node* ltree, Node* node, Node* rtree)
2023-06-16 19:55:43 +02:00
{
assert(ltree);
2024-11-08 18:38:20 +01:00
unsigned int lh = ltree->height_;
2023-06-16 19:55:43 +02:00
unsigned int rh = get_node_height(rtree);
assert(lh > rh + 1);
/*
* Descend down the right spine of "ltree".
* Stop at a node with compatible height, then insert "node"
* and attach "rtree".
*
* ltree
* / \
* X
* / \
* X <-- cur
* / \
* node
* / \
* X rtree
*/
// Descend to a point with compatible height.
Node* cur = ltree;
2024-11-08 18:38:20 +01:00
while (cur->right_child_ && (cur->right_child_->height_ > rh + 1)) {
cur = cur->right_child_;
2023-06-16 19:55:43 +02:00
}
// Insert "node" and "rtree".
2024-11-08 18:38:20 +01:00
node->left_child_ = cur->right_child_;
node->right_child_ = rtree;
if (node->left_child_) {
node->left_child_->parent_ = node;
2023-06-16 19:55:43 +02:00
}
if (rtree) {
2024-11-08 18:38:20 +01:00
rtree->parent_ = node;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
cur->right_child_ = node;
node->parent_ = cur;
2023-06-16 19:55:43 +02:00
// A double rotation may be necessary.
2024-11-08 18:38:20 +01:00
if ((! cur->left_child_) || (cur->left_child_->height_ <= rh)) {
2023-06-16 19:55:43 +02:00
node = rotate_right(node);
cur = rotate_left(cur);
} else {
repair_node(node);
repair_node(cur);
}
// Ascend from "cur" to the root of the tree; repair and rebalance.
2024-11-08 18:38:20 +01:00
while (cur->parent_) {
cur = cur->parent_;
assert(cur->left_child_);
assert(cur->right_child_);
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
if (cur->left_child_->height_ + 1 < cur->right_child_->height_) {
cur = rotate_left(cur);
2023-06-16 19:55:43 +02:00
} else {
repair_node(cur);
}
}
return cur;
}
/**
* Join a left subtree, middle node and right subtree together.
*
* The right subtree is higher than the left subtree.
*/
2024-11-08 18:38:20 +01:00
static Node* join_left(Node* ltree, Node* node, Node* rtree)
2023-06-16 19:55:43 +02:00
{
assert(rtree);
unsigned int lh = get_node_height(ltree);
2024-11-08 18:38:20 +01:00
unsigned int rh = rtree->height_;
2023-06-16 19:55:43 +02:00
assert(lh + 1 < rh);
/*
* Descend down the left spine of "rtree".
* Stop at a node with compatible height, then insert "node"
* and attach "ltree".
*
* rtree
* / \
* X
* / \
* cur --> X
* / \
* node
* / \
* ltree X
*/
// Descend to a point with compatible height.
Node* cur = rtree;
2024-11-08 18:38:20 +01:00
while (cur->left_child_ && (cur->left_child_->height_ > lh + 1)) {
cur = cur->left_child_;
2023-06-16 19:55:43 +02:00
}
// Insert "node" and "ltree".
2024-11-08 18:38:20 +01:00
node->left_child_ = ltree;
node->right_child_ = cur->left_child_;
2023-06-16 19:55:43 +02:00
if (ltree) {
2024-11-08 18:38:20 +01:00
ltree->parent_ = node;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
if (node->right_child_) {
node->right_child_->parent_ = node;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
cur->left_child_ = node;
node->parent_ = cur;
2023-06-16 19:55:43 +02:00
// A double rotation may be necessary.
2024-11-08 18:38:20 +01:00
if ((! cur->right_child_) || (cur->right_child_->height_ <= lh)) {
2023-06-16 19:55:43 +02:00
node = rotate_left(node);
cur = rotate_right(cur);
} else {
repair_node(node);
repair_node(cur);
}
// Ascend from "cur" to the root of the tree; repair and rebalance.
2024-11-08 18:38:20 +01:00
while (cur->parent_) {
cur = cur->parent_;
assert(cur->left_child_);
assert(cur->right_child_);
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
if (cur->left_child_->height_ > cur->right_child_->height_ + 1) {
2023-06-16 19:55:43 +02:00
cur = rotate_right(cur);
} else {
repair_node(cur);
}
}
return cur;
}
/**
* Join a left subtree, middle node and right subtree together.
*
* The index of all nodes in the left subtree must be less than
* the index of the middle node. The index of all nodes in
* the right subtree must be greater than the middle node.
*
* The left or right subtree may initially be a child of the middle
* node; such links will be broken as needed.
*
* The left and right subtrees must be consistent, semi-balanced trees.
* Height and priority of the middle node may initially be inconsistent;
* this function will repair it.
*
* @return Root node of the joined tree.
*/
2024-11-08 18:38:20 +01:00
static Node* join(Node* ltree, Node* node, Node* rtree)
2023-06-16 19:55:43 +02:00
{
unsigned int lh = get_node_height(ltree);
unsigned int rh = get_node_height(rtree);
if (lh > rh + 1) {
assert(ltree);
2024-11-08 18:38:20 +01:00
ltree->parent_ = nullptr;
2023-06-16 19:55:43 +02:00
return join_right(ltree, node, rtree);
} else if (lh + 1 < rh) {
assert(rtree);
2024-11-08 18:38:20 +01:00
rtree->parent_ = nullptr;
2023-06-16 19:55:43 +02:00
return join_left(ltree, node, rtree);
} else {
/*
* Subtree heights are compatible. Just join them.
*
* node
* / \
* ltree rtree
* / \ / \
*/
2024-11-08 18:38:20 +01:00
node->parent_ = nullptr;
node->left_child_ = ltree;
2023-06-16 19:55:43 +02:00
if (ltree) {
2024-11-08 18:38:20 +01:00
ltree->parent_ = node;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
node->right_child_ = rtree;
2023-06-16 19:55:43 +02:00
if (rtree) {
2024-11-08 18:38:20 +01:00
rtree->parent_ = node;
2023-06-16 19:55:43 +02:00
}
repair_node(node);
return node;
}
}
2024-11-08 18:38:20 +01:00
/** Name of this queue. */
const NameType name_;
2024-11-08 18:38:20 +01:00
2023-06-16 19:55:43 +02:00
/** Root node of the queue, or "nullptr" if the queue is empty. */
2024-11-08 18:38:20 +01:00
Node* tree_;
/** Left-most node that belongs to this queue. */
Node* first_node_;
/** Pointer to first sub-queue that got merged into this instance. */
ConcatenableQueue* first_subqueue_;
/** Linked list of sub-queues that were merged to build this queue. */
ConcatenableQueue* next_subqueue_;
2023-06-16 19:55:43 +02:00
};
2024-11-08 18:38:20 +01:00
/* **************************************************
* ** class PriorityQueue **
* ************************************************** */
2023-06-16 19:55:43 +02:00
/**
2024-11-08 18:38:20 +01:00
* Min-priority queue implemented as a binary heap.
*
* Elements in a heap have a priority and associated "data".
*
* The following operations can be done efficiently:
* - Insert an element into the queue.
* - Remove an element from the queue.
* - Change the priority of a given element.
* - Find the element with lowest priority in a given queue.
2023-06-16 19:55:43 +02:00
*/
2024-11-08 18:38:20 +01:00
template <typename PrioType,
typename DataType>
2023-06-16 19:55:43 +02:00
class PriorityQueue
{
public:
typedef unsigned int IndexType;
2024-11-08 18:38:20 +01:00
static constexpr IndexType INVALID_INDEX =
std::numeric_limits<IndexType>::max();
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
/**
* A Node instance represents an element in a PriorityQueue.
*
* A Node instance must remain valid while it is contained in a queue.
* The containing queue holds a pointer to the Node.
*
* A Node instance may be destructed if it is not contained in any queue.
* Alternatively, a Node instance may be destructed after its containing
* queue instance has been destructed.
*/
class Node
2023-06-16 19:55:43 +02:00
{
2024-11-08 18:38:20 +01:00
public:
/** Construct an invalid node, not contained in any queue. */
2023-06-16 19:55:43 +02:00
Node()
2024-11-08 18:38:20 +01:00
: index_(INVALID_INDEX)
2023-06-16 19:55:43 +02:00
{ }
// Prevent copying.
Node(const Node&) = delete;
Node& operator=(const Node&) = delete;
2024-11-08 18:38:20 +01:00
/** Return true if this node is contained in a queue. */
bool valid() const
{
return (index_ != INVALID_INDEX);
}
/**
* Return the priority of this node in the queue.
*
* The node must be contained in a queue.
* This function takes time O(log(n)).
*/
PrioType prio() const
{
assert(index_ != INVALID_INDEX);
return prio_;
}
private:
IndexType index_;
PrioType prio_;
DataType data_;
2023-06-16 19:55:43 +02:00
friend class PriorityQueue;
};
/** Construct an empty queue. */
PriorityQueue()
{ }
// Prevent copying.
PriorityQueue(const PriorityQueue&) = delete;
PriorityQueue& operator=(const PriorityQueue&) = delete;
/**
* Remove all elements from the queue.
*
* This function takes time O(n).
*/
void clear()
{
2024-11-08 18:38:20 +01:00
for (Node* node : heap_) {
node->index_ = INVALID_INDEX;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
heap_.clear();
2023-06-16 19:55:43 +02:00
}
/** Return true if the queue is empty. */
bool empty() const
{
2024-11-08 18:38:20 +01:00
return heap_.empty();
2023-06-16 19:55:43 +02:00
}
/**
2024-11-08 18:38:20 +01:00
* Return the minimum priority of any element in the queue.
2023-06-16 19:55:43 +02:00
*
* The queue must be non-empty.
2024-11-08 18:38:20 +01:00
* This function takes time O(1).
*/
PrioType min_prio() const
{
assert(! heap_.empty());
Node* top = heap_.front();
return top->prio_;
}
/**
* Return the element with minimum priority.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* The queue must be non-empty.
2023-06-16 19:55:43 +02:00
* This function takes time O(1).
*/
2024-11-08 18:38:20 +01:00
DataType min_elem() const
2023-06-16 19:55:43 +02:00
{
2024-11-08 18:38:20 +01:00
assert(! heap_.empty());
Node* top = heap_.front();
return top->data_;
2023-06-16 19:55:43 +02:00
}
/**
2024-11-08 18:38:20 +01:00
* Insert the given node into the queue with associated data.
2023-06-16 19:55:43 +02:00
*
2024-11-08 18:38:20 +01:00
* The node must not currently be contained in any queue.
* This function takes amortized time O(log(n)).
2023-06-16 19:55:43 +02:00
*/
void insert(Node* node, PrioType prio, const DataType& data)
{
2024-11-08 18:38:20 +01:00
assert(node->index_ == INVALID_INDEX);
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
node->index_ = heap_.size();
node->prio_ = prio;
node->data_ = data;
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
heap_.push_back(node);
sift_up(node->index_);
2023-06-16 19:55:43 +02:00
}
/**
2024-11-08 18:38:20 +01:00
* Update priority of an existing node.
2023-06-16 19:55:43 +02:00
*
* This function takes time O(log(n)).
*/
2024-11-08 18:38:20 +01:00
void set_prio(Node* node, PrioType prio)
2023-06-16 19:55:43 +02:00
{
2024-11-08 18:38:20 +01:00
IndexType index = node->index_;
2023-06-16 19:55:43 +02:00
assert(index != INVALID_INDEX);
2024-11-08 18:38:20 +01:00
assert(heap_[index] == node);
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
PrioType prev_prio = node->prio_;
node->prio_ = prio;
2023-06-16 19:55:43 +02:00
if (prio < prev_prio) {
sift_up(index);
} else if (prio > prev_prio) {
sift_down(index);
}
}
/**
* Remove the specified element from the queue.
*
* This function takes time O(log(n)).
*/
void remove(Node* node)
{
2024-11-08 18:38:20 +01:00
IndexType index = node->index_;
2023-06-16 19:55:43 +02:00
assert(index != INVALID_INDEX);
2024-11-08 18:38:20 +01:00
assert(heap_[index] == node);
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
node->index_ = INVALID_INDEX;
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
Node* move_node = heap_.back();
heap_.pop_back();
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
if (index < heap_.size()) {
heap_[index] = move_node;
move_node->index_ = index;
if (move_node->prio_ < node->prio_) {
2023-06-16 19:55:43 +02:00
sift_up(index);
2024-11-08 18:38:20 +01:00
} else if (move_node->prio_ > node->prio_) {
2023-06-16 19:55:43 +02:00
sift_down(index);
}
}
}
private:
/** Repair the heap along an ascending path to the root. */
void sift_up(IndexType index)
{
2024-11-08 18:38:20 +01:00
Node* node = heap_[index];
PrioType prio = node->prio_;
2023-06-16 19:55:43 +02:00
while (index > 0) {
IndexType next_index = (index - 1) / 2;
2024-11-08 18:38:20 +01:00
Node* next_node = heap_[next_index];
if (next_node->prio_ <= prio) {
2023-06-16 19:55:43 +02:00
break;
}
2024-11-08 18:38:20 +01:00
heap_[index] = next_node;
next_node->index_ = index;
2023-06-16 19:55:43 +02:00
index = next_index;
}
2024-11-08 18:38:20 +01:00
node->index_ = index;
heap_[index] = node;
2023-06-16 19:55:43 +02:00
}
/** Repair the heap along a descending path. */
void sift_down(IndexType index)
{
2024-11-08 18:38:20 +01:00
Node* node = heap_[index];
PrioType prio = node->prio_;
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
IndexType num_elem = heap_.size();
2023-06-16 19:55:43 +02:00
2024-11-08 18:38:20 +01:00
while (index < num_elem / 2) {
2023-06-16 19:55:43 +02:00
IndexType next_index = 2 * index + 1;
2024-11-08 18:38:20 +01:00
Node* next_node = heap_[next_index];
2023-06-16 19:55:43 +02:00
if (next_index + 1 < num_elem) {
2024-11-08 18:38:20 +01:00
Node* tmp_node = heap_[next_index + 1];
if (tmp_node->prio_ <= next_node->prio_) {
2023-06-16 19:55:43 +02:00
++next_index;
next_node = tmp_node;
}
}
2024-11-08 18:38:20 +01:00
if (next_node->prio_ >= prio) {
2023-06-16 19:55:43 +02:00
break;
}
2024-11-08 18:38:20 +01:00
heap_[index] = next_node;
next_node->index_ = index;
2023-06-16 19:55:43 +02:00
index = next_index;
}
2024-11-08 18:38:20 +01:00
heap_[index] = node;
node->index_ = index;
2023-06-16 19:55:43 +02:00
}
2024-11-08 18:38:20 +01:00
/** Heap data structure. */
std::vector<Node*> heap_;
2023-06-16 19:55:43 +02:00
};