package ca.pfv.spmf.datastructures.binarytree;
/* 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/>.
*/
/**
* This is an implementation of a "binary tree" based on the chapter 12 of the
* book: "Introductions to algorithms" by Cormen et al. (2001).
* Most of the code is based on the pseudo-code from this 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, higher, lower.
*
* @author Philippe Fournier-Viger
*/
public class BinaryTree<T extends Comparable<T>> {
// number of elements currently in the tree
private int size = 0;
// the tree root
private Node root = null;
// allow the same element to appear multiple times in the tree
// 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 BinaryTree(boolean allowSameElementMultipleTimes) {
this.allowSameElementMultipleTimes = allowSameElementMultipleTimes;
}
/**
* Default constructor
*/
public BinaryTree() {
}
/**
* Return the number of elements stored in the tree
* @return an integer
*/
public int size() {
return size;
}
/**
* Add an element to the tree.
* @param element the element to be added
*/
public void add(T element) {
// create a node for that element
Node z = new Node();
z.key = element;
Node y = null;
Node x = root; // the root
// loop while x is not null
while (x != null) {
// set y to x
y = x;
// compare the key of the element to be inserted with x
int compare = z.key.compareTo(x.key);
// if smaller we go in the left subtree
if (compare < 0) {
x = x.left;
} else {
// otherwise, it it is the same and we don,t allow
// multiple instances of the same item
if (compare == 0 && !allowSameElementMultipleTimes) {
return; // we don't add it
}
// otherwise go the right subtree
x = x.right;
}
}
// after the previous loop has terminated, we have found
// the place where the new node z should be inserted (as a child of y) so
// we will insert it there.
// we set the parent of z as y
z.parent = y;
if (y == null) { // case of an empty tree
root = z; // set z as the root
}// if z is small than y
else if (z.key.compareTo(y.key) < 0) {
// append as left child
y.left = z;
} else {
// otherwise append as right child
y.right = z;
}
// increase the number of elements in the tree
size++;
}
/**
* Check if the tree is empty.
* @return true if empty
*/
public boolean isEmpty(){
return root == null;
}
/**
* 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;
}
// if found, delete it
performDelete(z);
}
/**
* This method delete a given node from the tree
* @param z a node.
*/
private void performDelete(Node z) {
// create a node pointer y
Node y;
// if z has no left or right subtree
if (z.left == null || z.right == null) {
// set y to z
y = z;
} else {
// set y as the successor of z
y = successor(z);
}
// create a node pointer x
Node x;
// if y has a left subtree
if (y.left != null) {
// set x as that subtree
x = y.left;
} else {
//otherwise set x as the right subtree of y
x = y.right;
}
// if x is not null
if (x != null) {
// set the parent of x as the parent of y.
x.parent = y.parent;
}
// if the parent of y is null
if (y.parent == null) {
// set x as the root
root = x;
} else if (y.equals(y.parent.left)) {
// otherwise if y is the parent of the left subtree of y
// set it to x
y.parent.left = x;
} else {
// otherwise set the right subtree of the parent of y to x
y.parent.right = x;
}
// if y is not z
if (y != z) {
// set the element of z to y
z.key = y.key;
}
// decrease the size by 1
size--;
}
/**
* Get the smallest value greater than the one stored in a node x.
* @param x a node
* @return the node containing the smallest value greater than the one in x.
*/
private Node successor(Node x) {
// if there is a right subtree
if (x.right != null) {
// return the minimum of the right subtree
return minimum(x.right);
}
// otherwise, go to the parent
Node y = x.parent;
// while y is not the root and x is not equal to the right subtree of y
while (y != null && x.equals(y.right)) {
// set x to y
x = y;
// explore the parent of y
y = y.parent;
}
// return y
return y;
}
/**
* Get the largest value smaller than the one stored in node X.
* @param x the node X.
* @return the largest value smaller than the one stored in node X.
*/
private Node predecessor(Node x) {
// if there is a left subtree
if (x.left != null) {
// return the maximum of the right subtree
return maximum(x.left);
}
// otherwise, go to the parent
Node y = x.parent;
// while y is not the root and x is not equal to the left subtree of y
while (y != null && x.equals(y.left)) {
// set x to y
x = y;
// explore the parent of y
y = y.parent;
}
// return y
return y;
}
/**
* Get the minimum element in the tree and remove it from the tree
*
* @return the minimum element in the tree
*/
public T popMinimum() {
// if the tree is empty, return null
if (root == null) {
return null;
}
// From the root, go to the left until a leaf is reached
Node x = root;
while (x.left != null) {
x = x.left;
}
// get the value of the leaf
T value = x.key;
// delete the node
performDelete(x);
// return the value
return value;
}
/**
* Return the largest element having a value lower than a given element k.
*/
public T lower(T k) {
// call the method lowerNode who do the main job
Node result = lowerNode(k);
// if no result, return null
if (result == null) {
return null;
} else {
// otherwise return the value contained in the node found
return result.key;
}
}
/**
* Return the node having the largest element having a value lower than a
* given element k.
*/
private 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) {
// call the method higherNode to locate the node meeting the criterion
Node result = higherNode(k);
// if no result, return null
if (result == null) {
return null;
} else {
// otherwise return the value contained in the node found
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
*
* @return the minimum element in the tree
*/
public T minimum() {
// go down the left links until reaching a leaf
if (root == null) {
return null;
}
// return the leaf value
return minimum(root).key;
}
/**
* Return node with the smallest value in the tree
* @param x the node where the search should start
* @return the node meeting the criterion
*/
private Node minimum(Node x) {
// go down the left links until reaching a leaf
while (x.left != null) {
x = x.left;
}
// return the leaf
return x;
}
/**
* Get the maximum element in the tree and remove it from the tree
*
* @return the maximum element in the tree
*/
public T popMaximum() {
// if the tree is empty, return null
if (root == null) {
return null;
}
// start from the root and go down the right subtrees until
// a leaf is reached
Node x = root;
while (x.right != null) {
x = x.right;
}
// get the value of the leaf
T value = x.key;
// then delete the leaf and return the value
performDelete(x);
return value;
}
/**
* Get the maximum element in the tree
*
* @return the maximum element in the tree
*/
public T maximum() {
// if the tree is empty return nothing
if (root == null) {
return null;
}
// otherwise go down the right links until reaching a leaf and return the key
return maximum(root).key;
}
/**
* Get the maximum element in the tree
* @return the node containing the maximum element in the tree
*/
private Node maximum(Node x) {
// go down the right links until reaching a leaf
while (x.right != null) {
x = x.right;
}
// return the node
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) {
// call the search function and if not null it means
// that the element is in the tree
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 a leaf is not reach and the key has not been found
while (x != null && !k.equals(x.key)) {
// compare the key with the current node
// if smaller, go left
if (k.compareTo(x.key) < 0) {
x = x.left;
} else {
// otherwise go right
x = x.right;
}
}
// return the node or null if nothing is found
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 the tree is empty then return an empty string
if (root == null) {
return "";
}
// call the recursive helper method to print the tree
return print(root, new StringBuilder()).toString();
}
/**
* Print a subtree to a StringBuilder.
* @param x the root of the subtree
* @param buffer the StringBuilder
* @return the StringBuilder
*/
private StringBuilder print(Node x, StringBuilder buffer) {
// if the subtree is not empty and there is a key stored in that node
if (x != null && x.key != null) {
// recursive call
print(x.left, buffer);
// append the x value
buffer.append(x.key + " ");
// recursive call
print(x.right, buffer);
}
return buffer;
}
/**
* Internal class that represents a node of the binary tree
* @author Philippe Fournier-Viger
*/
public class Node {
T key = null; // the value stored in this node
Node left = null; // pointer to left child
Node right = null; // pointer to right child
Node parent = null; // pointer to parent
/**
* Get a string representatin of this node.
* @return a string
*/
public String toString() {
// create a string buffer
StringBuilder buffer = new StringBuilder();
// append the key stored in this node
buffer.append(key.toString());
// if there is a left subtree, then append the key of the left child
if (left != null) {
buffer.append(" L= " + left.key);
}
// if there is a right subtree, then append the key of the right child
if (right != null) {
buffer.append(" R= " + right.key);
}
return buffer.toString();
}
}
}