Package trees

Source Code of trees.BinarySearchTree

/* ************************
* Name : Eugene Krapivin *
* ID   : 306255084       *
* ***********************/

package trees;

import exceptions.*;

import java.io.Serializable;
import java.util.Stack;

import nodes.Node;

/**
* Data structure implements a Binary search tree.
* <p>
* In a binary search tree, every sub tree is a binary search tree as well. Left
* son of the root is smaller then the value of the root, the right side is
* larger then the root.
* <p>
* Class has some basic IO function to the structure. For more information see
* {@code http://en.wikipedia.org/wiki/Binary_search_tree}
* <p>
* Since it is a search tree, having duplicate values seem to be wrong because
* only one of the values will be reached, hence I do not allow duplicate values
* in the tree.
*
* @see Node
* @version 1.1 12/3/2012
* @author Eugene Krapivin
*/
public class BinarySearchTree implements Serializable
{
  private static final long serialVersionUID = -4148557239778403932L;
  protected Node root;

  /* Class constructors */

  /**
   * Class constructor.
   * <p>
   * This is the default constructor for the class {@code BinarySearchTree}.
   * Since the data in the nodes is immutable for obvious reasons, the node
   * will be automatically initialized to 0.
   *
   * @see Node
   * @deprecated
   */
  public BinarySearchTree()
  {
    root = new Node();
  }

  /**
   * Class constructor.
   * <p>
   * This constructor receives the first value of the root (in a B search tree
   * the root must always have a value) The sons of the first node a NULL and
   * the data of the first node (root) is set to the data value.
   *
   * @see Node
   * @param data
   *            the data to set in the root node
   */
  public BinarySearchTree(int data)
  {
    root = new Node(data);
  }

  /**
   * Class constructor.
   * <p>
   * This is a copy constructor for the BinarySearchTree structure. It will
   * create a distinct duplicate from the original.
   *
   * @param other
   *            the other binary search tree to copy from
   * @throws NotBinarySearchTree
   */
  public BinarySearchTree(BinarySearchTree other) throws NotBinarySearchTree
  {
    if (checkIfBST(other.getRoot()) == true)
    {
      root = other.root.clone(); // using the cloning method of class node
    }
    else
    {
      throw new NotBinarySearchTree(
          "Tree can not be set, the argument is not a legal binary search tree");
    }
  }

  /**
   * Class constructor.
   * <p>
   * This is a "soft" copy constructor. It receives a {@code Node} to which
   * the first node of the new tree will be initialized. The references of the
   * {@code Node} will be copied as well.
   *
   * @param root
   *            the Node to which the constructor will initialize the new
   *            tree.
   * @throws NotBinarySearchTree
   */
  public BinarySearchTree(Node root) throws NotBinarySearchTree
  {
    if (checkIfBST(root) == true)
    {
      setRoot(root);
    }
    else
    {
      throw new NotBinarySearchTree();
    }
  }

  /* getter/setter */

  /**
   * getter method for root field.
   *
   * @return root the root of the tree, first node.
   */
  public Node getRoot()
  {
    return root;
  }

  /**
   * Setter method for field root.
   *
   * @param root
   *            a Node to which I will initialize the root
   */
  public void setRoot(Node root)
  {
    this.root = root;
  }

  /* helper methods */

  /**
   * The methods checks whether a Node is a binary search tree
   * <p>
   * for more information about the rules of a binary search tree {code http
   * ://en.wikipedia.org/wiki/Binary_search_tree}
   *
   * @param root
   *            the node which will be checked
   *
   * @return true if the tree is in correspondence to the rules of a BST
   *         otherwise false
   */
  private boolean checkIfBST(Node root)
  {
    if (root == null)
    {
      return true;
    }
    // return false if the left branch is bigger then root value
    if (root.getLeft() != null)
    {
      if (root.getData().compareTo(root.getLeft().getData()) <= 0)
      {
        return false;
      }
    }
    // return false if the right branch is smaller then root value
    if (root.getRight() != null)
    {
      if (root.getData().compareTo(root.getRight().getData()) == 1)
      {
        return false;
      }
    }
    // if neither of the previous checks didn't yield false, check the sons
    return true && checkIfBST(root.getLeft())
        && checkIfBST(root.getRight());
  }

  /**
   * Find minimal value of the tree or sub-tree.
   * <p>
   * By the structure of binary search trees the smallest value will be the
   * left most value of the tree. the method is private and made for inner use
   * of method remove.
   * <p>
   * The method you uses recursion to traverse the tree.
   *
   * @see #remove(Node root, Comparable value)
   * @param root
   *            the root in which the method checks at the moment
   * @return the minimal value of the sub-tree
   */
  private Node minValue(Node root)
  {
    if (root.getLeft() == null)
    {
      return root;
    }
    else
    {
      return minValue(root.getLeft());
    }
  }

  /**
   * The method removes the left most node of the tree, used by method remove
   *
   * @see #remove(Comparable)
   * @param root
   *            the node in which the method checks at the moment
   */
  private void removeMin(Node root)
  {
    Node current = root;

    if (current.getRight().getLeft() == null)
    {
      current.setRight(current.getRight().getRight());
    }
    else
    {
      current = current.getRight();

      do
      {
        if (current.getLeft().getLeft() != null)
        {
          current = current.getLeft();
        }
        else
        {
          current.setLeft(current.getLeft().getLeft());
          break;
        }
      }
      while (current.getLeft() != null);
    }
  }

  /**
   * The method find the ancestor of the node it receives.
   * <p>
   * Throws exception {@link ItemNotFoundException} if the Node is not found.
   *
   * @param root
   *            the root for which the ancestor will be searched
   * @return returns null if the input node is the root. If not found throws
   *         exception, else returns reference to the ancestor
   * @throws ItemNotFoundException
   */
  private Node findPredecessor(Node root) throws ItemNotFoundException
  {
    if (this.root.getData() == root.getData())
    {
      return null;
    }

    Node current = this.root;

    while (current != null)
    {
      if (current.getLeft() != null)
      {
        if (current.getLeft() == root)
        {
          return current;
        }
      }
      if (current.getRight() == root)
      {
        return current;
      }
      if (root.getData().compareTo(current.getData()) <= 0)
      {
        current = current.getLeft();
      }
      else
      {
        current = current.getRight();
      }
    }

    throw new ItemNotFoundException("The item doesn't exists in the tree.");
  }

  /**
   * Remove method.
   * <p>
   * The method uses recursion to traverse the tree, find the node to delete
   * it.
   * <p>
   * If the item is not found a {@link ItemNotFoundException} will be thrown.
   * The method has a "wrapper" for the user use.
   *
   * @param root
   *            the current root to search in
   * @param value
   *            the value to delete from the tree
   * @throws ItemNotFoundException
   */
  private void remove(Node root, Comparable value)
      throws ItemNotFoundException
  {
    if (root != null)
    {
      // looking for the value to delete
      if (root.getData().compareTo(value) > 0)
      {
        remove(root.getLeft(), value);
      }
      else if (root.getData().compareTo(value) < 0)
      {
        remove(root.getRight(), value);
      }
      // value found - check if has 2 branches
      else if (root.getRight() != null && root.getLeft() != null)
      {
        Node min = minValue(root.getRight());

        removeMin(root);

        min.setLeft(root.getLeft());
        min.setRight(root.getRight());

        Node ancesstor = findPredecessor(root);
        if (ancesstor == null)
        {
          this.root = min;
        }
        else if (root == ancesstor.getLeft())
        {
          ancesstor.setLeft(min);
        }
        else
        {
          ancesstor.setRight(min);
        }
      }
      else
      // if has 1 branch or less
      {
        // find the father of the node to delete
        Node ancesstor = findPredecessor(root);
        // if the value to delete is in the root
        if (ancesstor == null)
        {
          if (root.getRight() != null || root.getLeft() != null)
          {
            this.setRoot(root.getLeft() == null ? root.getRight()
                : root.getLeft());
          }
          else
          {
            throw new ItemNotFoundException(
                "Can not remove the only node in the tree. "
                    + "A binary search tree must have at least 1 value.");
          }
        } // if not root check if the father's left is not null
        else if (ancesstor.getLeft() != null)
        { // if not null, check if to be deleted
          if (ancesstor.getLeft().getData().compareTo(value) == 0)
          { // if to be deleted replace the target by the son
            ancesstor.setLeft(root.getRight() == null ? root
                .getLeft() : root.getRight());
          }
          else
          {
            ancesstor.setRight(root.getRight() == null ? root
                .getLeft() : root.getRight());
          }
        }
        else
        {
          if (ancesstor.getRight().getData().compareTo(value) == 0)
          { // if to be deleted replace the target by the son
            ancesstor.setRight(root.getRight() == null ? root
                .getLeft() : root.getRight());
          }
          else
          {
            ancesstor.setLeft(root.getRight() == null ? root
                .getLeft() : root.getRight());
          }
        }
      }
    }
    else
    {
      throw new ItemNotFoundException(value.toString()
          + " not found in the tree.");
    }
  }

  /**
   * In-order traversal of the tree, using recursion algorithm.
   * <p>
   * The method has "wrapper" for the user use
   *
   * @param root
   *            the current root to work on
   */
  private void inorderRecursive(Node root)
  {
    if (root != null)
    {
      inorderRecursive(root.getLeft());
      System.out.print(root.getData() + " ");
      inorderRecursive(root.getRight());
    }
  }

  private Node search(Node root, Comparable value)
  {
    if (root == null || root.getData().compareTo(value) == 0)
    {
      return root;
    }
    else if (root.getData().compareTo(value) == 1)
    {
      return search(root.getLeft(), value);
    }
    else
    {
      return search(root.getRight(), value);
    }
  }

  /**
   * Insertion method.
   * <p>
   *
   * This method inserts a value to the current tree, keeping the laws of the
   * binary search tree structure. For more information on binary search trees
   * {code http://en.wikipedia.org/wiki/Binary_search_tree}.
   * <p>
   * The method does not allow insertion of duplicate values, and will throw a
   * DuplicateItemException if one is added. The method uses recursion to
   * traverse the tree until it finds the place to put the new item in.
   * <p>
   * This method is private, and has a "wrapper" for the user to use.
   *
   * @param root
   *            the root which is checked for insertion
   * @param value
   *            the value to insert to the tree
   * @throws DuplicateItemException
   */
  protected void insert(Node root, Node value)
  {
    if (root.compareTo(value) >= 0)
    {
      if (root.getLeft() == null) // if the son to the left is null
      {
        root.setLeft(value); // create new one with the value
      }
      else
      {
        insert(root.getLeft(), value); // otherwise call insert to left
      }
    }
    else if (root.compareTo(value) <= -1)
    {
      if (root.getRight() == null) // if the son to the left is null

        root.setRight(value); // create new one with value
      else
        insert(root.getRight(), value); // call insert to right
    }
    else
    {
      throw new DuplicateItemException(value.getData().toString());
    }
  }

  /* Public methods */

  /**
   * This method is a wrapper for the inner insert method.
   *
   * @see #insert(Node, Node)
   * @param value
   *            the value to insert to the tree.
   * @throws DuplicateItemException
   */
  public void insert(Comparable value) throws DuplicateItemException
  {
    insert(root, new Node(value));
  }

  /**
   * this method is a wrapper for the remove method.
   *
   * @see #remove(Node, Comparable)
   * @param value
   *            the value to remove from the tree
   * @throws ItemNotFoundException
   */
  public void remove(Comparable value) throws ItemNotFoundException
  {
    remove(root, value);
  }

  /**
   * this is a same method that runs the remove method in a try-catch block
   * and deals with errors.
   *
   * @param value
   *            the value to delete from the tree.
   */
  public void tryRemove(Comparable value)
  {
    try
    {
      remove(this.root, value);
    }
    catch (ItemNotFoundException ex)
    {
      System.out.println(ex.getMessage());
    }
    catch (NullPointerException ex)
    {
      System.out.println(ex.getStackTrace().toString());
    }
    catch (Exception e)
    {
      System.out.println(e.getStackTrace().toString());
    }
  }

  /**
   * This is the wrapper method for the search method.
   *
   * @see #search(Node, Comparable)
   * @param value
   * @return true if value is found, else false.
   */
  public boolean search(Comparable value)
  {
    return search(root, value) == null ? false : true;
  }

  /**
   * this is a wrapper method for the recursive method inorderSearch
   *
   * @see #inorderRecursive(Node)
   */
  public void inorderRecursive()
  {
    inorderRecursive(root);
  }

  /**
   * This is an iterative version to the in-order traversal.
   */
  public void inorderIterative()
  {
    Node current = this.root;
    Stack<Node> stack = new Stack<Node>();

    while (stack.isEmpty() == false || current != null)
    {
      if (current != null)
      {
        stack.push(current);
        current = current.getLeft();
      }
      else
      {
        current = stack.pop();
        System.out.print(current.getData() + " ");
        current = current.getRight();
      }
    }
  }

  /**
   * toString method
   * <p>
   * Prints the whole tree by printing the nodes in a in-order manner
   */
  public String toString()
  {
    Node current = this.root;
    Stack<Node> stack = new Stack<Node>();
    String resultString = "";

    while (stack.isEmpty() == false || current != null)
    {
      if (current != null)
      {
        stack.push(current);
        current = current.getLeft();
      }
      else
      {
        current = stack.pop();
        resultString += current.toString();
        current = current.getRight();
      }
    }

    return resultString;
  }

  /**
   * Equality method
   * <p>
   * Method checks if 2 trees are equal, using the toString methods of each of
   * the trees
   *
   * @param other
   * @return true if the trees are equal, false otherwise
   */
  public boolean equals(BinarySearchTree other)
  {
    return this.toString().equals(other.toString());
  }
}
TOP

Related Classes of trees.BinarySearchTree

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.