gecko-dev/ef/Utilities/General/Tree.cpp

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