aios/storage/indexlib/util/Avl.h (383 lines of code) (raw):
/*
* Copyright 2014-present Alibaba Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// ===================================================================
// Avl.h - Implementation of an Avl balanced tree
// Written by Brad Appleton (1997)
// http://www.bradapp.com/ftp/src/libs/C++/AvlTrees.html
#pragma once
#include <iostream>
#include <memory>
#include <stddef.h>
#include "autil/Log.h"
#include "indexlib/util/Comparable.h"
namespace indexlib { namespace util {
using namespace std;
// Indices into a subtree array
// AvlNode -- Class to implement an AVL Tree
//
template <class KeyType>
class AvlNode
{
public:
// Max number of subtrees per node
enum { MAX_SUBTREES = 2 };
enum dir_t { LEFT = 0, RIGHT = 1 };
// Return the opposite direction of the given index
static dir_t Opposite(dir_t dir) { return dir_t(1 - int(dir)); }
// ----- Constructors and destructors:
AvlNode(Comparable<KeyType>* item = NULL);
virtual ~AvlNode(void);
// ----- Query attributes:
// Get this node's data
Comparable<KeyType>* Data() const { return myData; }
// Get this node's key field
KeyType Key() const { return myData->Key(); }
// Query the balance factor, it will be a value between -1 .. 1
// where:
// -1 => left subtree is taller than right subtree
// 0 => left and right subtree are equal in height
// 1 => right subtree is taller than left subtree
short Bal(void) const { return myBal; }
// Get the item at the top of the left/right subtree of this
// item (the result may be NULL if there is no such item).
//
AvlNode* Subtree(dir_t dir) const { return mySubtree[dir]; }
AvlNode<KeyType>* Parent(AvlNode<KeyType>*);
// ----- Search/Insert/Delete
//
// NOTE: These are all static functions instead of member functions
// because most of them need to modify the given tree root
// pointer. If these were instance member functions than
// that would correspond to having to modify the 'this'
// pointer, which is not allowed in C++. Most of the
// functions that are static and which take an AVL tree
// pointer as a parameter are static for this reason.
// Look for the given key, return NULL if not found,
// otherwise return the item's address.
static AvlNode<KeyType>* Search(KeyType key, AvlNode<KeyType>* root, cmp_t cmp = EQ_CMP);
// Insert the given key, return a pointer to the node if it was inserted,
// otherwise return NULL
static AvlNode<KeyType>* Insert(Comparable<KeyType>* item, AvlNode<KeyType>*& root);
// Delete the given key from the tree. Return the corresponding
// node, or return NULL if it was not found.
static Comparable<KeyType>* Delete(KeyType key, AvlNode<KeyType>*& root, cmp_t cmp = EQ_CMP);
// Verification
// Return the height of this tree
int Height() const;
// Verify this tree is a valid AVL tree, return TRUE if it is,
// return FALSE otherwise
int Check() const;
// If you want to provide your own allocation scheme than simply
// #define the preprocessor manifest constant named CUSTOM_ALLOCATE
// and make sure you provide and link with your own overloaded
// versions of operators "new" and "delete" for this class.
#ifdef CUSTOM_ALLOCATE
void* operator new(size_t);
void operator delete(void*);
#endif /* CUSTOM_ALLOCATE */
private:
// Use mnemonic constants for valid balance-factor values
enum balance_t { LEFT_HEAVY = -1, BALANCED = 0, RIGHT_HEAVY = 1 };
// Use mnemonic constants for indicating a change in height
enum height_effect_t { HEIGHT_NOCHANGE = 0, HEIGHT_CHANGE = 1 };
// Return true if the tree is too heavy on the left side
inline static int LEFT_IMBALANCE(short bal) { return (bal < LEFT_HEAVY); }
// Return true if the tree is too heavy on the right side
inline static int RIGHT_IMBALANCE(short bal) { return (bal > RIGHT_HEAVY); }
// ----- Private data
Comparable<KeyType>* myData; // Data field
AvlNode<KeyType>* mySubtree[MAX_SUBTREES]; // Pointers to subtrees
short myBal; // Balance factor
// Reset all subtrees to null and clear the balance factor
void Reset(void)
{
myBal = 0;
mySubtree[LEFT] = mySubtree[RIGHT] = NULL;
}
// ----- Routines that do the *real* insertion/deletion
// Insert the given key into the given tree. Return the node if
// it already exists. Otherwise return NULL to indicate that
// the key was successfully inserted. Upon return, the "change"
// parameter will be '1' if the tree height changed as a result
// of the insertion (otherwise "change" will be 0).
static AvlNode<KeyType>* Insert(Comparable<KeyType>* item, AvlNode<KeyType>*& root, int& change);
// Delete the given key from the given tree. Return NULL if the
// key is not found in the tree. Otherwise return a pointer to the
// node that was removed from the tree. Upon return, the "change"
// parameter will be '1' if the tree height changed as a result
// of the deletion (otherwise "change" will be 0).
static Comparable<KeyType>* Delete(KeyType key, AvlNode<KeyType>*& root, int& change, cmp_t cmp = EQ_CMP);
// Routines for rebalancing and rotating subtrees
// Perform an XX rotation for the given direction 'X'.
// Return 1 if the tree height changes due to rotation,
// otherwise return 0.
static int RotateOnce(AvlNode<KeyType>*& root, dir_t dir);
// Perform an XY rotation for the given direction 'X'
// Return 1 if the tree height changes due to rotation,
// otherwise return 0.
static int RotateTwice(AvlNode<KeyType>*& root, dir_t dir);
// Rebalance a (sub)tree if it has become imbalanced
static int ReBalance(AvlNode<KeyType>*& root);
// Perform a comparison of the given key against the given
// item using the given criteria (min, max, or equivalence
// comparison). Returns:
// EQ_CMP if the keys are equivalent
// MIN_CMP if this key is less than the item's key
// MAX_CMP if this key is greater than item's key
cmp_t Compare(KeyType key, cmp_t cmp = EQ_CMP) const;
private:
// Disallow copying and assignment
AvlNode(const AvlNode<KeyType>&);
AvlNode& operator=(const AvlNode<KeyType>&);
private:
AUTIL_LOG_DECLARE();
};
// Class AvlTree is a simple container object to "house" an AvlNode
// that represents the root-node of and AvlTree. Most of the member
// functions simply delegate to the root AvlNode.
template <class KeyType>
class AvlTree
{
private:
// Disallow copying and assignment
AvlTree(const AvlTree<KeyType>&);
AvlTree& operator=(const AvlTree<KeyType>&);
public:
// Member data
AvlNode<KeyType>* myRoot; // The root of the tree
// Constructor and destructor
AvlTree() : myRoot(NULL) {};
~AvlTree()
{
if (myRoot)
delete myRoot;
}
// Dump the tree to the given output stream
void DumpTree() const;
// See if the tree is empty
int IsEmpty() const { return (myRoot == NULL); }
// Search, Insert, Delete, and Check
AvlNode<KeyType>* Search(KeyType key, cmp_t cmp = EQ_CMP) { return AvlNode<KeyType>::Search(key, myRoot, cmp); }
AvlNode<KeyType>* Insert(Comparable<KeyType>* item) { return AvlNode<KeyType>::Insert(item, myRoot); }
Comparable<KeyType>* Delete(KeyType key, cmp_t cmp = EQ_CMP) { return AvlNode<KeyType>::Delete(key, myRoot, cmp); }
// As with all binary trees, a node's in-order successor is the
// left-most child of its right subtree, and a node's in-order predecessor
// is the right-most child of its left subtree.
AvlNode<KeyType>* Next(AvlNode<KeyType>* node)
{
AvlNode<KeyType>*q, *p = node->Subtree(AvlNode<KeyType>::RIGHT);
if (p) {
while (p->Subtree(AvlNode<KeyType>::LEFT))
p = p->Subtree(AvlNode<KeyType>::LEFT);
return p;
} else {
// find parent, check if node is on left subtree
q = node;
p = node->Parent(myRoot);
while (p && (q == p->Subtree(AvlNode<KeyType>::RIGHT))) {
q = p;
p = p->Parent(myRoot);
}
return p;
}
}
AvlNode<KeyType>* Prev(AvlNode<KeyType>* node)
{
AvlNode<KeyType>*q, *p = node->Subtree(AvlNode<KeyType>::LEFT);
if (p) {
while (p->Subtree(AvlNode<KeyType>::RIGHT))
p = p->Subtree(AvlNode<KeyType>::RIGHT);
return p;
} else {
// find parent, check if node is on left subtree
q = node;
p = node->Parent(myRoot);
while (p && (q == p->Subtree(AvlNode<KeyType>::LEFT))) {
q = p;
p = p->Parent(myRoot);
}
return p;
}
}
int Check() const { return (myRoot) ? myRoot->Check() : 1; }
};
AUTIL_LOG_SETUP_TEMPLATE(indexlib.util, AvlNode, KeyType);
// ---------------------------------------------------------------- Definitions
// Return the minimum of two numbers
inline static int MIN(int a, int b) { return ((a) < (b)) ? (a) : (b); }
// Return the maximum of two numbers
inline static int MAX(int a, int b) { return ((a) > (b)) ? (a) : (b); }
// ----------------------------------------------- Constructors and Destructors
template <class KeyType>
AvlNode<KeyType>::AvlNode(Comparable<KeyType>* item) : myData(item)
, myBal(0)
{
Reset();
}
template <class KeyType>
AvlNode<KeyType>::~AvlNode(void)
{
if (mySubtree[LEFT])
delete mySubtree[LEFT];
if (mySubtree[RIGHT])
delete mySubtree[RIGHT];
}
// ------------------------------------------------- Rotating and Re-Balancing
template <class KeyType>
int AvlNode<KeyType>::RotateOnce(AvlNode<KeyType>*& root, dir_t dir)
{
dir_t otherDir = Opposite(dir);
AvlNode<KeyType>* oldRoot = root;
// See if otherDir subtree is balanced. If it is, then this
// rotation will *not* change the overall tree height.
// Otherwise, this rotation will shorten the tree height.
int heightChange = (root->mySubtree[otherDir]->myBal == 0) ? HEIGHT_NOCHANGE : HEIGHT_CHANGE;
// assign new root
root = oldRoot->mySubtree[otherDir];
// new-root exchanges it's "dir" mySubtree for it's parent
oldRoot->mySubtree[otherDir] = root->mySubtree[dir];
root->mySubtree[dir] = oldRoot;
// update balances
oldRoot->myBal = -((dir == LEFT) ? --(root->myBal) : ++(root->myBal));
return heightChange;
}
template <class KeyType>
int AvlNode<KeyType>::RotateTwice(AvlNode<KeyType>*& root, dir_t dir)
{
dir_t otherDir = Opposite(dir);
AvlNode<KeyType>* oldRoot = root;
AvlNode<KeyType>* oldOtherDirSubtree = root->mySubtree[otherDir];
// assign new root
root = oldRoot->mySubtree[otherDir]->mySubtree[dir];
// new-root exchanges it's "dir" mySubtree for it's grandparent
oldRoot->mySubtree[otherDir] = root->mySubtree[dir];
root->mySubtree[dir] = oldRoot;
// new-root exchanges it's "other-dir" mySubtree for it's parent
oldOtherDirSubtree->mySubtree[dir] = root->mySubtree[otherDir];
root->mySubtree[otherDir] = oldOtherDirSubtree;
// update balances
root->mySubtree[LEFT]->myBal = -MAX(root->myBal, 0);
root->mySubtree[RIGHT]->myBal = -MIN(root->myBal, 0);
root->myBal = 0;
// A double rotation always shortens the overall height of the tree
return HEIGHT_CHANGE;
}
template <class KeyType>
int AvlNode<KeyType>::ReBalance(AvlNode<KeyType>*& root)
{
int heightChange = HEIGHT_NOCHANGE;
if (LEFT_IMBALANCE(root->myBal)) {
// Need a right rotation
if (root->mySubtree[LEFT]->myBal == RIGHT_HEAVY) {
// RL rotation needed
heightChange = RotateTwice(root, RIGHT);
} else {
// RR rotation needed
heightChange = RotateOnce(root, RIGHT);
}
} else if (RIGHT_IMBALANCE(root->myBal)) {
// Need a left rotation
if (root->mySubtree[RIGHT]->myBal == LEFT_HEAVY) {
// LR rotation needed
heightChange = RotateTwice(root, LEFT);
} else {
// LL rotation needed
heightChange = RotateOnce(root, LEFT);
}
}
return heightChange;
}
// ------------------------------------------------------- Comparisons
template <class KeyType>
cmp_t AvlNode<KeyType>::Compare(KeyType key, cmp_t cmp) const
{
switch (cmp) {
default:
case EQ_CMP: // Standard comparison
return myData->Compare(key);
case MIN_CMP: // Find the minimal element in this tree
return (mySubtree[LEFT] == NULL) ? EQ_CMP : MIN_CMP;
case MAX_CMP: // Find the maximal element in this tree
return (mySubtree[RIGHT] == NULL) ? EQ_CMP : MAX_CMP;
}
}
template <class KeyType>
AvlNode<KeyType>* AvlNode<KeyType>::Parent(AvlNode<KeyType>* myRoot)
{
if (this == myRoot)
return 0;
AvlNode<KeyType>* p = myRoot;
while (p) {
if (this == p->Subtree(AvlNode<KeyType>::LEFT))
return p;
if (this == p->Subtree(AvlNode<KeyType>::RIGHT))
return p;
cmp_t result = p->Compare(this->Key());
if (result == MIN_CMP)
p = p->Subtree(AvlNode<KeyType>::LEFT);
else
p = p->Subtree(AvlNode<KeyType>::RIGHT);
}
return 0;
}
// ------------------------------------------------------- Search/Insert/Delete
template <class KeyType>
AvlNode<KeyType>* AvlNode<KeyType>::Search(KeyType key, AvlNode<KeyType>* root, cmp_t cmp)
{
cmp_t result;
while (root && (result = root->Compare(key, cmp))) {
root = root->mySubtree[(result < 0) ? LEFT : RIGHT];
}
return (root) ? root : NULL;
}
template <class KeyType>
AvlNode<KeyType>* AvlNode<KeyType>::Insert(Comparable<KeyType>* item, AvlNode<KeyType>*& root)
{
int change;
return Insert(item, root, change);
}
template <class KeyType>
Comparable<KeyType>* AvlNode<KeyType>::Delete(KeyType key, AvlNode<KeyType>*& root, cmp_t cmp)
{
int change;
return Delete(key, root, change, cmp);
}
template <class KeyType>
AvlNode<KeyType>* AvlNode<KeyType>::Insert(Comparable<KeyType>* item, AvlNode<KeyType>*& root, int& change)
{
// See if the tree is empty
if (root == NULL) {
// Insert new node here
root = new AvlNode<KeyType>(item);
change = HEIGHT_CHANGE;
return root;
}
// Initialize
AvlNode<KeyType>* found = NULL;
int increase = 0;
// Compare items and determine which direction to search
cmp_t result = root->Compare(item->Key());
dir_t dir = (result == MIN_CMP) ? LEFT : RIGHT;
if (result != EQ_CMP) {
// Insert into "dir" subtree
found = Insert(item, root->mySubtree[dir], change);
if (!found)
return NULL; // already here - don't insert
increase = result * change; // set balance factor increment
} else { // key already in tree at this node
increase = HEIGHT_NOCHANGE;
return NULL;
}
root->myBal += increase; // update balance factor
// ----------------------------------------------------------------------
// re-balance if needed -- height of current tree increases only if its
// subtree height increases and the current tree needs no rotation.
// ----------------------------------------------------------------------
change = (increase && root->myBal) ? (1 - ReBalance(root)) : HEIGHT_NOCHANGE;
return found;
}
template <class KeyType>
Comparable<KeyType>* AvlNode<KeyType>::Delete(KeyType key, AvlNode<KeyType>*& root, int& change, cmp_t cmp)
{
// See if the tree is empty
if (root == NULL) {
// Key not found
change = HEIGHT_NOCHANGE;
return NULL;
}
// Initialize
Comparable<KeyType>* found = NULL;
int decrease = 0;
// Compare items and determine which direction to search
cmp_t result = root->Compare(key, cmp);
dir_t dir = (result == MIN_CMP) ? LEFT : RIGHT;
if (result != EQ_CMP) {
// Delete from "dir" subtree
found = Delete(key, root->mySubtree[dir], change, cmp);
if (!found)
return found; // not found - can't delete
decrease = result * change; // set balance factor decrement
} else { // Found key at this node
found = root->myData; // set return value
// ---------------------------------------------------------------------
// At this point we know "result" is zero and "root" points to
// the node that we need to delete. There are three cases:
//
// 1) The node is a leaf. Remove it and return.
//
// 2) The node is a branch (has only 1 child). Make "root"
// (the pointer to this node) point to the child.
//
// 3) The node has two children. We swap items with the successor
// of "root" (the smallest item in its right subtree) and delete
// the successor from the right subtree of "root". The
// identifier "decrease" should be reset if the subtree height
// decreased due to the deletion of the successor of "root".
// ---------------------------------------------------------------------
if ((root->mySubtree[LEFT] == NULL) && (root->mySubtree[RIGHT] == NULL)) {
// We have a leaf -- remove it
delete root;
root = NULL;
change = HEIGHT_CHANGE; // height changed from 1 to 0
return found;
} else if ((root->mySubtree[LEFT] == NULL) || (root->mySubtree[RIGHT] == NULL)) {
// We have one child -- only child becomes new root
AvlNode<KeyType>* toDelete = root;
root = root->mySubtree[(root->mySubtree[RIGHT]) ? RIGHT : LEFT];
change = HEIGHT_CHANGE; // We just shortened the subtree
// Null-out the subtree pointers so we dont recursively delete
toDelete->mySubtree[LEFT] = toDelete->mySubtree[RIGHT] = NULL;
delete toDelete;
return found;
} else {
// We have two children -- find successor and replace our current
// data item with that of the successor
root->myData = Delete(key, root->mySubtree[RIGHT], decrease, MIN_CMP);
}
}
root->myBal -= decrease; // update balance factor
// ------------------------------------------------------------------------
// Rebalance if necessary -- the height of current tree changes if one
// of two things happens: (1) a rotation was performed which changed
// the height of the subtree (2) the subtree height decreased and now
// matches the height of its other subtree (so the current tree now
// has a zero balance when it previously did not).
// ------------------------------------------------------------------------
// change = (decrease) ? ((root->myBal) ? balance(root) : HEIGHT_CHANGE)
// : HEIGHT_NOCHANGE ;
if (decrease) {
if (root->myBal) {
change = ReBalance(root); // rebalance and see if height changed
} else {
change = HEIGHT_CHANGE; // balanced because subtree decreased
}
} else {
change = HEIGHT_NOCHANGE;
}
return found;
}
// --------------------------------------------------------------- Verification
template <class KeyType>
int AvlNode<KeyType>::Height() const
{
int leftHeight = (mySubtree[LEFT]) ? mySubtree[LEFT]->Height() : 0;
int rightHeight = (mySubtree[RIGHT]) ? mySubtree[RIGHT]->Height() : 0;
return (1 + MAX(leftHeight, rightHeight));
}
template <class KeyType>
int AvlNode<KeyType>::Check() const
{
int valid = 1;
// First verify that subtrees are correct
if (mySubtree[LEFT])
valid *= mySubtree[LEFT]->Check();
if (mySubtree[RIGHT])
valid *= mySubtree[RIGHT]->Check();
// Now get the height of each subtree
int leftHeight = (mySubtree[LEFT]) ? mySubtree[LEFT]->Height() : 0;
int rightHeight = (mySubtree[RIGHT]) ? mySubtree[RIGHT]->Height() : 0;
// Verify that AVL tree property is satisfied
int diffHeight = rightHeight - leftHeight;
if (LEFT_IMBALANCE(diffHeight) || RIGHT_IMBALANCE(diffHeight)) {
valid = 0;
AUTIL_LOG(ERROR, "Height difference is %lu at node %lu", diffHeight, Key());
}
// Verify that balance-factor is correct
if (diffHeight != myBal) {
valid = 0;
AUTIL_LOG(ERROR,
"Height difference %lu doesn't match "
"balance-factor of %lu at node %lu",
diffHeight, myBal, Key());
}
// Verify that search-tree property is satisfied
if ((mySubtree[LEFT]) && (mySubtree[LEFT]->Compare(Key()) == MIN_CMP)) {
valid = 0;
AUTIL_LOG(ERROR, "Node: %lu is smaller than left subtree %lu", Key(), mySubtree[LEFT]->Key());
}
if ((mySubtree[RIGHT]) && (mySubtree[RIGHT]->Compare(Key()) == MAX_CMP)) {
valid = 0;
AUTIL_LOG(ERROR, "Node: %lu is greater than right subtree %lu", Key(), mySubtree[RIGHT]->Key());
}
return valid;
}
//----------------------------------------------- Routines for dumping the tree
static inline ostream& Indent(ostream& os, int len)
{
for (int i = 0; i < len; i++) {
os << ' ';
}
return os;
}
enum TraversalOrder { LTREE, KEY, RTREE };
template <class KeyType>
static void Dump(ostream& os, TraversalOrder order, const AvlNode<KeyType>* node, int level = 0)
{
unsigned len = (level * 5) + 1;
if ((order == LTREE) && (node->Subtree(AvlNode<KeyType>::LEFT) == NULL)) {
Indent(os, len) << " **NULL**" << endl;
}
if (order == KEY) {
Indent(os, len) << node->Key()->key() << ":" << node->Bal() << endl;
}
if ((order == RTREE) && (node->Subtree(AvlNode<KeyType>::RIGHT) == NULL)) {
Indent(os, len) << " **NULL**" << endl;
}
}
template <class KeyType>
static void Dump(ostream& os, const AvlNode<KeyType>* node, int level = 0)
{
if (node == NULL) {
os << "***EMPTY TREE***" << endl;
} else {
Dump(os, RTREE, node, level);
if (node->Subtree(AvlNode<KeyType>::RIGHT) != NULL) {
Dump(os, node->Subtree(AvlNode<KeyType>::RIGHT), level + 1);
}
Dump(os, KEY, node, level);
if (node->Subtree(AvlNode<KeyType>::LEFT) != NULL) {
Dump(os, node->Subtree(AvlNode<KeyType>::LEFT), level + 1);
}
Dump(os, LTREE, node, level);
} // if non-empty tree
}
template <class KeyType>
void AvlTree<KeyType>::DumpTree() const
{
Dump(cout, myRoot);
}
}} // namespace indexlib::util