// CSE 143, Winter 2010, Marty Stepp // A SearchTree object represents a set of integer values implemented as a // binary search tree. The tree adds elements in order such that nodes on // the left have smaller values and nodes on the right have larger ones. // // Today's version adds getMin and remove methods. import java.util.*; // for NoSuchElementException public class SearchTree { private IntTreeNode overallRoot; // Constructs an empty binary tree (null root). public SearchTree() { overallRoot = null; } // Prints all elements of this tree in left-to-right order. public void print() { print(overallRoot); System.out.println(); } // Private helper prints the given part of the tree. private void print(IntTreeNode node) { if (node != null) { // print left, then myself, then right print(node.left); System.out.print(node.data + " "); print(node.right); } } // Prints the tree in a sideways indented format. // Elements are printed one per line, indented by 4 spaces. public void printSideways() { printSideways(overallRoot, 0); } // Recursive helper for printing the given subtree sideways at the // given level of indentation. private void printSideways(IntTreeNode root, int level) { if (root == null) { } else { // root != null printSideways(root.right, level + 1); for (int i = 1; i <= level * 4; i++) { System.out.print(" "); } System.out.println(root.data); printSideways(root.left, level + 1); } } // Returns true if the given value is found in this tree, else false. // Precondition: The elements of the tree are in sorted BST order, // so that smaller elements are to the left and larger ones to the right. public boolean contains(int value) { return contains(overallRoot, value); } // Helper searches the given part of the tree for the given value. private boolean contains(IntTreeNode node, int value) { if (node == null) { // base case; fell off the tree, not found return false; } else if (value == node.data) { // base case; found the node return true; } else if (value > node.data) { // the value we are searching for is bigger, so go right return contains(node.right, value); } else { // the value we are searching for is smaller, so go left return contains(node.left, value); } } // Adds the given value to this tree at an appropriate place to retain // sorted BST ordering. // If the value is already found in the tree, it is not added. public void add(int value) { overallRoot = add(overallRoot, value); } // Private helper adds the value to the given part of the tree. // This method uses the "x = change(x);" pattern, where the node's // initial state is passed in, and its new state is returned out. // This helps us attach newly created nodes to the tree. private IntTreeNode add(IntTreeNode node, int value) { if (node == null) { // we have hit the bottom of the tree; // this is the place to add the node node = new IntTreeNode(value); } else if (node.data != value) { if (value > node.data) { // so go right // x = change(x) node.right = add(node.right, value); } else { // value < node.data, so go left // x = change(x) node.left = add(node.left, value); } } // return the node's new state return node; } // Returns the smallest value currently stored in the tree. // Throws a NoSuchElementException if the tree is empty. public int getMin() { if (overallRoot == null) { throw new NoSuchElementException(); } else { return getMin(overallRoot); } } // Helper to return the smallest value in the given part of the tree. private int getMin(IntTreeNode node) { if (node.left == null) { // no left child, so this must be the leftmost (min) node return node.data; } else { // go to the left return getMin(node.left); } } // Returns the largest value currently stored in the tree. // Throws a NoSuchElementException if the tree is empty. public int getMax() { if (overallRoot == null) { throw new NoSuchElementException(); } else { return getMax(overallRoot); } } private int getMax(IntTreeNode node) { if (node.right == null) { // no right child, so this must be the leftmost (min) node return node.data; } else { // go to the right return getMax(node.right); } } // Removes the given value, if it is contained in the tree. // If the value is not contained in the tree, has no effect. public void remove(int value) { // x = change(x); overallRoot = remove(overallRoot, value); } // Private method to remove the value from the given part of the tree. // Accepts current node state and returns new node state. private IntTreeNode remove(IntTreeNode node, int value) { if (node == null) { // base case: not found return null; } else if (value < node.data) { // go to the left node.left = remove(node.left, value); } else if (value > node.data) { // go to the right node.right = remove(node.right, value); } else { // node.data == value // this is the node I'm looking to remove if (node.left == null && node.right == null) { // 1. leaf return null; } else if (node.right == null) { // 2. has a left child only; replace with left child return node.left; } else if (node.left == null) { // 3. has a right child only; replace with right child return node.right; } else { // 4. two children; replace with min value from right subtree int replacement = getMin(node.right); node.data = replacement; node.right = remove(node.right, replacement); } } return node; } }