/* Binary Search Tree Applet * * By James Stewart * Dept of Computer Science * University of Toronto * jstewart@cs.toronto.edu * * Feel free to copy and modify this applet, but please give credit * to the original author where appropriate. * * Draws a BST and allows the user to perform searches and rotations. The * applet parameters define the functionality, as follows: * * General parameters: * * keys list of keys to be inserted into the bst, in order of insertion * * action list of permissible actions, which include "rotate" and "search" * (If no action is supplied, the user cannot interact with the bst.) * * Parameters for rotations: * * only_root_children if present, only the children of the root may be rotated * * alternate_nodes a list of two keys, indicating that only two nodes may be * rotated, and they must be rotated alternately * * rotatable_node a list of keys, indicating that only these nodes may be * rotated. * * For examples of using this applet, see the *.html files in the * directory above this one. */ import java.util.*; import java.awt.*; import java.applet.Applet; /** A BST node */ class node { int key; // element key node left; // left child node right; // right child node parent; // parent float x, y; // (x,y) location in window float incx, incy; // increment in (x,y) with each move boolean highlight_node; // true if node is to be highlighted Color highlight_colour; boolean hide_edge; // true if parent edge is to be hidden (i.e. not drawn) } /** A Binary Search Tree */ public class bst extends java.applet.Applet { node root; // root of tree node rotation_node; // node to rotate about int num_edges; // edges to draw float edges[][] = new float[100][4]; rotate rotate_thread; // thread to handle rotations search search_thread; // thread to handle searches boolean only_root_children; // true if only the root's children may be rotated boolean alternate_nodes; // true if we alternately rotate two nodes only boolean allow_rotation; // true if we can do rotations boolean allow_search; // true if we can do searches final int MAX_ROTATABLE_NODES = 100; int rotatable_nodes[] = new int[MAX_ROTATABLE_NODES]; // nodes that we may rotate int num_rotatable_nodes = 0; // (num_rotable_nodes = 0 means all nodes may be rotated) TextField search_field; // UI component to capture user's search request int search_key; // the key to search for // Things having to do with appearance: Font f = new Font( "TimesRoman", Font.PLAIN, 14 ); FontMetrics fm = getFontMetrics( f ); final Color LineColor = Color.black; final Color HighlightColor = Color.orange; final Color NodeColor = Color.cyan; final int NODE_WIDTH = 40; // width of a node final int NODE_HEIGHT = 30; // height of a node /** * Init the applet. Just insert elements into the tree and * compute the tree's initial position. */ public void init() { // Get the applet parameters parse_parameters(); // Determine initial tree position position_tree( root, 0, 0 ); // Set up UI components if (allow_search) { setLayout( new FlowLayout(FlowLayout.LEFT) ); search_field = new TextField( 4 ); search_field.setEditable( true ); add( new Label( "Enter search key:", Label.RIGHT ) ); add( search_field ); } } /** * Upon starting, start any threads that were previously stopped */ public void start() { if (rotate_thread != null && rotate_thread.isAlive()) rotate_thread.resume(); if (search_thread != null && search_thread.isAlive()) search_thread.resume(); } /** * Upon stopping, stop all active threads */ public void stop() { if (rotate_thread != null && rotate_thread.isAlive()) rotate_thread.suspend(); if (search_thread != null && search_thread.isAlive()) search_thread.suspend(); } /** * Upon a `paint', just redraw the tree. */ public void paint( Graphics g ) { // Draw any stored edges if (num_edges > 0) { g.setColor( LineColor ); for (int i=0; i Math.abs(dy) ? Math.abs(dx) : Math.abs(dy)) / 2; int sleep_time = (int) (ROTATE_TIME/3/loops); if (sleep_time < 5) sleep_time = 5; dx = dx/loops; dy = dy/loops; tree.repaint(); for (int i=0; i < loops; i++) { tree.edges[0][2] += dx; tree.edges[0][3] += dy; tree.repaint(); try // delay 1/3 of ROTATE_TIME for ALL of Phase II Thread.sleep( sleep_time ); catch (InterruptedException e) {} } gc.highlight_node = false; } // Phase III: swap the upper edge if (gp != null) { tree.num_edges ++; gp.highlight_node = true; p.hide_edge = true; tree.edges[tree.num_edges-1][0] = gp.x; tree.edges[tree.num_edges-1][1] = gp.y; tree.edges[tree.num_edges-1][2] = p.x; tree.edges[tree.num_edges-1][3] = p.y - tree.NODE_HEIGHT/2; float dx = c.x - tree.edges[tree.num_edges-1][2]; float dy = c.y - tree.edges[tree.num_edges-1][3]; float loops = (Math.abs(dx) > Math.abs(dy) ? Math.abs(dx) : Math.abs(dy)) / 2; int sleep_time = (int) (ROTATE_TIME/3/loops); if (sleep_time < 5) sleep_time = 5; dx = dx/loops; dy = dy/loops; tree.repaint(); for (int i=0; i < loops; i++) { tree.edges[tree.num_edges-1][2] += dx; tree.edges[tree.num_edges-1][3] += dy; tree.repaint(); try // delay 1/3 of ROTATE_TIME for ALL of Phase II Thread.sleep( sleep_time ); catch (InterruptedException e) {} } p.hide_edge = false; gp.highlight_node = false; } if (gc != null) gc.hide_edge = false; tree.num_edges = 0; tree.rotate( c ); tree.repaint(); } void shift_up( node n ) { if (n == null) return; shift_up( n.left ); shift_up( n.right ); n.y --; } void shift_down( node n ) { if (n == null) return; shift_down( n.left ); shift_down( n.right ); n.y ++; } } /* A Thread to animate BST search */ class search extends Thread implements Runnable { final int SEARCH_TIME = 800; // search time per node, in milliseconds bst tree; // tree in which search occurs search( bst t ) { tree = t; } public void run() { node n, pn; // Clear old highlights if (tree.root != null && tree.root.highlight_node) { clear_highlights( tree.root ); tree.repaint(); try Thread.sleep( SEARCH_TIME ); catch (InterruptedException e) {} } // Search pn = null; n = tree.root; while (n != null && n.key != tree.search_key) { // Pause on the node n.highlight_node = true; tree.repaint(); try Thread.sleep( SEARCH_TIME ); catch (InterruptedException e) {} // Move downward pn = n; if (tree.search_key < n.key) n = n.left; else n = n.right; } if (n != null) { n.highlight_node = true; tree.repaint(); try Thread.sleep( SEARCH_TIME ); catch (InterruptedException e) {} n.highlight_colour = Color.green; } else if (pn != null) { pn.highlight_node = true; pn.highlight_colour = Color.red; } tree.repaint(); } void clear_highlights( node n ) { if (n == null) return; clear_highlights( n.left ); clear_highlights( n.right ); n.highlight_node = false; n.highlight_colour = null; } }