com.github.ojil.algorithm.ThreadedBinaryTree Maven / Gradle / Ivy
Show all versions of ojil-core Show documentation
/**
* Copyright 2008 by Jon A. Webb
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the Lesser GNU General Public License
* along with this program. If not, see .
*
*/
package com.github.ojil.algorithm;
import java.util.Vector;
import com.github.ojil.core.ImageError;
/**
* ThreadedBinaryTree is an implements threaded binary trees, as the name
* implies. The implementation of threaded binary trees here was guided by the
* tutorial at http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_tut_bst1.aspx#
* thread.
*
* Threaded binary trees are like normal (unbalanced) binary trees except that
* nodes which have no right successor have a pointer to their inorder inserted
* in the right pointer. This makes it possible to perform an inorder traversal
* starting at any node without doing a full traversal of the tree.
*
* The use of threaded binary trees in JJIL is to make it possible to add() a
* new element to the tree in average log n time and then to traverse from any
* node to any other node in time equal to the number of nodes between the two
* nodes. If I'd used a normal binary tree to do the traversal the time would
* have been equal to the number of nodes in the tree. Both are O(n) but the
* number of nodes between the two nodes being traversed should be a fraction of
* the total number of nodes in the tree.
*
* @author webb
*
*/
public class ThreadedBinaryTree {
/**
* boolThread identifies the case where the right pointer is the inorder
* successor, not the right descendant.
*/
private boolean boolThread = false;
/**
* key is the comparison key stored at the node.
*/
private final ComparableJ2me key;
/**
* left is a pointer to the left side of the tree, or null if there is no
* left descendant.
*/
private ThreadedBinaryTree left = null;
/**
* right is a pointer to the right side of the tree or the inorder successor
* if there is no right descendant.
*/
private ThreadedBinaryTree right = null;
/**
* value associated with this node
*/
private Object value = null;
/**
* Create a new ThreadedBinaryTree node without descendants or an inorder
* successor.
*
* @param key
* the key for comparison.
*/
public ThreadedBinaryTree(final ComparableJ2me key) {
this.key = key;
}
/**
* Create a new ThreadedBinaryTree node with an inorder successor.
*
* @param key
* the key for comparison.
* @param thread
* the inorder successor.
*/
public ThreadedBinaryTree(final ComparableJ2me key, final ThreadedBinaryTree thread) {
this.key = key;
right = thread;
boolThread = true;
}
/**
* Add a new key to an existing ThreadedBinaryTree, returning the
* ThreadedBinaryTree node with the matching key. If the key is already in
* the tree we make no change to the tree and return the matching node.
* Otherwise we create a new node and link it into the tree.
*
* @param key
* the key for comparison.
* @return the ThreadedBinaryTree node that matches key.
* @throws ImageError
* in the case of type error (key wrong type)
*/
public ThreadedBinaryTree add(final ComparableJ2me key) throws ImageError {
final int n = key.compareTo(this.key);
if (n == 0) {
return this; // already in tree
} else if (n < 0) {
if (left != null) {
return left.add(key);
} else {
// left descendant's inorder successor is this
left = new ThreadedBinaryTree(key, this);
return left;
}
} else {
// right descendant is (almost) never null, but boolThread
// indicates we're at the bottom of the tree.
if ((right != null) && !boolThread) {
return right.add(key);
} else {
// link in new right descendant. Its inorder
// successor is this node's inorder successor.
// This node now has a right descendant and is
// no longer a threaded node.
right = new ThreadedBinaryTree(key, right);
boolThread = false;
return right;
}
}
}
/**
* Find the ThreadedBinaryTree node that matches key.
*
* @param key
* the key for comparison
* @return the ThreadedBinaryTree node which matches key, or null if nothing
* does.
* @throws ImageError
* in the case of type error (key wrong type)
*/
public ThreadedBinaryTree find(final ComparableJ2me key) throws ImageError {
final int n = this.key.compareTo(key);
if (n == 0) {
return this; // found
} else if (n > 0) {
// this is > than the key we are searching for. The match
// is either to the left or not there
if (left != null) {
return left.find(key);
} else {
return null; // not found
}
} else {
// this is < than the key we are searching for. The match
// is either to the right or not there.
if ((right != null) && !boolThread) {
return right.find(key);
} else {
return null; // not found
}
}
}
/**
* Find the ThreadedBinaryTree node with the largest key which is @le; the
* given key.
*
* @param key
* the key to compare
* @return the ThreadedBinaryTree node which matches key, or null if nothing
* does.
* @throws ImageError
* in the case of type error (key wrong type)
*/
public ThreadedBinaryTree findNearest(final ComparableJ2me key) throws ImageError {
final int n = this.key.compareTo(key);
if (n == 0) {
return this; // found exact match
} else if (n < 0) {
// this is < the key we are searching for. The best match is
// the match to the right if there is one, otherwise this.
if ((right != null) && !boolThread) {
// the nearest is either this element, or down and to the
// right
final ThreadedBinaryTree tbtRight = right.findNearest(key);
if (tbtRight == null) {
return this;
} else {
return tbtRight;
}
} else {
// nothing to the right
return this;
}
} else {
// this is greater than the key. the nearest is either down and
// to the left, or nothing
if (left != null) {
return left.findNearest(key);
} else {
return null; // not found
}
}
}
/**
* Perform an inorder traversal of the tree from this to end and return a
* Vector containing all the nodes traversed, in the order of traversal, or
* null if there is no inorder path starting at this and ending at end.
*
* @param end
* the end of the path.
* @return a Vector containing all the nodes traversed, in the order of
* traversal, or null if there is no path.
*/
@SuppressWarnings("rawtypes")
public Vector inorderTraverse(final ThreadedBinaryTree end) {
final Vector v = new Vector();
// Start traversal with empty Vector. The traversal
// is initialized with bFollowedThread = false because
// we want to do inorder traversal from this node.
return this.inorderTraverse(v, false, end);
}
/**
* Helper function for actually doing the traversal. Appends the next node
* traversed to the end of v and continues the traversal recursively.
*
* @param v
* the input Vector; this becomes the output on a successful
* traversal.
* @param bFollowedThread
* if true indicates we followed a thread (an upward link) to get
* to this node.
* @param end
* the ending node for the traversal.
* @return the parameter v with the nodes traversed from this to end
* appended to it, if there is a path; otherwise, null.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private Vector inorderTraverse(final Vector v, final boolean bFollowedThread, final ThreadedBinaryTree end) {
if (!bFollowedThread) {
// last traversal was downwards, not a threaded link.
// this means that we should continue down and to the left.
if (left != null) {
return left.inorderTraverse(v, false, end);
} else {
// can't continue down and to the left. Add current
// node to the traversal list and continue right,
// either continuing down or up following thread.
v.addElement(this);
if (this == end) {
// success
return v;
} else {
return right.inorderTraverse(v, boolThread, end);
}
}
} else {
// last traversal was upwards (thread) link. We must
// add current node to traversal list and continue
// to the right (following descendant or thread).
v.addElement(this);
if (this == end) {
// success
return v;
}
if (right != null) {
return right.inorderTraverse(v, boolThread, end);
} else {
// there is no right successor, so we reached end of tree
// without finding end. There is no path.
return null;
}
}
}
/**
* Return the key associated with this node.
*
* @return the key associated with this node.
*/
public ComparableJ2me getKey() {
return key;
}
/**
* Return the current value associated with this node.
*
* @return the value associated with this node.
*/
public Object getValue() {
return value;
}
/**
* Change the value associated with this node.
*
* @param value
* the new value to associate with this node.
*/
public void setValue(final Object value) {
this.value = value;
}
/**
* Implement toString.
*/
@Override
public String toString() {
String szRes = super.toString() + "(key=";
if (key != null) {
szRes += key.toString();
} else {
szRes += "null";
}
szRes += ",left=";
if (left != null) {
szRes += left.toString();
} else {
szRes += "null";
}
szRes += ",right=";
if (boolThread) {
szRes += "thread," + ((Object) right).toString();
} else if (right == null) {
szRes += "null";
} else {
szRes += right.toString();
}
szRes += ")";
return szRes;
}
// Test code
// Tries all possible ThreadedBinaryTrees with elements 0-5 and verifies
// inorder traversal from 0-5.
// public int valueOf() {
// return this.n;
// }
// }
// try {
// for (int i = 0; i<6; i++) {
// for (int j = 0; j<6; j++) {
// if (i != j) for (int k=0; k<6; k++) {
// if (k != i && k != j) for (int l=0; l<6; l++) {
// if (l != k && l != j && l != i) for (int m=0; m<6; m++) {
// if (m != l && m != k && m != j && m != i) for (int n=0; n<6; n++) {
// if (n != m && n != l && n != k && n != j && n != i) {
// ThreadedBinaryTree t = new ThreadedBinaryTree(new ComparableInt(i));
// t.add(new ComparableInt(j));
// t.add(new ComparableInt(k));
// t.add(new ComparableInt(l));
// t.add(new ComparableInt(m));
// t.add(new ComparableInt(n));
// ThreadedBinaryTree tbt0 = t.find(new ComparableInt(0));
// ThreadedBinaryTree tbt5 = t.find(new ComparableInt(5));
// Vector> v = tbt0.inorderTraverse(tbt5);
// for (int o = 0; o<6; o++) {
// ThreadedBinaryTree tbt = (ThreadedBinaryTree)v.elementAt(o);
// ComparableInt ci = (ComparableInt)tbt.getKey();
// if (ci.valueOf() != o) {
// Vector> u = tbt0.inorderTraverse(tbt5);
// int error = 1;
// }
//
// }
// }
// }
// }
// }
// }
// }
// }
// } catch (jjil.core.Error e) {
//
// }
}