qse/include/qse/cmn/RedBlackTree.hpp

1021 lines
23 KiB
C++

/*
* $Id$
*
Copyright (c) 2006-2019 Chung, Hyung-Hwan. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _QSE_CMN_REDBLACKTREE_HPP_
#define _QSE_CMN_REDBLACKTREE_HPP_
/// \file
/// Provides the RedBlackTree class.
#include <qse/Types.hpp>
#include <qse/cmn/Mpool.hpp>
/////////////////////////////////
QSE_BEGIN_NAMESPACE(QSE)
/////////////////////////////////
template <typename T, typename COMPARATOR> class RedBlackTree;
template <typename T, typename COMPARATOR>
class RedBlackTreeNode
{
public:
friend class RedBlackTree<T,COMPARATOR>;
typedef RedBlackTreeNode<T,COMPARATOR> SelfType;
enum Color
{
RED,
BLACK
};
// You must take extreme care not to screw up the whole tree by
// overriding the 'value' variable with a randome value.
T value; // you can use this variable or accessor functions below
protected:
Color color;
SelfType* up;
SelfType* left; // left child
SelfType* right; // right child
RedBlackTreeNode(): color (BLACK), up (this), left (this), right (this)
{
// no initialization on 'value' in this constructor.
}
RedBlackTreeNode(const T& value, Color color, SelfType* up, SelfType* left, SelfType* right):
value (value), color (color), up (up), left (left), right (right)
{
QSE_ASSERT (up != this);
QSE_ASSERT (left != this);
QSE_ASSERT (right != this);
}
public:
T& getValue () { return this->value; }
const T& getValue () const { return this->value; }
void setValue (const T& v) { this->value = v; }
bool isNil () const
{
return this->up == this; // && this->left == this && this->right == this;
}
bool notNil () const
{
return !this->isNil ();
}
bool isBlack () const { return this->color == BLACK; }
bool isRed () const { return this->color == RED; }
SelfType* getUpNode () { return this->up; }
const SelfType* getUpNode () const { return this->up; }
SelfType* getLeftNode () { return this->left; }
const SelfType* getLeftNode () const { return this->left; }
SelfType* getRightNode () { return this->right; }
const SelfType* getRightNode () const { return this->right; }
//void setBlack () { this->color = BLACK; }
//void setRed () { this->color = RED; }
//void setUpNode (SelfType* node) { this->up = node; }
//void setLeftNode (SelfType* node) { this->left = node; }
//void setRightNode (SelfType* node) { this->right = node; }
};
template <typename T>
struct RedBlackTreeComparator
{
int operator() (const T& v1, const T& v2) const
{
if (v1 > v2) return 1;
if (v1 < v2) return -1;
QSE_ASSERT (v1 == v2);
return 0;
}
};
template <typename T, typename COMPARATOR, typename GET_NODE, typename GET_T>
class RedBlackTreeIterator
{
public:
typedef RedBlackTreeNode<T,COMPARATOR> Node;
typedef RedBlackTreeIterator<T,COMPARATOR,GET_NODE,GET_T> SelfType;
typedef RedBlackTreeComparator<T> DefaultComparator;
typedef Node* (Node::*GetChild) ();
enum Mode
{
ASCENDING,
DESCENDING
};
RedBlackTreeIterator ():
pending_action (0), current (QSE_NULL), previous (QSE_NULL),
get_left (QSE_NULL), get_right (QSE_NULL)
{
}
RedBlackTreeIterator (Node* root, Mode mode): pending_action (0), current (root)
{
QSE_ASSERT (root != QSE_NULL);
this->previous = root->getUpNode();
if (mode == DESCENDING)
{
this->get_left = &Node::getRightNode;
this->get_right = &Node::getLeftNode;
}
else
{
this->get_left = &Node::getLeftNode;
this->get_right = &Node::getRightNode;
}
this->__move_to_next_node ();
}
protected:
void __move_to_next_node ()
{
QSE_ASSERT (this->current != QSE_NULL);
while (this->current->notNil())
{
if (this->previous == this->current->getUpNode())
{
/* the previous node is the parent of the current node.
* it indicates that we're going down to the getChild(l) */
if ((this->current->*this->get_left)()->notNil())
{
/* go to the left child */
this->previous = this->current;
this->current = (this->current->*this->get_left)();
}
else
{
this->pending_action = 1;
break;
}
}
else if (this->previous == (this->current->*this->get_left)())
{
/* the left child has been already traversed */
this->pending_action = 2;
break;
}
else
{
/* both the left child and the right child have been traversed */
QSE_ASSERT (this->previous == (this->current->*this->get_right)());
/* just move up to the parent */
this->previous = this->current;
this->current = this->current->getUpNode();
}
}
}
void move_to_next_node ()
{
if (pending_action == 1)
{
if ((this->current->*this->get_right)()->notNil())
{
/* go down to the right node if exists */
this->previous = this->current;
this->current = (this->current->*this->get_right)();
}
else
{
/* otherwise, move up to the parent */
this->previous = this->current;
this->current = this->current->getUpNode();
}
}
else if (pending_action == 2)
{
if ((this->current->*this->get_right)()->notNil())
{
/* go down to the right node if it exists */
this->previous = this->current;
this->current = (this->current->*this->get_right)();
}
else
{
/* otherwise, move up to the parent */
this->previous = this->current;
this->current = this->current->getUpNode();
}
}
this->__move_to_next_node ();
}
public:
SelfType& operator++ () // prefix increment
{
this->move_to_next_node ();
return *this;
}
SelfType operator++ (int) // postfix increment
{
SelfType saved (*this);
this->move_to_next_node ();
return saved;
}
// no operator--
bool operator== (const SelfType& it) const
{
QSE_ASSERT (this->current != QSE_NULL);
return this->current == it.current && this->previous == it.previous;
}
bool operator!= (const SelfType& it) const
{
QSE_ASSERT (this->current != QSE_NULL);
return this->current != it.current || this->previous != it.previous;
}
bool isLegit() const
{
return current->notNil();
}
GET_T& operator* () // dereference
{
return this->current->getValue();
}
GET_T& getValue ()
{
return this->current->getValue();
}
// no setValue().
GET_NODE* getNode ()
{
return this->current;
}
protected:
int pending_action;
Node* current;
Node* previous;
//Node* (Node::*get_left) ();
//Node* (Node::*get_right) ();
GetChild get_left;
GetChild get_right;
};
/// The RedBlackTree class implements the red-black tree data structure.
///
/// A node is either red or black.
/// The root is black.
/// All leaves (NIL) are black. (All leaves are same color as the root.)
/// Every red node must have two black child nodes.
/// Every path from a given node to any of its descendant NIL nodes contains the same number of black nodes.
///
/// \sa RedBlackTable, qse_rbt_t
///
template <typename T, typename COMPARATOR = RedBlackTreeComparator<T> >
class RedBlackTree: public Mmged
{
public:
typedef RedBlackTree<T,COMPARATOR> SelfType;
typedef RedBlackTreeNode<T,COMPARATOR> Node;
typedef RedBlackTreeIterator<T,COMPARATOR,Node,T> Iterator;
typedef RedBlackTreeIterator<T,COMPARATOR,const Node,const T> ConstIterator;
typedef RedBlackTreeComparator<T> DefaultComparator;
private:
void init_tree ()
{
#if defined(QSE_REDBLACKTREE_ALLOCATE_NIL)
// create a nil object. note it doesn't go into the memory pool.
// the nil node allocated inside the memory pool makes implementation
// of this->clear (true) difficult as disposal of memory pool
// also deallocates the nil node.
this->nil = new(this->getMmgr()) Node();
#else
this->nil = &this->xnil;
#endif
// set root to nil
this->root = this->nil;
}
public:
RedBlackTree (Mmgr* mmgr = QSE_NULL): Mmged(mmgr), mp(mmgr, QSE_SIZEOF(Node), 0), node_count(0)
{
this->init_tree ();
}
RedBlackTree (qse_size_t mpb_size, Mmgr* mmgr = QSE_NULL): Mmged(mmgr), mp(QSE_NULL, QSE_SIZEOF(Node), mpb_size), node_count(0)
{
this->init_tree ();
}
RedBlackTree (const SelfType& rbt):
Mmged(rbt.getMmgr()),
mp(rbt.getMmgr(), rbt.mp.getDatumSize(), rbt.mp.getBlockSize()),
node_count(0)
{
#if defined(QSE_REDBLACKTREE_ALLOCATE_NIL)
// create a nil object. note it doesn't go into the memory pool.
// the nil node allocated inside the memory pool makes implementation
// of this->clear (true) difficult as disposal of memory pool
// also deallocates the nil node.
this->nil = new(this->getMmgr()) Node();
#else
this->nil = &this->xnil;
#endif
// set root to nil
this->root = this->nil;
// TODO: do the level-order traversal to minimize rebalancing.
Iterator it = rbt.getIterator();
while (it.isLegit())
{
this->insert (*it);
++it;
}
}
// TODO: move constructors and move assignment operators for c++11
~RedBlackTree ()
{
this->clear (true);
#if defined(QSE_REDBLACKTREE_ALLOCATE_NIL)
// destroy the nil node.
QSE_CPP_CALL_DESTRUCTOR (this->nil, Node);
QSE_CPP_CALL_PLACEMENT_DELETE1 (this->nil, this->getMmgr());
#else
// do nothing
#endif
}
SelfType& operator= (const SelfType& rbt)
{
if (this != &rbt)
{
this->clear (false);
// TODO: do the level-order traversal to minimize rebalancing.
Iterator it = rbt.getIterator();
while (it.isLegit())
{
this->insert (*it);
++it;
}
}
return *this;
}
Mpool& getMpool ()
{
return this->mp;
}
const Mpool& getMpool () const
{
return this->mp;
}
qse_size_t getSize () const
{
return this->node_count;
}
bool isEmpty () const
{
return this->node_count <= 0;
}
/// The getRootNode() function gets the pointer to the root node.
/// If no node exists in the tree, it returns #QSE_NULL.
Node* getRootNode ()
{
return this->root->isNil()? QSE_NULL: this->root;
}
const Node* getRootNode () const
{
return this->root->isNil()? QSE_NULL: this->root;
}
protected:
void dispose_node (Node* node)
{
QSE_CPP_CALL_DESTRUCTOR (node, Node);
QSE_CPP_CALL_PLACEMENT_DELETE1 (node, &this->mp);
}
Node* find_node (const T& datum) const
{
Node* node = this->root;
// normal binary tree search
while (node->notNil())
{
int n = this->comparator (datum, node->value);
if (n == 0) return node;
if (n > 0) node = node->right;
else /* if (n < 0) */ node = node->left;
}
return QSE_NULL;
}
template <typename MT, typename MCOMPARATOR>
Node* heterofind_node (const MT& datum) const
{
MCOMPARATOR mcomparator;
Node* node = this->root;
// normal binary tree search
while (node->notNil())
{
int n = mcomparator (datum, node->value);
if (n == 0) return node;
if (n > 0) node = node->right;
else /* if (n < 0) */ node = node->left;
}
return QSE_NULL;
}
void rotate (Node* pivot, bool leftwise)
{
/*
* == leftwise rotation
* move the pivot pair down to the poistion of the pivot's original
* left child(x). move the pivot's right child(y) to the pivot's original
* position. as 'c1' is between 'y' and 'pivot', move it to the right
* of the new pivot position.
* parent parent
* | | (left or right?) | |
* pivot y
* / \ / \
* x y =====> pivot c2
* / \ / \
* c1 c2 x c1
*
* == rightwise rotation
* move the pivot pair down to the poistion of the pivot's original
* right child(y). move the pivot's left child(x) to the pivot's original
* position. as 'c2' is between 'x' and 'pivot', move it to the left
* of the new pivot position.
*
* parent parent
* | | (left or right?) | |
* pivot x
* / \ / \
* x y =====> c1 pivot
* / \ / \
* c1 c2 c2 y
*
*
* the actual implementation here resolves the pivot's relationship to
* its parent by comparaing pointers as it is not known if the pivot pair
* is the left child or the right child of its parent,
*/
Node* up, * z, * c;
QSE_ASSERT (pivot != QSE_NULL);
up = pivot->up;
if (leftwise)
{
// y for leftwise rotation
z = pivot->right;
// c1 for leftwise rotation
c = z->left;
}
else
{
// x for rightwise rotation
z = pivot->left;
// c2 for rightwise rotation
c = z->right;
}
z->up = up;
if (up->notNil())
{
if (up->left == pivot)
{
up->left = z;
}
else
{
QSE_ASSERT (up->right == pivot);
up->right = z;
}
}
else
{
QSE_ASSERT (this->root == pivot);
this->root = z;
}
if (leftwise)
{
z->left = pivot;
pivot->right = c;
}
else
{
z->right = pivot;
pivot->left = c;
}
if (pivot->notNil()) pivot->up = z;
if (c->notNil()) c->up = pivot;
}
void rotate_left (Node* pivot)
{
this->rotate (pivot, true);
}
void rotate_right (Node* pivot)
{
this->rotate (pivot, false);
}
void rebalance_for_injection (Node* node)
{
while (node != this->root)
{
Node* tmp, * tmp2, * x_par, * x_grand_par;
bool leftwise;
x_par = node->up;
if (x_par->color == Node::BLACK) break;
QSE_ASSERT (x_par->up->notNil());
x_grand_par = x_par->up;
if (x_par == x_grand_par->left)
{
tmp = x_grand_par->right;
tmp2 = x_par->right;
leftwise = true;
}
else
{
tmp = x_grand_par->left;
tmp2 = x_par->left;
leftwise = false;
}
if (tmp->color == Node::RED)
{
x_par->color = Node::BLACK;
tmp->color = Node::BLACK;
x_grand_par->color = Node::RED;
node = x_grand_par;
}
else
{
if (node == tmp2)
{
node = x_par;
this->rotate (node, leftwise);
x_par = node->up;
x_grand_par = x_par->up;
}
x_par->color = Node::BLACK;
x_grand_par->color = Node::RED;
this->rotate (x_grand_par, !leftwise);
}
}
}
void rebalance_for_removal (Node* node, Node* par)
{
while (node != this->root && node->color == Node::BLACK)
{
Node* tmp;
if (node == par->left)
{
tmp = par->right;
if (tmp->color == Node::RED)
{
tmp->color = Node::BLACK;
par->color = Node::RED;
this->rotate_left (par);
tmp = par->right;
}
if (tmp->left->color == Node::BLACK &&
tmp->right->color == Node::BLACK)
{
if (tmp->notNil()) tmp->color = Node::RED;
node = par;
par = node->up;
}
else
{
if (tmp->right->color == Node::BLACK)
{
if (tmp->left->notNil())
tmp->left->color = Node::BLACK;
tmp->color = Node::RED;
this->rotate_right (tmp);
tmp = par->right;
}
tmp->color = par->color;
if (par->notNil()) par->color = Node::BLACK;
if (tmp->right->color == Node::RED)
tmp->right->color = Node::BLACK;
this->rotate_left (par);
node = this->root;
}
}
else
{
QSE_ASSERT (node == par->right);
tmp = par->left;
if (tmp->color == Node::RED)
{
tmp->color = Node::BLACK;
par->color = Node::RED;
this->rotate_right (par);
tmp = par->left;
}
if (tmp->left->color == Node::BLACK &&
tmp->right->color == Node::BLACK)
{
if (tmp->notNil()) tmp->color = Node::RED;
node = par;
par = node->up;
}
else
{
if (tmp->left->color == Node::BLACK)
{
if (tmp->right->notNil())
tmp->right->color = Node::BLACK;
tmp->color = Node::RED;
this->rotate_left (tmp);
tmp = par->left;
}
tmp->color = par->color;
if (par->notNil()) par->color = Node::BLACK;
if (tmp->left->color == Node::RED)
tmp->left->color = Node::BLACK;
this->rotate_right (par);
node = this->root;
}
}
}
node->color = Node::BLACK;
}
void remove_node (Node* node)
{
Node* x, * y, * par;
QSE_ASSERT (node && node->notNil());
if (node->left->isNil() || node->right->isNil())
{
y = node;
}
else
{
/* find a successor with NIL as a child */
y = node->right;
while (y->left->notNil()) y = y->left;
}
x = (y->left->isNil())? y->right: y->left;
par = y->up;
if (x->notNil()) x->up = par;
if (par->notNil()) // if (par)
{
if (y == par->left)
par->left = x;
else
par->right = x;
}
else
{
this->root = x;
}
if (y == node)
{
if (y->color == Node::BLACK && x->notNil())
this->rebalance_for_removal (x, par);
this->dispose_node (y);
}
else
{
if (y->color == Node::BLACK && x->notNil())
this->rebalance_for_removal (x, par);
if (node->up->notNil()) //if (node->up)
{
if (node->up->left == node) node->up->left = y;
if (node->up->right == node) node->up->right = y;
}
else
{
this->root = y;
}
y->up = node->up;
y->left = node->left;
y->right = node->right;
y->color = node->color;
if (node->left->up == node) node->left->up = y;
if (node->right->up == node) node->right->up = y;
this->dispose_node (node);
}
this->node_count--;
}
public:
Node* findNode (const T& datum)
{
return this->find_node (datum);
}
const Node* findNode (const T& datum) const
{
return this->find_node (datum);
}
T* findValue (const T& datum)
{
Node* b = this->findNode (datum);
if (!b) return QSE_NULL;
return &b->value;
}
const T* findValue (const T& datum) const
{
const Node* b = this->findNode (datum);
if (!b) return QSE_NULL;
return &b->value;
}
template <typename MT, typename MCOMPARATOR>
Node* heterofindNode (const MT& datum)
{
return this->heterofind_node<MT,MCOMPARATOR> (datum);
}
template <typename MT, typename MCOMPARATOR>
const Node* heterofindNode (const MT& datum) const
{
return this->heterofind_node<MT,MCOMPARATOR> (datum);
}
template <typename MT, typename MCOMPARATOR>
T* heterofindValue(const MT& datum)
{
Node* b = this->heterofind_node<MT,MCOMPARATOR> (datum);
if (!b) return QSE_NULL;
return &b->value;
}
template <typename MT, typename MCOMPARATOR>
const T* heterofindValue(const MT& datum) const
{
Node* b = this->heterofind_node<MT,MCOMPARATOR> (datum);
if (!b) return QSE_NULL;
return &b->value;
}
Node* search (const T& datum)
{
return this->find_node (datum);
}
const Node* search (const T& datum) const
{
return this->find_node (datum);
}
template <typename MT, typename MCOMPARATOR>
Node* heterosearch (const MT& datum)
{
return this->heterofind_node<MT,MCOMPARATOR> (datum);
}
template <typename MT, typename MCOMPARATOR>
const Node* heterosearch (const MT& datum) const
{
return this->heterofind_node<MT,MCOMPARATOR> (datum);
}
/// The inject() function inserts a \a datum if no existing datum
/// is found to be equal using the comparator. The \a mode argument
/// determines what action to take when an equal datum is found.
/// - -1: failure
/// - 0: do nothing
/// - 1: overwrite the existing datum
///
/// if \a injected is not #QSE_NULL, it is set to true when \a datum
/// has been inserted newly and false when an equal datum has been
/// found.
///
/// The function returns the poniter to the node inserted or
/// affected. It return #QSE_NULL if mode is set to -1 and a duplicate
/// item has been found.
Node* inject (const T& datum, int mode, bool* injected = QSE_NULL)
{
Node* x_cur = this->root;
Node* x_par = this->nil;
while (x_cur->notNil())
{
int n = this->comparator (datum, x_cur->value);
if (n == 0)
{
if (injected) *injected = false;
if (mode <= -1) return QSE_NULL; // return failure
if (mode >= 1) x_cur->value = datum;
return x_cur;
}
x_par = x_cur;
if (n > 0) x_cur = x_cur->right;
else /* if (n < 0) */ x_cur = x_cur->left;
}
Node* x_new = new(&this->mp) Node (datum, Node::RED, this->nil, this->nil, this->nil);
if (x_par->isNil())
{
QSE_ASSERT (this->root->isNil());
this->root = x_new;
}
else
{
int n = this->comparator (datum, x_par->value);
if (n > 0)
{
QSE_ASSERT (x_par->right->isNil());
x_par->right = x_new;
}
else
{
QSE_ASSERT (x_par->left->isNil());
x_par->left = x_new;
}
x_new->up = x_par;
this->rebalance_for_injection (x_new);
}
this->root->color = Node::BLACK;
this->node_count++;
if (injected) *injected = true; // indicate that a new node has been injected
return x_new;
}
Node* insert (const T& datum)
{
return this->inject (datum, -1, QSE_NULL);
}
Node* ensert (const T& datum)
{
return this->inject (datum, 0, QSE_NULL);
}
Node* upsert (const T& datum)
{
return this->inject (datum, 1, QSE_NULL);
}
Node* update (const T& datum)
{
Node* node = this->find_node (datum);
if (node) node->value = datum;
return node;
}
int remove (const T& datum)
{
Node* node = this->find_node (datum);
if (node == QSE_NULL) return -1;
this->remove_node (node);
return 0;
}
template <typename MT, typename MCOMPARATOR>
int heteroremove (const MT& datum)
{
Node* node = this->QSE_CPP_TEMPLATE_QUALIFIER heterofind_node<MT,MCOMPARATOR> (datum);
if (node == QSE_NULL) return -1;
this->remove_node (node);
return 0;
}
void clear (bool clear_mpool = false)
{
while (this->root->notNil()) this->remove_node (this->root);
QSE_ASSERT (this->root = this->nil);
QSE_ASSERT (this->node_count == 0);
if (clear_mpool) this->mp.dispose ();
}
Iterator getIterator (typename Iterator::Mode mode = Iterator::ASCENDING) const
{
return Iterator (this->root, mode);
}
ConstIterator getConstIterator (typename ConstIterator::Mode mode = ConstIterator::ASCENDING) const
{
return ConstIterator (this->root, mode);
}
protected:
Mpool mp;
COMPARATOR comparator;
qse_size_t node_count;
#if defined(QSE_REDBLACKTREE_ALLOCATE_NIL)
// nothing. let the constructor allocate it to this->nil.
#else
// use a statically declared nil object.
Node xnil;
#endif
Node* nil; // internal node to present nil
Node* root; // root node.
};
/////////////////////////////////
QSE_END_NAMESPACE(QSE)
/////////////////////////////////
#endif