/*
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
.
*/
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)
*
* @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, Elem> implements
DictionaryADT
{
/* The root of this tree */
private BinNodeADT root;
/* 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;
/**
* Creates a new BST.
*
* @param p
*/
public BST(TreePanel p) {
this();
treePanel = p;
}
/**
* Creates a new BST with a null root.
*/
public BST() {
root = null;
nodeCount = 0;
stateList = new ArrayList();
levels = 0;
}
/**
* Clears the BST (not implemented)
*/
public void clear() {
// TODO Auto-generated method stub
}
/**
* Find method.
*
* @param k
* The key you're looking for.
* @return The element associated with this key.
*/
public Elem find(Key k) {
stateList.clear();
return findHelper(root, k);
}
/**
* 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 Element corresponding to the key.
*/
private Elem findHelper(BinNodeADT node, Key k) {
if (node == null) {
return null;
}
Elem it = node.val();
if (k.compareTo(node.key()) < 0) {
State s = new State((GraphicalNode) node.val(),
State.OperationType.INSERT, "Went left of " + node.key()
+ "\n");
stateList.add(s);
System.out.println("find left of " + node.key());
return findHelper(node.left(), k);
} else if (node.key().equals(k)) {
State s = new State((GraphicalNode) it, State.OperationType.FIND,
"Found node successfully\n");
stateList.add(s);
System.out.println("find right of " + node.key());
return it;
} else {
State s = new State((GraphicalNode) node.val(),
State.OperationType.FIND, "Went right of " + node.key()
+ "\n");
stateList.add(s);
return findHelper(node.right(), k);
}
}
/**
* Inserts a new node in the Binary Search Tree with the specified Key and
* Element.
*
* @param k
* The key to put into the new node.
* @param e
* The element to put into the new node.
*/
public void insert(Key k, Elem e) {
stateList.clear();
int tmpLevels = levels;
levels = 0;
root = insertHelper(root, k, e);
if (levels < tmpLevels) {
levels = tmpLevels;
}
treePanel.setLevels(levels);
nodeCount++;
}
/**
* 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 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 BinNodeADT insertHelper(BinNodeADT node, Key k,
Elem it) {
if (node == null) {
// feedback.append("Node added successfully\n");
State s = new State((GraphicalNode) it, State.OperationType.INSERT,
"Inserted " + k.toString() + " successfully\n");
stateList.add(s);
levels++;
return new BinNode(k, it, null, null);
}
if (k.compareTo(node.key()) < 0) {
// feedback.append("Went left of " + root.key() + "\n");
State s = new State((GraphicalNode) node.val(),
State.OperationType.INSERT, "Went left of " + node.key()
+ "\n");
stateList.add(s);
levels++;
node.setLeft(insertHelper(node.left(), k, it));
} else {
// feedback.append("Went right of " + node.key() + "\n");
State s = new State((GraphicalNode) node.val(),
State.OperationType.INSERT, "Went right of " + node.key()
+ "\n");
stateList.add(s);
levels++;
node.setRight(insertHelper(node.right(), k, it));
}
return node;
}
/**
* Helper method for printing a BST.
*
* @param root
* The node to recursively print.
* @param w
* The writer to print to.
*/
private void printHelper(BinNodeADT root, Writer w) {
if (root == null) {
return;
}
printHelper(root.left(), w);
try {
w.write(root.val() + " ");
} catch (IOException e) {
e.printStackTrace();
}
printHelper(root.right(), w);
}
/**
* Removes the node with the specified key from the tree.
*/
public Elem remove(Key k) {
return removeHelper(root, k).val();
}
/**
* 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 BinNodeADT removeHelper(BinNodeADT node, Key k) {
if (node == null)
return null;
else if (k.compareTo(node.key()) < 0) {
node.setLeft(removeHelper(node.left(), k));
} else if (k.compareTo(node.key()) > 0) {
node.setRight(removeHelper(node.right(), k));
} else
// found it
{
if (node.left() == null) {
node = node.right();
} else if (root.right() == null) {
node = node.left();
} else {
BinNodeADT tmp = getMin(root.right());
node.setVal(tmp.val());
node.setRight(deleteMin(node.right()));
}
}
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 BinNodeADT getMin(BinNodeADT root) {
if (root.left() == null) {
return root;
} else {
return getMin(root.left());
}
}
/**
* Helper for the remove method.
*
* @param node
* The subtree to remove the minimum from.
* @return The node that was removed.
*/
private BinNodeADT deleteMin(BinNodeADT node) {
if (node.left() == null)
return node.right();
else {
node.setLeft(deleteMin(node.left()));
return node;
}
}
public Elem removeAny() {
// TODO Auto-generated method stub
return null;
}
/**
* 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.
*/
private void traversePostOrder(BinNodeADT 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
&& !((GraphicalNode) node.left().val()).isHidden()) {
g.setColor(Color.BLACK);
GraphicalNode n = (GraphicalNode)node.left().val();
GraphicalNode parent = (GraphicalNode)node.val();
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
&& !((GraphicalNode) node.right().val()).isHidden()) {
g.setColor(Color.BLACK);
GraphicalNode n = (GraphicalNode)node.right().val();
GraphicalNode parent = (GraphicalNode)node.val();
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(BinNodeADT node, int x, int y, Graphics g,
boolean recalcPos, boolean draw) {
// System.out.println("Evaluating node " + node.key());
GraphicalNode curr = (GraphicalNode) node.val();
if (recalcPos) {
System.out.println("Recalculating node positions");
curr.setXPos(treePanel.getPhysicalX(x, y));
curr.setYPos(y * TreePanel.NODE_VERTICAL_DISTANCE);
curr.setAbstractX(x);
curr.setAbstractY(y);
}
if (draw) {
System.out.println("BST Drawing: " + curr);
curr.draw(g);
}
}
}