/** * A binary search tree implemented by means of a linked structure. * * @author Franck van Breugel */ public class LinkedBinarySearchTree { /** * A node of a binary search tree. */ private static class Node { private int element; private Node left; private Node right; /** * Initializes this node with the given element and * left and right children . * * @param element the element of this node. * @param left the left child of this node. * @param right the right child of this node. */ public Node(int element, Node left, Node right) { super(); this.setElement(element); this.setLeft(left); this.setRight(right); } /** * Returns the element of this node. * * @return the element of this node. */ public int getElement() { return this.element; } /** * Sets the element of this node to the given element. * * @param element the new element of this node. */ public void setElement(int element) { this.element = element; } /** * Returns the left child of this node. * * @return the left child of this node. */ public Node getLeft() { return this.left; } /** * Sets the left child this node to the given node. * * @param left the left child of this node. */ public void setLeft(Node left) { this.left = left; } /** * Returns the right child of this node. * * @return the right child of this node. */ public Node getRight() { return this.right; } /** * Sets the right child this node to the given node. * * @param right the right child of this node. */ public void setRight(Node right) { this.right = right; } } private Node root; private int size; // redundant (space versus time trade off) /** * Initializes this tree to be empty. */ public LinkedBinarySearchTree() { this.root = null; this.size = 0; } /** * Returns the root of this tree. * * @return the root of this tree. */ private Node getRoot() { return this.root; } /** * Sets the root of this tree to the given root. * * @param root the root of this tree. */ private void setRoot(Node root) { this.root = root; } /** * Returns the size of this tree. * * @return the size of this tree. */ private int getSize() { return this.size; } /** * Sets the size of this tree to the given size. * * @param size the size of this tree. */ private void setSize(int size) { this.size = size; } public int size() { return this.getSize(); } public boolean contains(int element) { return this.contains(element, this.getRoot()); } /** * Tests whether the subtree of this tree rooted at the given node * contains the given element. * * @param element an element. * @param node root of the subtree. * @return true if the subtree of this tree rooted at the given node * contains the given element, false otherwise. */ private boolean contains(int element, Node node) { boolean contains; if (node == null) { contains = false; } else if (node.element == element) { contains = true; } else if (node.element > element) { contains = this.contains(element, node.getLeft()); } else { contains = this.contains(element, node.getRight()); } return contains; } public boolean add(int element) { boolean success = this.add(element, this.getRoot()); if (success) { this.setSize(this.getSize() + 1); } return success; } /** * Attempts to add the given element to the subtree of this tree * rooted at the given node. The attempt is successful if the * subtree does not contain the given element yet. Returns whether * the attempt is successful. * * @param element an element. * @param node root of the subtree. * @return true if the attempt to add the given element is successful, * false otherwise. */ public boolean add(int element, Node node) { boolean success; if (node == null) { this.setRoot(new Node(element, null, null)); success = true; } else if (node.getElement() == element) { success = false; } else if (node.getElement() > element) { if (node.getLeft() == null) { node.setLeft(new Node(element, null, null)); success = true; } else { success = this.add(element, node.getLeft()); } } else { if (node.getRight() == null) { node.setRight(new Node(element, null, null)); success = true; } else { success = this.add(element, node.getRight()); } } return success; } public boolean remove(int element) { boolean success = this.remove(element, this.getRoot(), null); if (success) { this.setSize(this.getSize() - 1); } return success; } /** * Returns the minimal element in the subtree of this tree * rooted at the given node. * * @param node root of the subtree. * @pre. node != null * @return the minimal element in the subtree of this tree * rooted at the given node. */ private int minimumValue(Node node) { int minimum; if (node.getLeft() == null) { minimum = node.getElement(); } else { minimum = this.minimumValue(node.getLeft()); } return minimum; } /** * Attempts to remove the given element from the subtree of this tree * rooted at the given node with the given parent. * The attempt is successful if the subtree contains the given * element. Returns whether the attempt is successful. * * @param element an element. * @return true if the attempt to remove the given element is successful, * false otherwise. */ private boolean remove(int element, Node node, Node parent) { boolean success; if (node == null) { success = false; } else if (node.getElement() == element) { if (node == this.getRoot()) { this.setRoot(null); } else { if (node.getLeft() == null) { parent.setRight(node.getRight()); } else if (node.getRight() == null) { parent.setLeft(node.getLeft()); } else { int minimum = this.minimumValue(node); node.setElement(minimum); this.remove(minimum, node, parent); } } success = true; } else if (node.getElement() > element) { if (node.getLeft() == null) { success = false; } else { success = this.remove(element, node.getLeft(), node); } } else { if (node.getRight() == null) { success = false; } else { success = this.remove(element, node.getRight(), node); } } return success; } }