blob: a90bbf6886e56706dab72f18af07476f709fc4ae [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ds_SplayTree_h
#define ds_SplayTree_h
#include "LifoAlloc.h"
namespace js {
/*
* Class which represents a splay tree with nodes allocated from a LifoAlloc.
* Splay trees are balanced binary search trees for which search, insert and
* remove are all amortized O(log n).
*
* T indicates the type of tree elements, C must have a static
* compare(const T&, const T&) method ordering the elements. As for LifoAlloc
* objects, T objects stored in the tree will not be explicitly destroyed.
*/
template <class T, class C>
class SplayTree
{
struct Node {
T item;
Node *left, *right, *parent;
Node(const T &item)
: item(item), left(NULL), right(NULL), parent(NULL)
{}
};
LifoAlloc *alloc;
Node *root, *freeList;
SplayTree(const SplayTree &) MOZ_DELETE;
SplayTree &operator=(const SplayTree &) MOZ_DELETE;
public:
SplayTree(LifoAlloc *alloc = NULL)
: alloc(alloc), root(NULL), freeList(NULL)
{}
void setAllocator(LifoAlloc *alloc) {
this->alloc = alloc;
}
bool empty() const {
return !root;
}
bool contains(const T &v, T *res)
{
if (!root)
return false;
Node *last = lookup(v);
splay(last);
checkCoherency(root, NULL);
if (C::compare(v, last->item) == 0) {
*res = last->item;
return true;
}
return false;
}
bool insert(const T &v)
{
Node *element = allocateNode(v);
if (!element)
return false;
if (!root) {
root = element;
return true;
}
Node *last = lookup(v);
int cmp = C::compare(v, last->item);
// Don't tolerate duplicate elements.
JS_ASSERT(cmp);
Node *&parentPointer = (cmp < 0) ? last->left : last->right;
JS_ASSERT(!parentPointer);
parentPointer = element;
element->parent = last;
splay(element);
checkCoherency(root, NULL);
return true;
}
void remove(const T &v)
{
Node *last = lookup(v);
JS_ASSERT(last && C::compare(v, last->item) == 0);
splay(last);
JS_ASSERT(last == root);
// Find another node which can be swapped in for the root: either the
// rightmost child of the root's left, or the leftmost child of the
// root's right.
Node *swap, *swapChild;
if (root->left) {
swap = root->left;
while (swap->right)
swap = swap->right;
swapChild = swap->left;
} else if (root->right) {
swap = root->right;
while (swap->left)
swap = swap->left;
swapChild = swap->right;
} else {
freeNode(root);
root = NULL;
return;
}
// The selected node has at most one child, in swapChild. Detach it
// from the subtree by replacing it with that child.
if (swap == swap->parent->left)
swap->parent->left = swapChild;
else
swap->parent->right = swapChild;
if (swapChild)
swapChild->parent = swap->parent;
root->item = swap->item;
freeNode(swap);
checkCoherency(root, NULL);
}
template <class Op>
void forEach(Op op)
{
forEachInner(op, root);
}
private:
Node *lookup(const T &v)
{
JS_ASSERT(root);
Node *node = root, *parent;
do {
parent = node;
int c = C::compare(v, node->item);
if (c == 0)
return node;
else if (c < 0)
node = node->left;
else
node = node->right;
} while (node);
return parent;
}
Node *allocateNode(const T &v)
{
Node *node = freeList;
if (node) {
freeList = node->left;
new(node) Node(v);
return node;
}
return alloc->new_<Node>(v);
}
void freeNode(Node *node)
{
node->left = freeList;
freeList = node;
}
void splay(Node *node)
{
// Rotate the element until it is at the root of the tree. Performing
// the rotations in this fashion preserves the amortized balancing of
// the tree.
JS_ASSERT(node);
while (node != root) {
Node *parent = node->parent;
if (parent == root) {
// Zig rotation.
rotate(node);
JS_ASSERT(node == root);
return;
}
Node *grandparent = parent->parent;
if ((parent->left == node) == (grandparent->left == parent)) {
// Zig-zig rotation.
rotate(parent);
rotate(node);
} else {
// Zig-zag rotation.
rotate(node);
rotate(node);
}
}
}
void rotate(Node *node)
{
// Rearrange nodes so that node becomes the parent of its current
// parent, while preserving the sortedness of the tree.
Node *parent = node->parent;
if (parent->left == node) {
// x y
// y c ==> a x
// a b b c
parent->left = node->right;
if (node->right)
node->right->parent = parent;
node->right = parent;
} else {
JS_ASSERT(parent->right == node);
// x y
// a y ==> x c
// b c a b
parent->right = node->left;
if (node->left)
node->left->parent = parent;
node->left = parent;
}
node->parent = parent->parent;
parent->parent = node;
if (Node *grandparent = node->parent) {
if (grandparent->left == parent)
grandparent->left = node;
else
grandparent->right = node;
} else {
root = node;
}
}
template <class Op>
void forEachInner(Op op, Node *node)
{
if (!node)
return;
forEachInner(op, node->left);
op(node->item);
forEachInner(op, node->right);
}
Node *checkCoherency(Node *node, Node *minimum)
{
#ifdef DEBUG
if (!node) {
JS_ASSERT(!root);
return NULL;
}
JS_ASSERT_IF(!node->parent, node == root);
JS_ASSERT_IF(minimum, C::compare(minimum->item, node->item) < 0);
if (node->left) {
JS_ASSERT(node->left->parent == node);
Node *leftMaximum = checkCoherency(node->left, minimum);
JS_ASSERT(C::compare(leftMaximum->item, node->item) < 0);
}
if (node->right) {
JS_ASSERT(node->right->parent == node);
return checkCoherency(node->right, node);
}
return node;
#else
return NULL;
#endif
}
};
} /* namespace js */
#endif /* ds_SplayTree_h */