/** * Grant D Hawkes * This file implements and AVL Tree including a modified version of lazy deletion. * Much of the code in this file is from Mark Alan Weiss' Data Structures and Algorithms * book and it's accompaning website. */ import java.util.LinkedList; import java.util.StringTokenizer; public class AVLTree { private class AVLNode { public int freq; public int height; public String name; public AVLNode right; public AVLNode left; public LinkedList nameList; public AVLNode(String xname, String xwholeName, AVLNode xleft, AVLNode xright) { name = xname; left = xleft; right = xright; height = -1;// this line is not needed, just an initialization nameList = new LinkedList(); nameList.add(xwholeName); freq = 1;// freq is one because 1 name was just added } } private AVLNode root; public AVLTree() { root = null; } /** * public method to print the tree and then the names of the children in the * tree in alphabetical order (by first name). */ public void printTeams() { printTree(root, ""); System.out.println(); printNames(root); } /** * Given the root of a tree and an initial buffer (indent) String this * method will print a AVL tree sideways to the console. Imagine a handdrawn * tree and flip it 90 degrees counter-clockwise. This is what this method * will print. Format is the name of the node then a colon then the * frequency of people with that first name, example: Grant:2 */ private void printTree(AVLNode curr, String buffer) { if (curr != null) { printTree(curr.right, buffer + " "); System.out.println(buffer + curr.name + ":" + curr.freq); printTree(curr.left, buffer + " "); } } /** * Prints the names of the children in the tree (1 per line) sorted by first * name. */ private void printNames(AVLNode curr) { if (curr != null) { printNames(curr.left); for (int i = 0; i < curr.freq; i++) System.out.println(curr.nameList.get(i)); printNames(curr.right); } } /** Does lazy deletion on the String wholeName in the AVL tree. */ public void remove(String wholeName) { StringTokenizer st = new StringTokenizer(wholeName); String name = st.nextToken();// pulls the first token (first name) // out of the wholeName AVLNode temp = find(name, root); if (temp != null) { if (temp.nameList.contains(wholeName)) { temp.nameList.remove(wholeName); temp.freq--; } } } /** Makes the tree empty by setting the root to null */ public void makeEmpty() { root = null; } /** * Finds the String wholeName in the tree and returns it. Returns null if * not found. */ public String find(String wholeName) { StringTokenizer st = new StringTokenizer(wholeName); String name = st.nextToken(); /* * pulls the first token (first name) out of the wholeName */ /* * temp is the node with the same FIRST name as the person we are * searching for */ AVLNode temp = find(name, root); if (temp != null) { if (temp.nameList.contains(wholeName)) return wholeName; else return null; } return null; } /** * Internal method to find an item in a subtree. * * @param x * is item to search for. * @param t * the node that roots the tree. * @return node containing the matched item. Code from Mark Alan Weiss' data * structures and alogorithms book. */ private AVLNode find(String name, AVLNode curr) { while (curr != null) if (name.compareTo(curr.name) < 0) curr = curr.left; else if (name.compareTo(curr.name) > 0) curr = curr.right; else return curr; return null; } public AVLNode insert(String wholeName) { StringTokenizer st = new StringTokenizer(wholeName); String name = st.nextToken(); root = insert(name, wholeName, root); return root; } /** * Internal method to insert into a subtree. * * @param x * the item to insert. * @param t * the node that roots the tree. * @return the new root. Code from Mark Alan Weiss' data structures and * alogorithms book. */ private AVLNode insert(String name, String wholeName, AVLNode curr) { if (curr == null)// the node is null curr = new AVLNode(name, wholeName, null, null); else if (name.compareTo(curr.name) < 0) { curr.left = insert(name, wholeName, curr.left); if (height(curr.left) - height(curr.right) == 2) // there is an imbalance // see what kind of imbalance it is if (name.compareTo(curr.left.name) < 0) curr = rotateWithLeftChild(curr); else curr = doubleWithLeftChild(curr); } else if (name.compareTo(curr.name) > 0) { curr.right = insert(name, wholeName, curr.right); if (height(curr.right) - height(curr.left) == 2) if (name.compareTo(curr.right.name) > 0) curr = rotateWithRightChild(curr); else curr = doubleWithRightChild(curr); } else /* * it is a duplicate, increase freq and insert name into linked list */ { curr.nameList.add(wholeName); curr.freq++; } // finally update the height as we go curr.height = Math.max(height(curr.left), height(curr.right)) + 1; return curr; } private int height(AVLNode curr) { return curr == null ? -1 : curr.height; } /** * Code from Mark Alan Weiss' data structures and alogorithms book. */ /** * Rotate binary tree node with left child. For AVL trees, this is a single * rotation for case 1. Update heights, then return new root. Code from Mark * Alan Weiss' data structures and alogorithms book. */ private AVLNode rotateWithLeftChild(AVLNode k2) { AVLNode k1 = k2.left; k2.left = k1.right; k1.right = k2; k2.height = Math.max(height(k2.left), height(k2.right)) + 1; k1.height = Math.max(height(k1.left), k2.height) + 1; return k1; } /** * Rotate binary tree node with right child. For AVL trees, this is a single * rotation for case 4. Update heights, then return new root. Code from Mark * Alan Weiss' data structures and alogorithms book. */ private AVLNode rotateWithRightChild(AVLNode k1) { AVLNode k2 = k1.right; k1.right = k2.left; k2.left = k1; k1.height = Math.max(height(k1.left), height(k1.right)) + 1; k2.height = Math.max(height(k2.right), k1.height) + 1; return k2; } /** * Double rotate binary tree node: first left child with its right child; * then node k3 with new left child. For AVL trees, this is a double * rotation for case 2. Update heights, then return new root. Code from Mark * Alan Weiss' data structures and alogorithms book. */ private AVLNode doubleWithLeftChild(AVLNode k3) { k3.left = rotateWithRightChild(k3.left); return rotateWithLeftChild(k3); } /** * Double rotate binary tree node: first right child with its left child; * then node k1 with new right child. For AVL trees, this is a double * rotation for case 3. Update heights, then return new root. Code from Mark * Alan Weiss' data structures and alogorithms book. */ private AVLNode doubleWithRightChild(AVLNode k1) { k1.right = rotateWithLeftChild(k1.right); return rotateWithRightChild(k1); } public static void main(String[] a) { AVLTree avl = new AVLTree(); System.out.println("Insert and Delete TEST1"); avl.insert("Chris Stuhr"); avl.insert("Bob Brenly"); avl.insert("Alan Keyes"); avl.printTeams(); // test1 System.out.println("/nInsert and Delete TEST2"); avl.insert("Xing Shen"); avl.insert("Grant Hawkes"); avl.printTeams(); // test2 avl.remove("Chris Stuhr"); // found avl.printTeams(); // test2 System.out.println("/nInsert and Delete TEST3"); avl.remove("Xing Shen"); // found avl.remove("Grant Hawkes"); // found avl.insert("Bob Dole"); avl.insert("Grant Davis"); avl.printTeams(); // test3 System.out.println("/nInsert and Delete TEST4"); avl.insert("Zing Ye"); avl.insert("Grant Hawkes"); avl.insert("Bill Clinton"); avl.remove("George W Bush"); // not found avl.printTeams(); // test4 System.out.println("/nInsert and Delete TEST5"); avl.insert("George W Bush"); avl.insert("Sanghu Gite"); avl.remove("George W Bush"); // found avl.printTeams(); // test5 System.out.println("/nFind TEST"); System.out.println(avl.find("Grant Hawkes")); // found System.out.println(avl.find("Grant Davis")); // found System.out.println(avl.find("Grant Smith")); // not found System.out.println(avl.find("Sanghu Gite")); // found System.out.println(avl.find("Munindra Das")); // not found System.out.println("/nMakeEmpty TEST"); avl.makeEmpty(); avl.printTeams(); } }