blob: 06e71a9280ad0cf62e1bfb355f2b182c5c0b0512 [file] [log] [blame]
/**
* Quick balanced binary search tree
*
* @version 2017-02-14_001
* @author Robert Altnoeder (r.altnoeder@gmx.net)
*
* Copyright (C) 2012 - 2017 Robert ALTNOEDER
*
* 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.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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 QTREE_H
#define QTREE_H
#include <cstddef>
#include <new>
#include <dsaext.h>
template<typename K, typename V>
class QTree : public dsaext::Map<K, V>
{
private:
enum qtree_dir
{
DIR_LESS,
DIR_GREATER
};
public:
typedef int (*compare_func)(const K* key, const K* other);
class Node : dsaext::Map<K, V>::Node
{
friend class QTree;
private:
K* key {nullptr};
V* value {nullptr};
Node* less {nullptr};
Node* greater {nullptr};
Node* parent {nullptr};
int balance {0};
public:
Node(K* key_ptr, V* value_ptr)
{
key = key_ptr;
value = value_ptr;
}
Node(const Node& orig):
key(orig.key),
value(orig.value)
{
}
Node& operator=(const Node& orig)
{
if (this != &orig)
{
key = orig.key;
value = orig.value;
}
return *this;
}
Node(Node&& orig) = default;
Node& operator=(Node&& orig) = default;
virtual ~Node() = default;
virtual K* get_key() const
{
return key;
}
virtual V* get_value() const
{
return value;
}
virtual void reuse()
{
key = nullptr;
value = nullptr;
less = nullptr;
greater = nullptr;
parent = nullptr;
balance = 0;
}
};
protected:
template<typename T>
class BaseIterator : public dsaext::QIterator<T>
{
public:
BaseIterator(QTree<K, V>& qtree_ref):
qtree_obj(qtree_ref)
{
if (qtree_obj.root != nullptr)
{
for (iter_node = qtree_obj.root;
iter_node->less != nullptr;
iter_node = iter_node->less)
{
// intentional no-op block
}
}
else
{
iter_node = nullptr;
}
}
BaseIterator(const BaseIterator& orig) = default;
BaseIterator& operator=(const BaseIterator& orig) = default;
BaseIterator(BaseIterator&& orig) = default;
BaseIterator& operator=(BaseIterator&& orig) = default;
virtual ~BaseIterator()
{
}
virtual T* next() = 0;
virtual bool has_next() const
{
return iter_node != nullptr;
}
virtual size_t get_size() const
{
return qtree_obj.size;
}
protected:
virtual Node* next_node()
{
Node* ret_node = iter_node;
if (ret_node != nullptr)
{
if (iter_node->greater != nullptr)
{
for (iter_node = iter_node->greater;
iter_node->less != nullptr;
iter_node = iter_node->less)
{
// intentional no-op block
}
}
else
{
do
{
if (iter_node->parent != nullptr)
{
if (iter_node->parent->less == iter_node)
{
iter_node = iter_node->parent;
break;
}
}
iter_node = iter_node->parent;
}
while (iter_node != nullptr);
}
}
return ret_node;
}
private:
QTree& qtree_obj;
Node* iter_node {nullptr};
};
public:
class KeysIterator : public BaseIterator<K>
{
public:
KeysIterator(QTree<K, V>& qtree_ref):
BaseIterator<K>::BaseIterator(qtree_ref)
{
}
KeysIterator(const KeysIterator& orig) = default;
KeysIterator& operator=(const KeysIterator& orig) = default;
KeysIterator(KeysIterator&& orig) = default;
KeysIterator& operator=(KeysIterator&& orig) = default;
virtual ~KeysIterator()
{
}
virtual K* next()
{
K* iter_key {nullptr};
Node* node = BaseIterator<K>::next_node();
if (node != nullptr)
{
iter_key = node->key;
}
return iter_key;
}
};
class ValuesIterator : public BaseIterator<V>
{
public:
ValuesIterator(QTree<K, V>& qtree_ref):
BaseIterator<V>::BaseIterator(qtree_ref)
{
}
ValuesIterator(const ValuesIterator& orig) = default;
ValuesIterator& operator=(const ValuesIterator& orig) = default;
ValuesIterator(ValuesIterator&& orig) = default;
ValuesIterator& operator=(ValuesIterator&& orig) = default;
virtual ~ValuesIterator()
{
}
virtual V* next()
{
V* iter_value {nullptr};
Node* node = BaseIterator<V>::next_node();
if (node != nullptr)
{
iter_value = node->value;
}
return iter_value;
}
};
class NodesIterator : public BaseIterator<Node>
{
public:
NodesIterator(QTree<K, V>& qtree_ref):
BaseIterator<Node>::BaseIterator(qtree_ref)
{
}
NodesIterator(const NodesIterator& orig) = default;
NodesIterator& operator=(const NodesIterator& orig) = default;
NodesIterator(NodesIterator&& orig) = default;
NodesIterator& operator=(NodesIterator&& orig) = default;
virtual ~NodesIterator()
{
}
virtual Node* next()
{
return BaseIterator<Node>::next_node();
}
};
public:
QTree(compare_func compare_fn):
compare(compare_fn)
{
}
QTree(const QTree& orig) = delete;
QTree& operator=(const QTree& orig) = delete;
QTree(QTree&& orig) = default;
QTree& operator=(QTree&& orig) = default;
virtual ~QTree()
{
clear_impl();
}
virtual V* get(const K* key) const
{
V* value {nullptr};
Node* node = find_node(key);
if (node != nullptr)
{
value = node->value;
}
return value;
}
virtual typename dsaext::Map<K, V>::entry get_entry(const K* key) const
{
typename dsaext::Map<K, V>::entry qtree_entry {nullptr, nullptr};
Node* node = find_node(key);
if (node != nullptr)
{
qtree_entry.key = node->key;
qtree_entry.value = node->value;
}
return qtree_entry;
}
virtual Node* get_node(const K* key) const
{
return find_node(key);
}
// @throws std::bad_alloc, dsaext::DuplicateInsertionException
virtual void insert(K* key, V* value)
{
Node* ins_node = new Node(key, value);
if (!insert_node_impl(ins_node))
{
delete ins_node;
throw dsaext::DuplicateInsertException();
}
}
// @throws std::bad_alloc, dsaext::DuplicateInsertionException
virtual void insert_node(Node* node)
{
if (!insert_node_impl(node))
{
throw dsaext::DuplicateInsertException();
}
}
virtual void remove(const K* key)
{
Node* node = find_node(key);
if (node != nullptr)
{
remove_node_impl(node);
}
}
virtual void remove_node(Node* node)
{
remove_node_impl(node);
}
virtual void unlink_node(Node* node)
{
unlink_node_impl(node);
}
virtual void clear()
{
clear_impl();
root = nullptr;
size = 0;
}
size_t get_size() const
{
return size;
}
private:
inline void clear_impl()
{
Node* node = root;
while (node != nullptr)
{
if (node->less != nullptr)
{
node = node->less;
}
else
if (node->greater != nullptr)
{
node = node->greater;
}
else
{
Node* leaf = node;
node = node->parent;
if (node != nullptr)
{
if (leaf == node->less)
{
node->less = nullptr;
}
else
{
node->greater = nullptr;
}
}
delete leaf;
}
}
}
inline Node* find_node(const K* key) const
{
Node* node = root;
while (node != nullptr)
{
int cmp_rc = compare(key, node->key);
if (cmp_rc < 0)
{
node = node->less;
}
else
if (cmp_rc > 0)
{
node = node->greater;
}
else
{
break;
}
}
return node;
}
inline bool insert_node_impl(Node* ins_node)
{
bool inserted = true;
if (root == nullptr)
{
root = ins_node;
++size;
}
else
{
Node* parent_node = root;
while (true)
{
int cmp_rc = compare(ins_node->key, parent_node->key);
if (cmp_rc < 0)
{
if (parent_node->less == nullptr)
{
parent_node->less = ins_node;
ins_node->parent = parent_node;
++size;
rebalance_insert(ins_node, parent_node);
break;
}
else
{
parent_node = parent_node->less;
}
}
else
if (cmp_rc > 0)
{
if (parent_node->greater == nullptr)
{
parent_node->greater = ins_node;
ins_node->parent = parent_node;
++size;
rebalance_insert(ins_node, parent_node);
break;
}
else
{
parent_node = parent_node->greater;
}
}
else
{
inserted = false;
break;
}
}
}
return inserted;
}
inline void remove_node_impl(Node* rm_node)
{
unlink_node_impl(rm_node);
delete rm_node;
}
inline void unlink_node_impl(Node* rm_node)
{
--size;
if (rm_node->less == nullptr && rm_node->greater == nullptr)
{
// leaf node - removal without replacement
if (root == rm_node)
{
// root node leaf
root = nullptr;
}
else
{
// non-root node leaf
Node* rot_node = rm_node->parent;
QTree::qtree_dir dir;
if (rot_node->less == rm_node)
{
// node to remove is in the left subtree
// of its parent
// save direction
dir = QTree::qtree_dir::DIR_LESS;
rot_node->less = nullptr;
}
else
{
// node to remove is in the right subtree
// of its parent
// save direction
dir = QTree::qtree_dir::DIR_GREATER;
rot_node->greater = nullptr;
}
rebalance_remove(dir, rot_node);
}
}
else
{
Node* replace_node {nullptr};
// not a leaf node, removal by replacement
// at least one child, or a child and a subtree, or two subtrees
// find replacement node
if (rm_node->balance == -1)
{
for (replace_node = rm_node->less;
replace_node->greater != nullptr;
replace_node = replace_node->greater)
{
// intentional no-op block
}
}
else
{
for (replace_node = rm_node->greater;
replace_node->less != nullptr;
replace_node = replace_node->less)
{
// intentional no-op block
}
}
Node* rot_node = replace_node->parent;
QTree::qtree_dir dir;
if (rot_node->less == replace_node)
{
// node to remove is in the left subtree
// of its parent
// save direction
dir = QTree::qtree_dir::DIR_LESS;
if (replace_node->less != nullptr)
{
// replace node by its left child
rot_node->less = replace_node->less;
replace_node->less->parent = rot_node;
}
else
if (replace_node->greater != nullptr)
{
// replace node by its right child
rot_node->less = replace_node->greater;
replace_node->greater->parent = rot_node;
}
else
{
// non-root leaf node
rot_node->less = nullptr;
}
}
else
{
// node to remove is in the right subtree
// of its parent
// save direction
dir = QTree::qtree_dir::DIR_GREATER;
if (replace_node->less != nullptr)
{
// replace node by its left child
rot_node->greater = replace_node->less;
replace_node->less->parent = rot_node;
}
else
if (replace_node->greater != nullptr)
{
// replace node by its right child
rot_node->greater = replace_node->greater;
replace_node->greater->parent = rot_node;
}
else
{
// non-root leaf node
rot_node->greater = nullptr;
}
}
// replace rm_node with replace_node
if (rm_node->parent == nullptr)
{
// Node to be removed is the root node
root = replace_node;
}
else
{
if (rm_node->parent->less == rm_node)
{
rm_node->parent->less = replace_node;
}
else
{
rm_node->parent->greater = replace_node;
}
}
if (rm_node->less != nullptr)
{
rm_node->less->parent = replace_node;
}
if (rm_node->greater != nullptr)
{
rm_node->greater->parent = replace_node;
}
replace_node->parent = rm_node->parent;
replace_node->less = rm_node->less;
replace_node->greater = rm_node->greater;
replace_node->balance = rm_node->balance;
if (rot_node == rm_node)
{
rot_node = replace_node;
}
rebalance_remove(dir, rot_node);
}
}
inline void rebalance_insert(
Node* sub_node,
Node* rot_node
)
{
// update balance and perform rotations
do
{
if (rot_node->less == sub_node)
{
--(rot_node->balance);
}
else
{
++(rot_node->balance);
}
if (rot_node->balance == 0)
{
break;
}
else
if (rot_node->balance == -2)
{
if (sub_node->balance == -1)
{
// rotate R
rot_node->balance = 0;
sub_node->balance = 0;
sub_node->parent = rot_node->parent;
if (rot_node->parent != nullptr)
{
if (rot_node->parent->less == rot_node)
{
rot_node->parent->less = sub_node;
}
else
{
rot_node->parent->greater = sub_node;
}
}
else
{
root = sub_node;
}
rot_node->less = sub_node->greater;
if (sub_node->greater != nullptr)
{
sub_node->greater->parent = rot_node;
}
sub_node->greater = rot_node;
rot_node->parent = sub_node;
}
else
{
// rotate LR
if (sub_node->greater->balance == -1)
{
sub_node->balance = 0;
rot_node->balance = 1;
}
else
if (sub_node->greater->balance == 1)
{
sub_node->balance = -1;
rot_node->balance = 0;
}
else
{
sub_node->balance = 0;
rot_node->balance = 0;
}
sub_node->greater->balance = 0;
sub_node->parent = sub_node->greater;
sub_node->greater = sub_node->greater->less;
sub_node->parent->less = sub_node;
rot_node->less = sub_node->parent->greater;
sub_node->parent->parent = rot_node->parent;
if (sub_node->greater != nullptr)
{
sub_node->greater->parent = sub_node;
}
if (rot_node->less != nullptr)
{
rot_node->less->parent = rot_node;
}
if (rot_node->parent != nullptr)
{
if (rot_node->parent->less == rot_node)
{
rot_node->parent->less = sub_node->parent;
}
else
{
rot_node->parent->greater = sub_node->parent;
}
}
else
{
root = sub_node->parent;
}
rot_node->parent = sub_node->parent;
sub_node->parent->greater = rot_node;
}
break;
}
else
if (rot_node->balance == 2)
{
if (sub_node->balance == 1)
{
// rotate L
rot_node->balance = 0;
sub_node->balance = 0;
sub_node->parent = rot_node->parent;
if (rot_node->parent != nullptr)
{
if (rot_node->parent->less == rot_node)
{
rot_node->parent->less = sub_node;
}
else
{
rot_node->parent->greater = sub_node;
}
}
else
{
root = sub_node;
}
rot_node->greater = sub_node->less;
if (sub_node->less != nullptr)
{
sub_node->less->parent = rot_node;
}
sub_node->less = rot_node;
rot_node->parent = sub_node;
}
else
{
// rotate RL
if (sub_node->less->balance == -1)
{
sub_node->balance = 1;
rot_node->balance = 0;
}
else
if (sub_node->less->balance == 1)
{
sub_node->balance = 0;
rot_node->balance = -1;
}
else
{
sub_node->balance = 0;
rot_node->balance = 0;
}
sub_node->less->balance = 0;
sub_node->parent = sub_node->less;
sub_node->less = sub_node->less->greater;
sub_node->parent->greater = sub_node;
rot_node->greater = sub_node->parent->less;
sub_node->parent->parent = rot_node->parent;
if (sub_node->less != nullptr)
{
sub_node->less->parent = sub_node;
}
if (rot_node->greater != nullptr)
{
rot_node->greater->parent = rot_node;
}
if (rot_node->parent != nullptr)
{
if (rot_node->parent->less == rot_node)
{
rot_node->parent->less = sub_node->parent;
}
else
{
rot_node->parent->greater = sub_node->parent;
}
}
else
{
root = sub_node->parent;
}
rot_node->parent = sub_node->parent;
sub_node->parent->less = rot_node;
}
break;
}
sub_node = rot_node;
rot_node = rot_node->parent;
}
while (rot_node != nullptr);
}
inline void rebalance_remove(qtree_dir dir, Node* rot_node)
{
// update balance and perform rotations
while (rot_node != nullptr)
{
if (dir == QTree::qtree_dir::DIR_LESS)
{
// node was removed from left subtree
++rot_node->balance;
if (rot_node->balance == 1)
{
break;
}
}
else
{
// node was removed from right subtree
--rot_node->balance;
if (rot_node->balance == -1)
{
break;
}
}
if (rot_node->parent != nullptr)
{
if (rot_node->parent->less == rot_node)
{
dir = QTree::qtree_dir::DIR_LESS;
}
else
{
dir = QTree::qtree_dir::DIR_GREATER;
}
}
// update balance and perform rotations
if (rot_node->balance == -2)
{
Node* sub_node = rot_node->less;
// 0 or -1
if (sub_node->balance <= 0)
{
// rotate R
sub_node->parent = rot_node->parent;
if (rot_node->parent != nullptr)
{
if (rot_node->parent->less == rot_node)
{
rot_node->parent->less = sub_node;
}
else
{
rot_node->parent->greater = sub_node;
}
}
else
{
root = sub_node;
}
rot_node->less = sub_node->greater;
if (sub_node->greater != nullptr)
{
sub_node->greater->parent = rot_node;
}
sub_node->greater = rot_node;
rot_node->parent = sub_node;
if (sub_node->balance == 0)
{
rot_node->balance = -1;
sub_node->balance = 1;
break;
}
else
{
rot_node->balance = 0;
sub_node->balance = 0;
}
}
else
{
// rotate LR
if (sub_node->greater->balance == -1)
{
sub_node->balance = 0;
rot_node->balance = 1;
}
else
if (sub_node->greater->balance == 1)
{
sub_node->balance = -1;
rot_node->balance = 0;
}
else
{
sub_node->balance = 0;
rot_node->balance = 0;
}
sub_node->greater->balance = 0;
sub_node->parent = sub_node->greater;
sub_node->greater = sub_node->greater->less;
sub_node->parent->less = sub_node;
rot_node->less = sub_node->parent->greater;
sub_node->parent->parent = rot_node->parent;
if (sub_node->greater != nullptr)
{
sub_node->greater->parent = sub_node;
}
if (rot_node->less != nullptr)
{
rot_node->less->parent = rot_node;
}
if (rot_node->parent != nullptr)
{
if (rot_node->parent->less == rot_node)
{
rot_node->parent->less = sub_node->parent;
}
else
{
rot_node->parent->greater = sub_node->parent;
}
}
else
{
root = sub_node->parent;
}
rot_node->parent = sub_node->parent;
sub_node->parent->greater = rot_node;
}
rot_node = rot_node->parent;
// end of R / LR rotations
}
else
if (rot_node->balance == 2)
{
Node* sub_node = rot_node->greater;
// 0 or 1
if (sub_node->balance >= 0)
{
// rotate L
sub_node->parent = rot_node->parent;
if (rot_node->parent != nullptr)
{
if (rot_node->parent->less == rot_node)
{
rot_node->parent->less = sub_node;
}
else
{
rot_node->parent->greater = sub_node;
}
}
else
{
root = sub_node;
}
rot_node->greater = sub_node->less;
if (sub_node->less != nullptr)
{
sub_node->less->parent = rot_node;
}
sub_node->less = rot_node;
rot_node->parent = sub_node;
if (sub_node->balance == 0)
{
rot_node->balance = 1;
sub_node->balance = -1;
break;
}
else
{
rot_node->balance = 0;
sub_node->balance = 0;
}
}
else
{
// rotate RL
if (sub_node->less->balance == -1)
{
sub_node->balance = 1;
rot_node->balance = 0;
}
else
if (sub_node->less->balance == 1)
{
sub_node->balance = 0;
rot_node->balance = -1;
}
else
{
sub_node->balance = 0;
rot_node->balance = 0;
}
sub_node->less->balance = 0;
sub_node->parent = sub_node->less;
sub_node->less = sub_node->less->greater;
sub_node->parent->greater = sub_node;
rot_node->greater = sub_node->parent->less;
sub_node->parent->parent = rot_node->parent;
if (sub_node->less != nullptr)
{
sub_node->less->parent = sub_node;
}
if (rot_node->greater != nullptr)
{
rot_node->greater->parent = rot_node;
}
if (rot_node->parent != nullptr)
{
if (rot_node->parent->less == rot_node)
{
rot_node->parent->less = sub_node->parent;
}
else
{
rot_node->parent->greater = sub_node->parent;
}
}
else
{
root = sub_node->parent;
}
rot_node->parent = sub_node->parent;
sub_node->parent->less = rot_node;
}
rot_node = rot_node->parent;
// end of L / RL rotations
}
rot_node = rot_node->parent;
}
}
private:
Node* root {nullptr};
size_t size {0};
compare_func compare;
};
#endif /* QTREE_H */