/*
This file is part of the algoviz@vt collection of algorithm
visualizations.
This program 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.
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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this distribution. If not, see
.
*/
package bst;
import java.awt.Color;
import java.awt.Graphics;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
/**
* A Binary Search Tree class. Although this class is heavily modified, the core
* BST functionality remains intact. Basically, the modifications make tracing
* each BST operations easy.
*
* @author Andrew Whitaker (aawhitak@vt.edu)
* @author Andy Street [Major updates for AVL]
* @author Akash Jana (akjana35@gmail.com)
*
* @param The data type of the keys for the Binary Search Tree.
* @param The data type of the values in this BST.
*/
public class BST, V extends GraphicalNode> implements DictionaryADT
{
public static enum Orientation {
LEFT, RIGHT;
}
private static int count=0;
/* The root of this tree */
private Node root;
private Node lastNode;
/* The number of nodes in the tree */
private int nodeCount;
/*
* A list containing the States that the most recent operation has gone
* through.
*/
private ArrayList stateList;
/* The TreePanel that this BST belongs to. */
private TreePanel treePanel;
/* The number of levels in this tree. */
private int levels;
private boolean found=false;
VirtualState initialState;
private Node son,parent,grandParent,supergrandParent;
/**
* Creates a new BST.
*
* @param p
*/
public BST(TreePanel p) {
this();
treePanel = p;
}
public BST() {
root = null;
nodeCount = 0;
stateList = new ArrayList();
levels = 0;
lastNode=null;
}
/**
* Clears the BST (not implemented)
*/
public void clear() {
root=null;
// TODO Auto-generated method stub
}
public Node find(K k) {
found=false;
stateList.clear();
lastNode=null;
stateList.clear();
VirtualStateCollection vt = new VirtualStateCollection();
// vt.setDelay(treePanel.ANIMATION_TIME);
ChangeState initial = new ChangeState(getCurrentState(), vt, Operation.FIND, "Starting find operation.\n");
stateList.add(initial);
Node tmp= findHelper(root, k, vt);
traversePostOrder(root, 0, 0, treePanel.getGraphics(), true, false);
VirtualState fin = getCurrentState();
fin.setNodesMoving(true);
ChangeState changeState = new ChangeState(fin, vt, Operation.FIND, "Done.");
lastNode=null;
return tmp;
}
/**
* Find helper method. Works just like a normal BST find method, but tracks
* each "State" the BST is in using a list.
*
* @param node
* The node to recursively find from.
* @param k
* The key to look for.
* @return The Vent corresponding to the key.
*/
private Node findHelper(Node node, K k, VirtualStateCollection vt) {
if (node == null){
ChangeState s = new ChangeState(getCurrentState(), vt, Operation.FIND, "Node not found\n");
stateList.add(s);
return null;
}
if (k.compareTo(node.key) < 0) {
node.value.select();
ChangeState s = new ChangeState(getCurrentState(), vt, Operation.FIND, "Following " + node.key + "'s left branch\n");
node.value.unSelect();
stateList.add(s);
lastNode=node;
return findHelper(node.left, k, vt);
}
else if (node.key.equals(k))
{
lastNode=node;
found=true;
node.value.select();
ChangeState s = new ChangeState(getCurrentState(), vt, Operation.FIND, "Found node successfully\n");
node.value.unSelect();
stateList.add(s);
return node;
}
else
{
node.value.select();
ChangeState s = new ChangeState(getCurrentState(), vt, Operation.FIND, "Following " + node.key + "\'s right branchn");
node.value.unSelect();
stateList.add(s);
lastNode=node;
return findHelper(node.right, k, vt);
}
}
/**
* Inserts a new node in the Binary Search Tree with the specified K and
* Vent.
*
* @param k
* The key to put into the new node.
* @param e
* The element to put into the new node.
*/
public void insert(K k, V e) {
stateList.clear();
lastNode=null;
VirtualStateCollection sc = new VirtualStateCollection();
// sc.setDelay(treePanel.ANIMATION_TIME);
ChangeState initial = new ChangeState(getCurrentState(), sc, Operation.INSERT, "Starting insert operation.\n");
stateList.add(initial);
root = insertHelper(root, k, e, sc);
levels=getLevels();
treePanel.setLevels(levels);
traversePostOrder(root, 0, 0, null, true, false);
VirtualState fin = getCurrentState();
fin.setNodesMoving(true);
ChangeState changeState = new ChangeState(fin, sc, Operation.INSERT, "Done.");
}
/**
* Helper method for insertion. Works just like a regular BST insertion
* except "States" are kept track of.
*
* @param node
* The node to recursively insert on.
* @param k
* The key to insert
* @param it
* The element to insert
* @return The newly inserted node.
*/
private Node insertHelper(Node node, K k, V it, VirtualStateCollection sc)
{
if (node == null) {
levels++;
Node temp= new Node(k, it, null, null);
ChangeState s = new ChangeState(getCurrentState(), sc, Operation.INSERT, "New Node Inserted\n");
stateList.add(s);
return temp;
}
if (k.compareTo(node.key) < 0) {
node.value.select();
ChangeState s = new ChangeState(getCurrentState(), sc, Operation.INSERT, "Following " + node.key + "'s left branch\n");
node.value.unSelect();
stateList.add(s);
levels++;
node.left = insertHelper(node.left, k, it, sc);
node.updateHeight();
} else {
node.value.select();
ChangeState s = new ChangeState(getCurrentState(), sc, Operation.INSERT, "Following " + node.key + "'s right branch\n");
node.value.unSelect();
stateList.add(s);
levels++;
node.right = insertHelper(node.right, k, it, sc);
node.updateHeight();
}
return node;
}
/**
* Prints the BST to the specified writer.
*
* @param w
* The writer to write the BST to.
*/
public void print(Writer w) {
printHelper(root, w);
}
/**
* @return The list of states that the BST went through to complete the last
* operation.
*/
public ArrayList getStateList() {
return stateList;
}
/**
* Helper method for printing a BST.
*
* @param root
* The node to recursively print.
* @param w
* The writer to print to.
*/
private void printHelper(Node root, Writer w) {
if (root == null) {
return;
}
printHelper(root.left, w);
try {
w.write(root.value + " ");
} catch (IOException e) {
}
printHelper(root.right, w);
}
/**
* Removes the node with the specified key from the tree.
*/
public V remove(K k) {
lastNode=null;
stateList.clear();
VirtualStateCollection sc = new VirtualStateCollection();
// sc.setDelay(treePanel.ANIMATION_TIME);
ChangeState initial = new ChangeState(getCurrentState(), sc, Operation.REMOVE, "Starting remove operation.\n");
stateList.add(initial);
if(root.key.compareTo(k)==0){
if(root.left==null&&root.right==null){
root=null;
}
else if (root.left == null) {
root=root.right;
} else if (root.right == null) {
root=root.left;
} else {
Node tmp = getMin(root.right,sc);
root.key=tmp.key;
root.right = deleteMin(root.right);
}
traversePostOrder(root, 0, 0, null, true, false);
VirtualState fin = getCurrentState();
fin.setNodesMoving(true);
ChangeState changeState = new ChangeState(fin, sc, Operation.REMOVE, "Done.");
if(root!=null)
return root.value;
else
return null;
}
root = removeHelper(root, k, sc);
// if(lastNode!=null){
// System.out.println("lastNode is "+lastNode.value);
// }
traversePostOrder(root, 0, 0, null, true, false);
VirtualState fin = getCurrentState();
fin.setNodesMoving(true);
ChangeState changeState = new ChangeState(fin, sc, Operation.REMOVE, "Done.");
if(root!=null)
return root.value;
else
return null;
}
/**
* Helper for the remove method. State-tracking has not been implemented for
* remove().
*
* @param node
* The node to recursively remove on.
* @param k
* The key to remove.
* @return The node that was removed.
*/
public Node removeHelper(Node node, K k, VirtualStateCollection sc) {
if (node == null)
return null;
else if (k.compareTo(node.key) < 0) {
lastNode=node;
node.value.select();
ChangeState s = new ChangeState(getCurrentState(), sc, Operation.REMOVE, "Following " + node.key + "'s left branch\n");
node.value.unSelect();
stateList.add(s);
levels++;
node.left = removeHelper(node.left, k, sc);
node.updateHeight();
} else if (k.compareTo(node.key) > 0) {
lastNode=node;
node.value.select();
ChangeState s = new ChangeState(getCurrentState(), sc, Operation.REMOVE, "Following " + node.key + "'s right branch\n");
node.value.unSelect();
stateList.add(s);
levels++;
node.right = removeHelper(node.right, k, sc);
node.updateHeight();
} else {
node.value.select();
ChangeState s = new ChangeState(getCurrentState(), sc, Operation.REMOVE, "Deleting node" + node.key + "\nFollowing "+node.key+"'s right branch\n");
node.value.unSelect();
stateList.add(s);
Node tmp = getMin(node.right,sc);
Node old=node;
Node oldright=node.right;
//node.value=tmp.value;
node.key=tmp.key;
node.right = deleteMin(node.right);
VirtualState next=initialState.copy();
if(tmp.key.compareTo(this.getParent(old.key, root).key)<0){
next.removeLine(-this.getParent(old.key, root).hashCode());
next.addLine(-this.getParent(old.key, root).hashCode(), next.getNode(this.getParent(old.key, root).hashCode()), next.getNode(tmp.hashCode()));
stateList.add(new ChangeState(next,sc,Operation.REMOVE,"Changing pointer to replace left child\n"));
}
else{
next.removeLine(this.getParent(old.key, root).hashCode());
next.addLine(this.getParent(old.key, root).hashCode(), next.getNode(this.getParent(old.key, root).hashCode()), next.getNode(tmp.hashCode()));
stateList.add(new ChangeState(next,sc,Operation.REMOVE,"Changing pointer to replace left child\n"));
}
next=next.copy();
next.addLine(-tmp.hashCode(), next.getNode(tmp.hashCode()), next.getNode(node.left.hashCode()));
if(oldright.key.compareTo(tmp.key)!=0)
stateList.add(new ChangeState(next,sc,Operation.REMOVE,"Changing pointer to replace right child\n"));
next=next.copy();
node.value=tmp.value;
if(node.right!=null)
next.addLine(tmp.hashCode(), next.getNode(tmp.hashCode()), next.getNode(node.right.hashCode()));
if(tmp.right!=null)
stateList.add(new ChangeState(next,sc,Operation.REMOVE,"Changing pointers to reposition In-order Successor's right child\n"));
else{
ChangeState f = new ChangeState(getCurrentState(), sc, Operation.REMOVE, "Deletion over \n");
stateList.add(f);
return node;
}
next=next.copy();
if(tmp.right!=null){
next.removeLine(tmp.hashCode());
next.addLine(this.getParent(tmp.key, root).hashCode(),next.getNode(this.getParent(tmp.key, root).hashCode()),next.getNode(tmp.right.hashCode()));
}
ChangeState f = new ChangeState(getCurrentState(), sc, Operation.REMOVE, "Deletion over \n");
stateList.add(f);
}
return node;
}
private Node replaceRight(Node node,Node parent,VirtualStateCollection sc){
Node temp=getParent(parent.key,root);
if(node==null)
return null;
VirtualState oldState=getCurrentState();
ChangeState sa=(new ChangeState(oldState,sc,Operation.REMOVE,"Following "+node.key+"'s right branch\nFound Replacement Node\n Changing pointers\n"));
stateList.add(sa);
VirtualState next=oldState.copy();
if(temp.right==parent){
next.removeLine(parent.hashCode());
next.removeLine(temp.hashCode());
next.addLine(temp.hashCode(),next.getNode(temp.hashCode()),next.getNode(node.hashCode()));
}
else{
next.removeLine(parent.hashCode());
next.removeLine(-temp.hashCode());
next.addLine(-temp.hashCode(),next.getNode(temp.hashCode()),next.getNode(node.hashCode()));
}
ChangeState f = new ChangeState(next, sc, Operation.REMOVE, "Deletion over \n");
stateList.add(f);
return node;
}
private Node replaceLeft(Node node,Node parent,VirtualStateCollection sc){
Node temp=getParent(parent.key,root);
VirtualState oldState=getCurrentState();
ChangeState sa=(new ChangeState(oldState,sc,Operation.REMOVE,"Following "+node.key+"'s left branch\nFound Replacement Node\n Changing pointers\n"));
stateList.add(sa);
VirtualState next=oldState.copy();
if(temp.right==parent){
next.removeLine(-parent.hashCode());
next.removeLine(temp.hashCode());
next.addLine(temp.hashCode(),next.getNode(temp.hashCode()),next.getNode(node.hashCode()));
}
else{
next.removeLine(-parent.hashCode());
next.removeLine(-temp.hashCode());
next.addLine(-temp.hashCode(),next.getNode(temp.hashCode()),next.getNode(node.hashCode()));
}
ChangeState f = new ChangeState(next, sc, Operation.REMOVE, "Deletion over \n");
stateList.add(f);
return node;
}
/**
* Helper method for remove, gets the minimum node in the subtree at root.
*
* @param root
* The subtree to search for the minimum node.
* @return The minimum node in the subtree.
*/
private Node getMin(Node node,VirtualStateCollection sc) {
if (node.left == null) {
initialState=getCurrentState();
node.value.select();
stateList.add(new ChangeState(getCurrentState(), sc, Operation.REMOVE, "Found in-order successor node "+node.key+"\n"));
node.value.unSelect();
return node;
} else {
node.value.select();
ChangeState f = new ChangeState(getCurrentState(), sc, Operation.REMOVE, "Following "+node.key+"'s left branch\n");
node.value.unSelect();
stateList.add(f);
return getMin(node.left,sc);
}
}
/**
* Helper for the remove method.
*
* @param node
* The subtree to remove the minimum from.
* @return The node that was removed.
*/
private Node deleteMin(Node node) {
if (node.left == null){
return node.right;
}
else {
node.left = deleteMin(node.left);
return node;
}
}
/**
* Returns the size of this tree.
*/
public int size() {
// TODO Auto-generated method stub
return nodeCount;
}
/**
* Draws the tree in a post-order traversal. Draws each node at the proper
* place, using the BST's TreePanel.
*
* @param g The graphics to draw on.
* @param recalc If the tree should be recalculated.
* @param draw If the tree should be redrawn
*/
public void drawPostOrder(Graphics g, boolean recalc, boolean draw) {
traversePostOrder(root, 0, 0, g, recalc, draw);
}
/**
* Recursive helper for drawing the tree.
* @param node The node to draw/traverse
* @param x The "abstract x" of the current node.
* @param y The "abstract y" of the current node.
* @param g The graphics to draw on.
* @param recalcPos If the tree should be recalculated
* @param draw If the tree should be drawn.
*/
public void traversePostOrder(Node node, int x, int y,
Graphics g, boolean recalcPos, boolean draw) {
if (node == null) {
return;
}
traversePostOrder(node.left, x * 2, y + 1, g, recalcPos, draw);
traversePostOrder(node.right, x * 2 + 1, y + 1, g, recalcPos, draw);
visit(node, x, y, g, recalcPos, draw);
if (node.left != null && !(node.left.value).isHidden() && draw) {
g.setColor(Color.BLACK);
GraphicalNode n = node.left.value;
GraphicalNode parent = node.value;
g.drawLine(parent.getX() + (GraphicalNode.NODE_WIDTH / 2), parent.getY() + GraphicalNode.NODE_HEIGHT, n.getX() + (GraphicalNode.NODE_WIDTH / 2), n.getY());//, g);
}
if (node.right != null && !(node.right.value).isHidden() && draw) {
g.setColor(Color.BLACK);
GraphicalNode n = node.right.value;
GraphicalNode parent = node.value;
g.drawLine(parent.getX() + (GraphicalNode.NODE_WIDTH / 2), parent.getY() + GraphicalNode.NODE_HEIGHT, n.getX() + (GraphicalNode.NODE_WIDTH / 2), n.getY());//, g);
}
}
/**
* Visit the specified node.
* @param node The node to visit.
* @param x The x position of the node.
* @param y The y position of the node.
* @param g The graphics to draw on
* @param recalcPos Recalculate the tree?
* @param draw Redraw the tree?
*/
private void visit(Node node, int x, int y, Graphics g,
boolean recalcPos, boolean draw) {
// System.out.println("visiting node "+node.key);
GraphicalNode curr = node.value;
if (recalcPos) {
curr.setXPos(treePanel.getPhysicalX(x, y));
curr.setYPos(y * TreePanel.NODE_VERTICAL_DISTANCE);
// System.out.println(curr.getX()+" "+curr.getY());
curr.setAbstractX(x);
curr.setAbstractY(y);
}
if (draw) {
curr.draw(g);
}
}
/**
* Gets the VirtualState of the whole tree.
*/
private VirtualState getCurrentState()
{
return getCurrentState(root);
}
/**
* Returns the VirtualState of the tree with root r
*
* @param r the root
* @return the current VirtualState of that tree
*/
private VirtualState getCurrentState(Node r)
{
VirtualState ret = new VirtualState();
_saveState(ret, r);
return ret;
}
private void _saveState(VirtualState s, Node n)
{
if(n == null) return;
s.addNode(n);
Node left = n.left;
Node right = n.right;
Location myLoc = n.value.getLocation();
if(left != null)// && !left.value.isHidden())
{
Location lLoc = left.value.getLocation();
Line l = new Line(myLoc, lLoc);
l.id = -n.value.hashCode();
s.addLine(l);
}
if(right != null)// && !right.value.isHidden())
{
Location rLoc = right.value.getLocation();
Line l = new Line(myLoc, rLoc);
l.id = n.value.hashCode();
s.addLine(l);
}
_saveState(s, left);
_saveState(s, right);
}
//Method getParent returns the parent node for input node key
public Node getParent(K key,Node root){
if(root!=null&&key==root.key){
return null;
}
else if(key.compareTo(root.key)<0){
if(root.left!=null&&root.left.key==key)
return root;
else return getParent(key,root.left);
}
else{
if(root.right!=null&&root.right.key==key)
return root;
else
return getParent(key,root.right);
}
}
public void traversePreOrder(Node node){
if(node==null|| count>100){
return;
}
visit1(node);
traversePreOrder(node.left);
traversePreOrder(node.right);
}
public void visit1(Node node){
count++;
System.out.println(node.key+" pre"+ node.left +" and "+node.right+" count is "+count);
}
public int getLevels(){
int levelCount=0;
if(root==null)
return levelCount;
else{
levelCount++;
return getLevels2(levelCount,root);
}
}
public int getLevels2(int levelCount, Node node){
if(node==null||node.isLeaf())
return levelCount;
else{
levelCount++;
return Math.max(getLevels2(levelCount,node.left), getLevels2(levelCount,node.right));
}
}
}