package ca.pfv.spmf.datastructures.redblacktree;
/* This file is copyright (c) 2008-2013 Philippe Fournier-Viger
*
* This file is part of the SPMF DATA MINING SOFTWARE
* (http://www.philippe-fournier-viger.com/spmf).
*
* SPMF is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* SPMF is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with
* SPMF. If not, see <http://www.gnu.org/licenses/>.
*/
import java.util.Iterator;
import java.util.Stack;
/**
* This is an implementation of a "red-black tree" based on the chapter 13 of the
* book: "Introductions to algorithms" by Cormen et al. (2001).
* Most of the code is based on the pseudo-code from this book.
* To understand the details of this implementation, please refer to the book.
* <br/><br/>
*
* Elements inserted in the tree have to extend the Comparable class.
* <br/><br/>
*
* The tree provides these operations: add, remove, size, contains, minimum,
* maximum, popMinimum, popMaximum, lower, higher.
*
* @author Philippe Fournier-Viger
*/
public class RedBlackTree<T extends Comparable<T>> implements Iterable<T> {
static final boolean BLACK = true; // define black
static final boolean RED = false; //define red
final private Node NULL = new Node(); // the sentinel node
private int size = 0; // number of elements currently in the tree
private Node root = NULL; // the tree root
// allow the same element to appear in the tree multiple times or not
boolean allowSameElementMultipleTimes = true;
/**
* Constructor
*
* @param allowSameElementMultipleTimes
* if set to true, this allows the tree to contains the same
* element multiple times. To check if an element is the same,
* this class use the compareTo method
*/
public RedBlackTree(boolean allowSameElementMultipleTimes) {
this.allowSameElementMultipleTimes = allowSameElementMultipleTimes;
}
/**
* Default constructor
*/
public RedBlackTree() {
// TODO Auto-generated constructor stub
}
/**
* Get the number of elements stored in this red-black tree.
* @return the number of elements.
*/
public int size() {
return size;
}
/**
* Is this tree empty?
* @return true if yes.
*/
public boolean isEmpty(){
return root == NULL;
}
/**
* Perform a left rotate of a sub-tree (see the book for details).
* @param x the node where the operation will be performed.
*/
private void leftRotate(Node x) {
Node y = x.right;
x.right = y.left;
if(!y.left.equals(NULL)){
y.left.parent = x;
}
y.parent = x.parent;
if(x.parent.equals(NULL)){
root = y;
}else if(x.equals(x.parent.left)){
x.parent.left = y;
}else{
x.parent.right = y;
}
y.left = x;
x.parent = y;
}
/**
* Perform a right rotate of a sub-tree (see the book for details).
* @param x the node where the operation will be performed.
*/
private void rightRotate(Node x) {
Node y = x.left;
x.left = y.right;
if(!y.right.equals(NULL)){
y.right.parent = x;
}
y.parent = x.parent;
if(x.parent.equals(NULL)){
root = y;
}else if(x.equals(x.parent.right)){
x.parent.right = y;
}else{
x.parent.left = y;
}
y.right = x;
x.parent = y;
}
/**
* Add an element to the tree (see the book for details).
* @param element the element to be added
*/
public void add(T element) {
// create element
Node z = new Node();
z.key = element;
Node y = NULL;
Node x = root;
while (x != NULL) {
y = x;
int compare = z.key.compareTo(x.key);
if (compare < 0) {
x = x.left;
} else {
if (compare == 0 && !allowSameElementMultipleTimes) {
return;
}
x = x.right;
}
}
z.parent = y;
if (y == NULL) { // case of an empty tree
root = z;
} else if (z.key.compareTo(y.key) < 0) {
y.left = z;
} else {
y.right = z;
}
size++;
// code specific to red black trees
z.left = NULL;
z.right = NULL;
z.color = RED;
insertFixup(z);
}
/**
* Fix the insertion at a given the node(see the book for details).
* @param z the node.
*/
private void insertFixup(Node z) {
while(z.parent.color == RED){
if(z.parent.equals(z.parent.parent.left)){
Node y = z.parent.parent.right;
if(y.color == RED){
z.parent.color = BLACK;
y.color = BLACK;
z.parent.parent.color = RED;
z = z.parent.parent;
}else{
if(z.equals(z.parent.right)){
z = z.parent;
leftRotate(z);
}
z.parent.color = BLACK;
z.parent.parent.color = RED;
rightRotate(z.parent.parent);
}
}else{
Node y = z.parent.parent.left;
if(y.color == RED){
z.parent.color = BLACK;
y.color = BLACK;
z.parent.parent.color = RED;
z = z.parent.parent;
}else{
if(z.equals(z.parent.left)){
z = z.parent;
rightRotate(z); // ok?
}
z.parent.color = BLACK;
z.parent.parent.color = RED;
leftRotate(z.parent.parent); // ok?
}
}
}
root.color = BLACK;
}
/**
* Transplant a subtree (see the book for details).
* @param u a node u
* @param v a node v
*/
private void transplant(Node u, Node v){
if(u.parent == NULL){
root = v;
}else if(u == u.parent.left){
u.parent.left = v;
}else{
u.parent.right = v;
}
v.parent = u.parent;
}
/**
* Remove an element from the tree
* @param element the element to be removed
*/
public void remove(T element) {
// First find the node containing the element.
Node z = search(root, element);
if (z == NULL) { // if the element is not in the tree
return;
}
performDelete(z);
}
/**
* perform the delete of an element from a node z (see the book for details)
* @param z the node
*/
private void performDelete(Node z) {
Node x;
Node y = z;
boolean yOriginalColor = y.color;
if (z.left == NULL) {
x = z.right;
transplant(z,z.right);
}else if(z.right == NULL){
x = z.left;
transplant(z, z.left);
}
else{
y = minimum(z.right);
yOriginalColor = y.color;
x = y.right;
if(y.parent.equals(z)){
x.parent = y;
}else{
transplant(y,y.right);
y.right = z.right;
y.right.parent = y;
}
transplant(z, y);
y.left = z.left;
y.left.parent = y;
y.color = z.color;
}
if(yOriginalColor == BLACK){
deleteFixup(x);
}
size--;
}
/**
* Perform the fixup for a delete operation (see book for details)
* @param x the node
*/
private void deleteFixup(Node x) {
while(x != root && x.color == BLACK){
if(x.equals(x.parent.left)){
Node w = x.parent.right;
if(w.color == RED){
w.color = BLACK;
x.parent.color = RED;
leftRotate(x.parent);
w = x.parent.right;
}
if(w.left.color == BLACK && w.right.color == BLACK){
w.color = RED;
x = x.parent; // A V�RIFIER....
}else{
if(w.right.color == BLACK){
w.left.color = BLACK;
w.color = RED;
rightRotate(w);
w = x.parent.right;
}
w.color = x.parent.color;
x.parent.color = BLACK;
w.right.color = BLACK;
leftRotate(x.parent);
x = root;
}
}else{
// repeat with left/right exchanged
Node w = x.parent.left;
if(w.color == RED){
w.color = BLACK;
x.parent.color = RED;
rightRotate(x.parent);
w = x.parent.left;
}
if(w.right.color == BLACK && w.left.color == BLACK){
w.color = RED;
x = x.parent; // A V�RIFIER....
}else{
if(w.left.color == BLACK){
w.right.color = BLACK;
w.color = RED;
leftRotate(w);
w = x.parent.left;
}
w.color = x.parent.color;
x.parent.color = BLACK;
w.left.color = BLACK;
rightRotate(x.parent);
x = root;
}
}
}
x.color = BLACK;
}
/**
* Get the successor node of a node
* (the node having the smallest value larger than the one of this node).
* @param x a node
* @return the succesor node
*/
private Node successor(Node x) {
if (x.right != NULL) {
return minimum(x.right);
}
Node y = x.parent;
while (y != NULL && x.equals(y.right)) {
x = y;
y = y.parent;
}
return y;
}
/**
* Get the predecessor node of a node
* (the node having the largest value smaller than the one of this node).
* @param x a node
* @return the predecessor node
*/
private Node predecessor(Node x) {
if (x.left != NULL) {
return maximum(x.left);
}
Node y = x.parent;
while (y != NULL && x.equals(y.left)) {
x = y;
y = y.parent;
}
return y;
}
/**
* Return the largest element having a value lower than a given element k.
*/
public T lower(T k){
Node result = lowerNode(k);
if(result == NULL){
return null;
}else{
return result.key;
}
}
/**
* Return the node having the largest element having a value lower than a given element k.
*/
public Node lowerNode(T k) {
Node x = root;
while (x != NULL) {
if (k.compareTo(x.key) >0) {
if(x.right != NULL){
x = x.right;
}else{
return x;
}
} else {
if(x.left != NULL){
x = x.left;
}else{
Node current = x;
while(current.parent != NULL && current.parent.left == current) {
current = current.parent;
}
return current.parent;
}
}
}
return NULL;
}
/**
* Return the largest element having a value lower than a given element k.
*/
public T higher(T k){
Node result = higherNode(k);
if(result == NULL){
return null;
}else{
return result.key;
}
}
/**
* Return the node having the largest element having a value higher than a given element k.
*/
private Node higherNode(T k) {
Node x = root;
while (x != NULL) {
if (k.compareTo(x.key) <0) {
if(x.left != NULL){
x = x.left;
}else{
return x;
}
} else {
if(x.right != NULL){
x = x.right;
}else{
Node current = x;
while(current.parent != NULL && current.parent.right == current) {
current = current.parent;
}
return current.parent;
}
}
}
return NULL;
}
/**
* Get the minimum element in the tree and remove it from the tree
* @return the minimum element in the tree
*/
public T popMinimum() {
if (root == NULL) {
return null;
}
Node x = root;
while (x.left != NULL) {
x = x.left;
}
T value = x.key;
performDelete(x);
return value;
}
/**
* Get the maximum element in the tree and remove it from the tree
* @return the maximum element in the tree
*/
public T popMaximum() {
if (root == NULL) {
return null;
}
Node x = root;
while (x.right != NULL) {
x = x.right;
}
T value = x.key;
performDelete(x);
return value;
}
/**
* Get the minimum element in the tree
* @return the minimum element in the tree
*/
public T minimum() {
if (root == NULL) {
return null;
}
return minimum(root).key;
}
/**
* Get the minimum node of a sub-tree.
* @param x the root of the sub-tree
* @return the minimum node
*/
private Node minimum(Node x) {
while (x.left != NULL) {
x = x.left;
}
return x;
}
/**
* Get the maximum element in the tree
* @return the maximum element in the tree
*/
public T maximum() {
if (root == NULL) {
return null;
}
return maximum(root).key;
}
/**
* Get the maximum node of a sub-tree.
* @param x the root of the sub-tree
* @return the maximum node
*/
private Node maximum(Node x) {
while (x.right != NULL) {
x = x.right;
}
return x;
}
/**
* Check if an element is contained in the tree
* @param k the element.
* @return true if the element is in the tree. Otherwise, false.
*/
public boolean contains(T k) {
return search(root, k) != NULL;
}
/**
* Method that search for an element and return the node that contains this
* element.
*
* @param x
* The node where the search will start.
* @param k
* The element to search
* @return The node containing the element or null if the element is not in
* the tree.
*/
private Node search(Node x, T k) {
while (x != NULL && !k.equals(x.key)) {
if (k.compareTo(x.key) < 0) {
x = x.left;
} else {
x = x.right;
}
}
return x;
}
/**
* Method toString that returns a string with all the elements in the tree
* according to the ascending order. NOTE : could be transformed into a non
* recursive algorithm.
*/
public String toString() {
if (root == null) {
return "";
}
return print(root, new StringBuilder()).toString();
}
/**
* Print a subtree to a StringBuilder
* @param x the root of the subtree
* @param buffer the buffer
* @return the buffer
*/
private StringBuilder print(Node x, StringBuilder buffer) {
if (x != null && x.key != null) {
print(x.left, buffer);
buffer.append(x.key + " ");
print(x.right, buffer);
}
return buffer;
}
// ============ FOR DEBUGGING ================
// public int calculateRealTreeSize(){
//
// return getRealSize(root, 1);
// }
//
// private int getRealSize(Node x, int count){
// if (x != null && x.key != null) {
// if(x.left.key != null){
// count = getRealSize(x.left, count+1);
// }if(x.right.key != null){
// count = getRealSize(x.right, count+1);
// }
// }
// return count;
// }
///=============================== NODE ================================================================
/**
* Inner class representing a node of a red-black tree.
* @author Philippe Fournier-Viger
*/
public class Node {
public T key = null; // the value stored in that node
boolean color = BLACK; // BLACK, otherwise RED
public Node left = NULL; // pointer to left sub-tree
public Node right = NULL; // pointer to right sub-tree
Node parent = NULL; // pointer to parent if not the root
}
///=============================== ITERATOR ================================================================
/**
* This is an iterator for traversing the red-black tree
* @author Philippe Fournier-Viger
*
* @param <S>
*/
public class RedBlackTreeIterator<S> implements Iterator<S> {
// we need to use a stack to traverse the tree in the order
// from smallest to largest.
private Stack<Node> nodesToVisitNext;
public RedBlackTreeIterator(Node root) {
nodesToVisitNext = new Stack<Node>();
nodesToVisitNext.push(root);
}
@Override
/**
* Is there a next element after this one?
*/
public boolean hasNext() {
return nodesToVisitNext.isEmpty() == false;
}
@Override
/**
* Get the next element.
*/
public S next() {
Node currentNode = nodesToVisitNext.pop();
// if(currentNode.key == null ){ // DEBUG
// System.out.println("CURRENT : " + currentNode.key);
// }
if(currentNode.left != null && currentNode.left.key != null){
nodesToVisitNext.push(currentNode.left);
}
if(currentNode.right != null && currentNode.right.key != null){
nodesToVisitNext.push(currentNode.right);
}
return (S) currentNode.key;
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not implemented yet.");
}
}
@Override
public Iterator<T> iterator() {
return new RedBlackTreeIterator<T>(root);
}
}