/*
* Copyright (c) 2012, NTT Multimedia Communications Laboratories, Inc. and Koushik Sen
*
* All rights reserved.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT
* HOLDER OR CONTRIBUTORS 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.
*/
/**
* This code originally by Tuomo Saarni. Obtained from:
*
* http://users.utu.fi/~tuiisa/Java/index.html
*
* under the following license:
*
* Here's some java sources I've made. Most codes are free to
* download. If you use some of my sources just remember give me
* the credits.
*/
package tests.rbtree;
import catg.CATG;
/**
* A <code>RedBlackTree</code> object is a Red-Black
* tree.
* <p/>
*
* @author Tuomo Saarni
* @version 1.1, 08/16/01
*/
public class RedBlackTree {
/**
* The root node of current tree.
*/
private RedBlackTreeNode root;
/**
* Constructs a new empty tree.
*/
public RedBlackTree() {
root = null;
}
/**
* Constructs a new tree with a root node x.
*
* @param x The root node of the tree.
*/
public RedBlackTree(RedBlackTreeNode x) {
root = x;
}
/**
* Inserts a node to the tree.
* Runs in O(lg n) time.
*
* @param x The new node of the tree.
* @throws NullPointerException If the parameter x is null.
*/
public void treeInsert(RedBlackTreeNode x) throws NullPointerException {
System.out.println("Inserting...");
RedBlackTreeNode y = null;
RedBlackTreeNode z = this.root;
while (z != null) {
y = z;
if (x.key() < z.key()) {
z = z.left();
} else {
z = z.right();
}
}
x.parentTo(y);
if (y == null) {
this.root = x;
} else {
if (x.key() < y.key()) {
y.leftTo(x);
} else {
y.rightTo(x);
}
}
x.setRed();
while (x != this.root && (x.parent()).isRed()) {
if (x.parent() == ((x.parent()).parent()).left()) {
y = ((x.parent()).parent()).right();
if (y != null && y.isRed()) {
(x.parent()).setBlack();
y.setBlack();
((x.parent()).parent()).setRed();
x = (x.parent()).parent();
} else {
if (x == (x.parent()).right()) {
x = x.parent();
CATG.event("l");
leftRotate(x);
}
(x.parent()).setBlack();
((x.parent()).parent()).setRed();
rightRotate((x.parent()).parent());
}
} else {
y = ((x.parent()).parent()).left();
if (y != null && y.isRed()) {
(x.parent()).setBlack();
y.setBlack();
((x.parent()).parent()).setRed();
x = (x.parent()).parent();
} else {
if (x == (x.parent()).left()) {
x = x.parent();
CATG.event("r");
rightRotate(x);
}
(x.parent()).setBlack();
((x.parent()).parent()).setRed();
leftRotate((x.parent()).parent());
}
}
} // End while
(this.root).setBlack();
}
/**
* Rotates the tree from the node x to left.
* Runs in Theta(n) time.
*
* @param x The node from which to start.
*/
private void leftRotate(RedBlackTreeNode x) {
RedBlackTreeNode y = x.right();
x.rightTo(y.left());
if (y.left() != null) {
(y.left()).parentTo(x);
}
y.parentTo(x.parent());
if (x.parent() == null) {
this.root = y;
} else {
if (x == (x.parent()).left()) {
(x.parent()).leftTo(y);
} else {
(x.parent()).rightTo(y);
}
}
y.leftTo(x);
x.parentTo(y);
}
/**
* Rotates the tree from the node x to right.
* Runs in Theta(n) time.
*
* @param x The node from which to start.
*/
private void rightRotate(RedBlackTreeNode x) {
RedBlackTreeNode y = x.left();
x.leftTo(y.right());
if (y.right() != null) {
(y.right()).parentTo(x);
}
y.parentTo(x.parent());
if (x.parent() == null) {
this.root = y;
} else {
if (x == (x.parent()).right()) {
(x.parent()).rightTo(y);
} else {
(x.parent()).leftTo(y);
}
}
y.rightTo(x);
x.parentTo(y);
}
/**
* Deletes the given node from the tree.
* Runs in O(lg n) time.
*
* @param z The node to be deleted of the tree.
* @throws NullPointerException If the parameter z is null.
*/
public RedBlackTreeNode treeDelete(RedBlackTreeNode z) throws NullPointerException {
RedBlackTreeNode x = new RedBlackTreeNode(0);
RedBlackTreeNode y = new RedBlackTreeNode(0);
RedBlackTreeNode sentinel = new RedBlackTreeNode(0);
if (z.left() == null || z.right() == null) {
y = z;
} else {
y = treeSuccessor(z);
}
// Now y != null, y = z or greater
if (y.left() != null) {
x = y.left();
} else {
if (y.right() != null) {
x = y.right();
} else {
x = sentinel;
}
}
x.parentTo(y.parent());
if (y.parent() == null) // if y is root
{
this.root = x;
} else // y is not root
{
if (y == (y.parent()).left()) // if y = left[p[y]]
{
(y.parent()).leftTo(x); // then left[p[y]] <- x
} else {
(y.parent()).rightTo(x);
}
}
if (y != z) {
z.keyTo(y.key());
z.objectTo(y.object());
}
if (y.isBlack()) {
deleteFixup(x);
}
// Remove all references to sentinel
// Otherwise sentinel can be 'seen' in the tree
if (this.root != sentinel) {
if ((sentinel.parent()).left() == sentinel) {
(sentinel.parent()).leftTo(null);
}
if ((sentinel.parent()).right() == sentinel) {
(sentinel.parent()).rightTo(null);
}
sentinel = null;
} else {
this.root = null;
sentinel = null;
}
return y;
}
/**
* Fixes the red-black tree after a deletion if needed.
*
* @param x The node from which to start.
*/
private void deleteFixup(RedBlackTreeNode x) {
RedBlackTreeNode w = new RedBlackTreeNode(0);
while (x != this.root && x.isBlack()) {
if (x == (x.parent()).left()) //x is left son of its parent
{
w = (x.parent()).right(); // set w to refer x's parent's right son
if (w.isRed()) {
w.setBlack();
(x.parent()).setRed();
leftRotate(x.parent());
w = (x.parent()).right();
}
if ((w.left() == null && w.right() == null) ||
(w.left() == null && w.right() != null && (w.right()).isBlack()) ||
(w.right() == null && w.left() != null && (w.left()).isBlack()) ||
(w.right() != null && w.left() != null
&& (w.right()).isBlack() && (w.left()).isBlack())
) {
w.setRed();
x = x.parent();
} else {
if (w.right() != null && (w.right()).isBlack()) {
(w.left()).setBlack();
w.setRed();
rightRotate(w);
w = (x.parent()).right();
}
if ((x.parent()).isBlack()) {
w.setBlack();
} else {
w.setRed();
}
(x.parent()).setBlack();
if (w.right != null) // Otherwise its already black
{
(w.right()).setBlack();
}
leftRotate(x.parent());
x = this.root;
}
} else {
w = (x.parent()).left(); // set w to refer x's parent's right son
if (w.isRed()) {
w.setBlack();
(x.parent()).setRed();
rightRotate(x.parent());
w = (x.parent()).left();
}
if ((w.left() == null && w.right() == null) ||
(w.left() == null && w.right() != null && (w.right()).isBlack()) ||
(w.right() == null && w.left() != null && (w.left()).isBlack()) ||
(w.right() != null && w.left() != null
&& (w.right()).isBlack() && (w.left()).isBlack())
) {
w.setRed();
x = x.parent();
} else {
if (w.left() != null && (w.left()).isBlack()) {
(w.right()).setBlack();
w.setRed();
leftRotate(w);
w = (x.parent()).left();
}
if ((x.parent()).isBlack()) {
w.setBlack();
} else {
w.setRed();
}
(x.parent()).setBlack();
if (w.left != null) // Otherwise its already black
{
(w.left()).setBlack();
}
rightRotate(x.parent());
x = this.root;
}
}
} // End while
x.setBlack();
} // End deleteFixup
/**
* Prints the keys of current tree in inorder (ascending).
* Runs in Theta(n) time.
*
* @param x The node from which to start.
*/
public void inorderTreeWalk(RedBlackTreeNode x, String space) {
if (!(x == null)) {
System.out.println(space + x.key());
inorderTreeWalk(x.left(), space + " ");
inorderTreeWalk(x.right(), space + " ");
}
}
/**
* Searches a node with key k starting from the node x which is usually the root.
* If the node is not found returns <code>null</code> otherwise returns the pointer
* to the current node.Runs in O(h) time where h is the height of the tree. Works recursively.
*
* @return The node with key k or <code>null</code>.
* @param x The node from which to start, usually the root.
* @param k The key of the wanted node.
*/
public RedBlackTreeNode treeSearch(RedBlackTreeNode x, int k) {
if (x == null || k == x.key()) {
return x;
}
if (k < x.key()) {
return treeSearch(x.left(), k);
} else {
return treeSearch(x.right(), k);
}
}
/**
* Searches a node with key k starting from the node x which is usually the root.
* If the node is not found returns <code>null</code> otherwise returns the pointer
* to the current node. Runs in O(h) time where h is the height of the tree. Works iteratively.
*
* @return The node with key k or <code>null</code>.
* @param x The node from which to start, usually the root.
* @param k The key of the wanted node.
*/
public RedBlackTreeNode iterativeTreeSearch(RedBlackTreeNode x, int k) {
while (!(x == null) && k != x.key()) {
if (k < x.key()) {
x = x.left();
} else {
x = x.right();
}
}
return x;
}
/**
* Searches a node with smallest key starting from the node x which is usually the root.
* Runs in O(h) time where h is the height of the tree.
*
* @return The node with the smallest key.
* @throws NullPointerException If the parameter x is null.
* @param x The node from which to start, usually the root.
*/
public RedBlackTreeNode treeMinimum(RedBlackTreeNode x) throws NullPointerException {
while (x.left() != null) {
x = x.left();
}
return x;
}
/**
* Searches a node with biggest key starting from the node x which is usually the root.
* Runs in O(h) time where h is the height of the tree.
*
* @return The node with the biggest key.
* @throws NullPointerException If the parameter x is null.
* @param x The node from which to start, usually the root.
*/
public RedBlackTreeNode treeMaximum(RedBlackTreeNode x) throws NullPointerException {
while (x.right() != null) {
x = x.right();
}
return x;
}
/**
* Searches the successor node of the key x.
* Runs in O(h) time where h is the height of the tree.
*
* @return The successor node.
* @throws NullPointerException If the parameter x is null.
* @param x The node from which successor is wanted.
*/
public RedBlackTreeNode treeSuccessor(RedBlackTreeNode x) throws NullPointerException {
if (x.right() != null) {
return treeMinimum(x.right());
}
RedBlackTreeNode y = x.parent();
while (y != null && x.equals(y.right())) {
x = y;
y = y.parent();
}
return y;
}
/**
* Searches the predessor node of the key x.
* Runs in O(h) time where h is the height of the tree.
*
* @return The predessor node.
* @throws NullPointerException If the parameter x is null.
* @param x The node from which predessor is wanted.
*/
public RedBlackTreeNode treePredessor(RedBlackTreeNode x) throws NullPointerException {
if (x.left() != null) {
return treeMaximum(x.left());
}
RedBlackTreeNode y = x.parent();
while (y != null && x.equals(y.left()))
{
x = y;
y = y.parent();
}
return y;
}
/**
* Returns the root of the tree.
*
* @return The root of the tree.
*/
public RedBlackTreeNode root()
{
return this.root;
}
} // End class RedBlackTree