// Helene Martin, CSE 143 // An IntSearchTree object represents an entire binary search tree of ints. // Class invariants: // - nodes to the left of a root have smaller values // - nodes to the right of a root have larger values // - there are no duplicates import java.util.NoSuchElementException; public class IntSearchTree { private IntTreeNode overallRoot; // Constructs an empty binary search tree. public IntSearchTree() { overallRoot = null; } // Removes all nodes without children (leaves) in this tree. public void removeLeaves() { overallRoot = removeLeaves(overallRoot); } // Removes all leaf nodes in the tree with the given root. private IntTreeNode removeLeaves(IntTreeNode root) { if (root != null) { if (root.left == null && root.right == null) { root = null; } else { root.left = removeLeaves(root.left); root.right = removeLeaves(root.right); } } return root; } // Returns the minimum value from this BST. // Throws a NoSuchElementException if the tree is empty. public int getMin() { if (overallRoot == null) { throw new NoSuchElementException(); } return getMin(overallRoot); } // Returns the minimum value from the BST with the given root. // Pre: root is not null private int getMin(IntTreeNode root) { if (root.left == null) { return root.data; } else { return getMin(root.left); } } // Removes the node with the given value from this BST. public void remove(int value) { overallRoot = remove(overallRoot, value); } private IntTreeNode remove(IntTreeNode root, int value) { if (root != null) { if (value < root.data) { root.left = remove(root.left, value); } else if (value > root.data) { root.right = remove(root.right, value); } else { // found it! // leaf node case is same as removeLeaves // (not necessary to have as a separate case!) if (root.left == null && root.right == null) { root = null; } else if (root.right == null) { // left child only root = root.left; } else if (root.left == null) { // right child only; root = root.right; } else { int newRoot = getMin(root.right); remove(newRoot); root.data = newRoot; } } } return root; } // Returns the number of nodes in this tree. public int size() { return size(overallRoot); } // Returns the number of nodes in the subtree with root as its root node. private int size(IntTreeNode root) { if (root == null) { return 0; } else { return 1 + size(root.left) + size(root.right); } } // Returns true if the value is in this tree, false otherwise. public boolean contains(int value) { return contains(overallRoot, value); } // Returns true if the value is in the tree starting at the // specified root. private boolean contains(IntTreeNode root, int value) { if (root == null) { return false; } else if (root.data == value) { return true; } else if (value < root.data) { return contains(root.left, value); } else { return contains(root.right, value); } } // Adds the given value to this BST. // Post: the tree is still a valid BST public void add(int value) { overallRoot = add(overallRoot, value); } // Adds the given value to the BST starting at the given root. // Post: the tree is still a valid BST private IntTreeNode add(IntTreeNode root, int value) { if (root == null) { root = new IntTreeNode(value); } else if (value < root.data) { root.left = add(root.left, value); } else if (value > root.data) { root.right = add(root.right, value); } return root; } // Prints a pre-order traversal of this tree. public void printPreorder() { printPreorder(overallRoot); } // Prints a pre-order traversal of the tree starting at the specified root private void printPreorder(IntTreeNode root) { // implicit base case: do nothing when we reach a null root if (root != null) { System.out.print(root.data + " "); printPreorder(root.left); printPreorder(root.right); } } // Prints an in-order traversal of this tree. public void printInorder() { printInorder(overallRoot); } // Prints an in-order traversal of the tree starting at the specified root private void printInorder(IntTreeNode root) { if (root != null) { printInorder(root.left); System.out.print(root.data + " "); printInorder(root.right); } } // Prints a post-order traversal of this tree. public void printPostorder() { printPostorder(overallRoot); } // Prints a post-order traversal of the tree starting at the specified root private void printPostorder(IntTreeNode root) { if (root != null) { printPostorder(root.left); printPostorder(root.right); System.out.print(root.data + " "); } } // Prints the tree contents, one per line, following an inorder // traversal and using indentation to indicate node depth; prints // right to left so that it looks correct when the output is rotated. public void printSideways() { printSideways(overallRoot, 0); } // Prints in reversed preorder the tree with given root, indenting // each line to the given level private void printSideways(IntTreeNode root, int level) { if (root != null) { printSideways(root.right, level + 1); for (int i = 0; i < level; i++) { System.out.print(" "); } System.out.println(root.data); printSideways(root.left, level + 1); } } }