зеркало из https://github.com/mozilla/gecko-dev.git
395 строки
11 KiB
C++
395 строки
11 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (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.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
|
|
#include "Tree.h"
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// TreeNodeImpl
|
|
|
|
|
|
//
|
|
// Copy the link data from the src node to this node. On entry this node must not
|
|
// be linked to a tree, while src must be linked to a tree; on exit the opposite
|
|
// will be true.
|
|
//
|
|
// CAUTION: Do not call this directly; call TreeImpl::substitute or one of its
|
|
// derivatives instead.
|
|
//
|
|
inline void TreeNodeImpl::move(TreeNodeImpl &src)
|
|
{
|
|
assert(!linked && src.linked);
|
|
*this = src;
|
|
src.unlink();
|
|
}
|
|
|
|
|
|
//
|
|
// Let s be the subtree with this node as the root. Return the first (if right
|
|
// is false) or last (if right is true) node of this subtree.
|
|
//
|
|
TreeNodeImpl &TreeNodeImpl::extremeNode(bool right)
|
|
{
|
|
TreeNodeImpl *p;
|
|
TreeNodeImpl *subtree = this;
|
|
|
|
while ((p = subtree->getChild(right)) != 0)
|
|
subtree = p;
|
|
return *subtree;
|
|
}
|
|
|
|
|
|
//
|
|
// Return the node in the tree immediately before this node or nil if this
|
|
// node is the first in the tree.
|
|
//
|
|
TreeNodeImpl *TreeNodeImpl::prev()
|
|
{
|
|
TreeNodeImpl *child = getChild(false);
|
|
if (child)
|
|
return &child->extremeNode(true);
|
|
TreeNodeImpl *node = this;
|
|
while (node && node->isLeft())
|
|
node = node->getParent();
|
|
if (node)
|
|
node = node->getParent();
|
|
return node;
|
|
}
|
|
|
|
|
|
//
|
|
// Return the node in the tree immediately after this node or nil if this
|
|
// node is the last in the tree.
|
|
//
|
|
TreeNodeImpl *TreeNodeImpl::next()
|
|
{
|
|
TreeNodeImpl *child = getChild(true);
|
|
if (child)
|
|
return &child->extremeNode(false);
|
|
TreeNodeImpl *node = this;
|
|
while (node && node->isRight())
|
|
node = node->getParent();
|
|
if (node)
|
|
node = node->getParent();
|
|
return node;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// TreeImpl
|
|
|
|
|
|
//
|
|
// Link a node to its new parent on the right side (if right is true) or left side
|
|
// if (right is false), replacing the parent's previous child link, if any, on that
|
|
// side. A nil node indicates that the parent's previous child link should be cleared.
|
|
// A nil parent represents the root, in which case right is ignored.
|
|
//
|
|
void TreeImpl::linkNode(TreeNodeImpl *node, TreeNodeImpl *parent, bool right)
|
|
{
|
|
if (node) {
|
|
node->setParent(parent);
|
|
if (parent)
|
|
node->setRight(right);
|
|
else
|
|
node->setLeft();
|
|
}
|
|
if (parent)
|
|
parent->setChild(right, node);
|
|
else
|
|
root = node;
|
|
}
|
|
|
|
|
|
//
|
|
// Rotate a node pair to the right (if right is true) or left (if right is false)
|
|
// without changing their colors. The given node starts at the parent of its
|
|
// partner and ends as the child of its partner.
|
|
//
|
|
void TreeImpl::rotate(TreeNodeImpl &node, bool right)
|
|
{
|
|
TreeNodeImpl *partner = node.getChild(!right);
|
|
assert(partner);
|
|
linkNode(partner->getChild(right), &node, !right);
|
|
linkNode(partner, node.getParent(), node.isRight());
|
|
linkNode(&node, partner, right);
|
|
}
|
|
|
|
|
|
//
|
|
// Attach a new red node and link it with the parent on the right side (if right is
|
|
// true) or left side if (right is false). The given node must not be already linked
|
|
// into a tree.
|
|
// A nil parent represents the root.
|
|
//
|
|
void TreeImpl::addNode(TreeNodeImpl &node, TreeNodeImpl *parent, bool right)
|
|
{
|
|
assert(!(parent ? parent->getChild(right) : root));
|
|
node.link();
|
|
node.setRed();
|
|
node.setChild(false, 0);
|
|
node.setChild(true, 0);
|
|
linkNode(&node, parent, right);
|
|
nNodes++;
|
|
}
|
|
|
|
|
|
//
|
|
// Make node be the right (if right is true) or left (if right is false) child of
|
|
// parent, which must be in the tree. That child must be currently nil.
|
|
// If where is nil, the tree must be currently empty, and in this case make node
|
|
// be the new root, ignoring the value of right.
|
|
// Rebalance the tree as needed. node should not be part of any tree on entry.
|
|
//
|
|
void TreeImpl::attach(TreeNodeImpl &node, TreeNodeImpl *parent, bool right)
|
|
{
|
|
addNode(node, parent, right);
|
|
|
|
// Rebalance the tree to make sure that we don't have two red nodes in a row.
|
|
TreeNodeImpl *p = &node;
|
|
while (true) {
|
|
// If we are the root, we make this node black and we're done.
|
|
if (!parent) {
|
|
p->setBlack();
|
|
break;
|
|
}
|
|
|
|
// If we're black, we're done.
|
|
if (parent->isBlack())
|
|
break;
|
|
|
|
// Otherwise we need a more complex rebalance.
|
|
right = p->isRight();
|
|
TreeNodeImpl *grandparent = parent->getParent();
|
|
assert(grandparent);
|
|
bool parentRight = parent->isRight();
|
|
TreeNodeImpl *uncle = grandparent->getChild(!parentRight);
|
|
if (uncle && uncle->isRed()) {
|
|
parent->setBlack();
|
|
uncle->setBlack();
|
|
grandparent->setRed();
|
|
p = grandparent;
|
|
} else {
|
|
if (right != parentRight) {
|
|
rotate(*parent, parentRight);
|
|
p = parent;
|
|
parent = p->getParent();
|
|
}
|
|
parent->setBlack();
|
|
grandparent->setRed();
|
|
rotate(*grandparent, !parentRight);
|
|
break;
|
|
}
|
|
parent = p->getParent();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Insert node into the tree after node where. If where is nil, make node be the first
|
|
// node in the tree. Rebalance the tree as needed.
|
|
// node should not be part of any tree on entry.
|
|
//
|
|
void TreeImpl::insertAfter(TreeNodeImpl &node, TreeNodeImpl *where)
|
|
{
|
|
TreeNodeImpl *child = where ? where->getChild(true) : root;
|
|
if (child)
|
|
attach(node, &child->extremeNode(false), false);
|
|
else
|
|
attach(node, where, true);
|
|
}
|
|
|
|
|
|
//
|
|
// Insert node into the tree before node where. If where is nil, make node be the last
|
|
// node in the tree. Rebalance the tree as needed.
|
|
// node should not be part of any tree on entry.
|
|
//
|
|
void TreeImpl::insertBefore(TreeNodeImpl &node, TreeNodeImpl *where)
|
|
{
|
|
TreeNodeImpl *child = where ? where->getChild(false) : root;
|
|
if (child)
|
|
attach(node, &child->extremeNode(true), true);
|
|
else
|
|
attach(node, where, false);
|
|
}
|
|
|
|
|
|
//
|
|
// Remove the node from the tree, rebalancing the tree as needed.
|
|
//
|
|
void TreeImpl::remove(TreeNodeImpl &node)
|
|
{
|
|
// If the node has both children, exchange node with node's predecessor
|
|
// in the tree ordering, which is guaranteed not to have a right child.
|
|
if (node.getChild(false) && node.getChild(true)) {
|
|
TreeNodeImpl &prev = node.getChild(false)->extremeNode(true);
|
|
// Exchange the link fields of prev and node.
|
|
TreeNodeImpl nodeCopy = node;
|
|
TreeNodeImpl prevCopy = prev;
|
|
|
|
node.setRed(prevCopy.isRed());
|
|
prev.setRed(nodeCopy.isRed());
|
|
if (prevCopy.getParent() == &node)
|
|
linkNode(&node, &prev, false);
|
|
else {
|
|
linkNode(&node, prevCopy.getParent(), prevCopy.isRight());
|
|
linkNode(nodeCopy.getChild(false), &prev, false);
|
|
}
|
|
linkNode(&prev, nodeCopy.getParent(), nodeCopy.isRight());
|
|
linkNode(nodeCopy.getChild(true), &prev, true);
|
|
linkNode(prevCopy.getChild(false), &node, false);
|
|
node.setChild(true, 0);
|
|
// Quiet the assert about a linked node being deleted.
|
|
nodeCopy.unlink();
|
|
prevCopy.unlink();
|
|
}
|
|
|
|
// At this point node has no more than one child. Let p be that child
|
|
// or nil if node has no children.
|
|
assert(!(node.getChild(false) && node.getChild(true)));
|
|
TreeNodeImpl *p = node.getChild(false);
|
|
if (!p)
|
|
p = node.getChild(true);
|
|
|
|
// Link node's child directly to node's parent, bypassing node.
|
|
bool red = node.isRed();
|
|
bool right = node.isRight();
|
|
TreeNodeImpl *parent = node.getParent();
|
|
linkNode(p, parent, right);
|
|
nNodes--;
|
|
node.unlink();
|
|
|
|
// If we just deleted a black node, we need to rebalance the tree.
|
|
if (!red) {
|
|
while (!p || p->isBlack()) {
|
|
if (!parent)
|
|
break;
|
|
|
|
TreeNodeImpl *brother = parent->getChild(!right);
|
|
assert(brother);
|
|
if (brother->isRed()) {
|
|
brother->setRed(parent->isRed());
|
|
parent->setRed();
|
|
rotate(*parent, right);
|
|
brother = parent->getChild(!right);
|
|
}
|
|
|
|
TreeNodeImpl *cousin = brother->getChild(!right);
|
|
if (cousin && cousin->isRed()) {
|
|
cousin->setBlack();
|
|
brother->setRed(parent->isRed());
|
|
parent->setBlack();
|
|
rotate(*parent, right);
|
|
break;
|
|
}
|
|
|
|
cousin = brother->getChild(right);
|
|
if (cousin && cousin->isRed()) {
|
|
brother->setRed();
|
|
cousin->setBlack();
|
|
rotate(*brother, !right);
|
|
} else {
|
|
brother->setRed();
|
|
p = parent;
|
|
right = p->isRight();
|
|
parent = p->getParent();
|
|
}
|
|
}
|
|
if (p)
|
|
p->setBlack();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Splice newNode into oldNode's position in the tree. oldNode is removed from
|
|
// the tree.
|
|
//
|
|
void TreeImpl::substitute(TreeNodeImpl &newNode, TreeNodeImpl &oldNode)
|
|
{
|
|
if (&newNode != &oldNode) {
|
|
newNode.move(oldNode);
|
|
|
|
// Change the parent's pointer from oldNode to newNode
|
|
TreeNodeImpl *parent = newNode.getParent();
|
|
if (parent)
|
|
parent->setChild(newNode.isRight(), &newNode);
|
|
else
|
|
root = &newNode;
|
|
|
|
// Change the children's parent pointers from oldNode to newNode
|
|
TreeNodeImpl *child = newNode.getChild(false);
|
|
if (child)
|
|
child->setParent(&newNode);
|
|
child = newNode.getChild(true);
|
|
if (child)
|
|
child->setParent(&newNode);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
//
|
|
// Verify the integrity of a subtree rooted at the given node. Assert if
|
|
// anything wrong is found.
|
|
// Increment nNodes by the number of nodes encountered in the subtree.
|
|
// Return the number of non-nil black nodes encountered on any path from node
|
|
// to a leaf (this number must be constant regardless of the path and leaf
|
|
// chosen) in blackDepth. Return true if this node is red.
|
|
//
|
|
bool TreeImpl::verifySubtree(TreeNodeImpl *node, TreeNodeImpl *parent, bool rightChild, Uint32 &nNodes, Uint32 &blackDepth)
|
|
{
|
|
if (node) {
|
|
nNodes++;
|
|
assert(node->getParent() == parent && node->isRight() == rightChild);
|
|
Uint32 leftChildDepth;
|
|
Uint32 rightChildDepth;
|
|
bool red = node->isRed();
|
|
bool hasRedChild = verifySubtree(node->getChild(false), node, false, nNodes, leftChildDepth);
|
|
hasRedChild |= verifySubtree(node->getChild(true), node, true, nNodes, rightChildDepth);
|
|
assert(leftChildDepth == rightChildDepth);
|
|
if (red)
|
|
assert(!hasRedChild);
|
|
else
|
|
leftChildDepth++;
|
|
blackDepth = leftChildDepth;
|
|
return red;
|
|
} else {
|
|
blackDepth = 0;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Verify that the tree is internally consistent. Assert if anything wrong is found.
|
|
//
|
|
void TreeImpl::verify() const
|
|
{
|
|
Uint32 n = 0;
|
|
Uint32 blackDepth;
|
|
bool rootIsRed = verifySubtree(root, 0, false, n, blackDepth);
|
|
assert(!rootIsRed && n == nNodes);
|
|
}
|
|
#endif
|