// CSE 143, Winter 2012 // Each TreeSet object represents a Set storing a collection of unique objects as // its elements. This class is based on the SearchTree we wrote in previous lectures. // A Set does not allow duplicates to be added. // // The key differences between SearchTree and TreeSet are: // * TreeSet implements a Set interface representing the Set abstract data type (ADT). // * TreeSet places its TreeNode as an inner class rather than as a separate file. // * TreeSet uses a generic type parameter, , to store any kind of data (not just ints). // // class invariant: nodes are always kept in proper BST (left-to-right) sorted order. import java.util.NoSuchElementException; public class TreeSet> implements Set { private TreeNode overallRoot; private int size; // Constructs an empty set. public TreeSet() { overallRoot = null; size = 0; } // Adds the given value to this set, if it was not already in the set. // If the element is already a member of this set, adding it again has no effect. public void add(E value) { overallRoot = add(overallRoot, value); } // private recursive helper for add // uses the "x = change(x)" pattern: // pass in current state of root // return out desired new state of root private TreeNode add(TreeNode root, E value) { if (root == null) { root = new TreeNode(value); // reached dead end; put new node here size++; } else if (value.compareTo(root.data) > 0) { root.right = add(root.right, value); } else if (value.compareTo(root.data) < 0) { root.left = add(root.left, value); } // else a duplicate; do nothing return root; } // Returns true if this set contains the given element value. public boolean contains(E value) { return contains(overallRoot, value); } // private recursive helper for contains private boolean contains(TreeNode root, E value) { if (root == null) { return false; } else if (value.compareTo(root.data) > 0) { return contains(root.right, value); } else if (value.compareTo(root.data) < 0) { return contains(root.left, value); } else { // value.equals(root.data); found it! return true; } } // Returns the minimum value in this set. // Throws a NoSuchElementException if this set is empty. public E getMin() { if (overallRoot == null) { throw new NoSuchElementException("empty tree"); } return getMin(overallRoot); } // private recursive helper for getMin private E getMin(TreeNode root) { if (root.left == null) { return root.data; } else { return getMin(root.left); } } // Returns true if this set does not contain any elements. public boolean isEmpty() { return size == 0; } // Prints all elements of this tree in a left-to-right order (in-order) traversal. public void print() { print(overallRoot); } // private recursive helper for print private void print(TreeNode root) { if (root != null) { print(root.left); System.out.print(root.data + " "); print(root.right); } // implicit base case: else do nothing } // Removes the given value from this set, if it is found in this set. // If the element is not found, calling this method has no effect. public void remove(E value) { overallRoot = remove(overallRoot, value); } // private recursive helper for remove private TreeNode remove(TreeNode root, E value) { if (root == null) { // nothing here; do nothing? } else if (value.compareTo(root.data) < 0) { root.left = remove(root.left, value); } else if (value.compareTo(root.data) > 0) { root.right = remove(root.right, value); } else { // (value == root.data) // found it! remove now. size--; if (root.left == null && root.right == null) { // leaf root = null; } else if (root.right == null) { // only left child root = root.left; } else if (root.left == null) { // only right child root = root.right; } else { // hard case: two children; replace me with min from my right E rightMin = getMin(root.right); root.right = remove(root.right, rightMin); root.data = rightMin; } } return root; } // Returns the number of elements in this set. public int size() { return size; } // Each TreeNode object stores a single node of a binary tree. // The class has just public data fields and constructors. // This version of our code places the node as an inner class. private class TreeNode { public E data; // data stored at this node public TreeNode left; // reference to left subtree public TreeNode right; // reference to right subtree // Constructs a leaf node with the given data. public TreeNode(E data) { this(data, null, null); } // Constructs a leaf or branch node with the given data and links. public TreeNode(E data, TreeNode left, TreeNode right) { this.data = data; this.left = left; this.right = right; } } }