package net.datastructures; import java.util.Iterator; /** * An implementation of the BinaryTree interface by means of a linked structure. * This class serves as a superclass for the BinarySearchTree * implementation. This design decision was made to emphasize the * conceptual relationship that a BinarySearchTree is a specialized * LinkedBinaryTree. An unwanted side-effect of this is that the * {@link #size() size} method returns the number of total nodes * whereas the {@link BinarySearchTree#size() size} method in the * {@link BinarySearchTree BinarySearchTree} class returns the number * of internal nodes only. For this reason, the the {@link #size * size} variable instead of the {@link #size() size} method is used * within this class. * * @author Luca Vismara, Roberto Tamassia, Michael Goodrich, Eric * Zamore * @see BinaryTree */ public class LinkedBinaryTree implements BinaryTree { protected Position root; // reference to the root protected int size; // number of nodes /** Creates an empty binary tree. */ public LinkedBinaryTree() { root = null; // start with an empty tree size = 0; } // BinaryTree interface methods /** Returns the number of nodes in the tree. */ public int size() { return size; } /** Returns whether the tree is empty. */ public boolean isEmpty() { return (size==0); } /** Returns whether a node is internal. */ public boolean isInternal(Position v) throws InvalidPositionException { checkPosition(v); // auxiliary method return (hasLeft(v) || hasRight(v)); } /** Returns whether a node is external. */ public boolean isExternal(Position v) throws InvalidPositionException { return !isInternal(v); } /** Returns whether a node is the root. */ public boolean isRoot(Position v) throws InvalidPositionException { checkPosition(v); return (v == root()); } /** Returns whether a node has a left child. */ public boolean hasLeft(Position v) throws InvalidPositionException { BTPosition vv = checkPosition(v); return (vv.getLeft() != null); } /** Returns whether a node has a right child. */ public boolean hasRight(Position v) throws InvalidPositionException { BTPosition vv = checkPosition(v); return (vv.getRight() != null); } /** Returns the root of the tree. */ public Position root() throws EmptyTreeException { if (root == null) throw new EmptyTreeException("The tree has no root"); return root; } /** Returns the left child of a node. */ public Position left(Position v) throws InvalidPositionException, BoundaryViolationException { if (!hasLeft(v)) throw new BoundaryViolationException("No left child"); return ((BTPosition)v).getLeft(); } /** Returns the right child of a node. */ public Position right(Position v) throws InvalidPositionException, BoundaryViolationException { if (!hasRight(v)) throw new BoundaryViolationException("No right child"); return ((BTPosition)v).getRight(); } /** Returns the parent of a node. */ public Position parent(Position v) throws InvalidPositionException, BoundaryViolationException { if (isRoot(v)) throw new BoundaryViolationException("Root has no parent"); return ((BTPosition)v).getParent(); } /** Returns an iterator of the children of a node. */ public Iterator children(Position v) throws InvalidPositionException { List children = new NodeList(); if (hasLeft(v)) children.insertLast(left(v)); if (hasRight(v)) children.insertLast(right(v)); return children.elements(); } /** Returns an iterator of the tree nodes. */ public Iterator positions() { List positions = new NodeList(); if(size != 0) inorderPositions(root(), positions); // assign positions in inorder return positions.elements(); } /** Returns an iterator of the elements stored at the nodes */ public Iterator elements() { Iterator positer = positions(); List elements = new NodeList(); for (int i = 0; i < size; i++) elements.insertLast(((Position) positer.next()).element()); return elements.elements(); // An iterator of elements } /** Replaces the element at a node. */ public Object replace(Position v, Object o) throws InvalidPositionException { BTPosition vv = checkPosition(v); Object temp = v.element(); vv.setElement(o); return temp; } // Additional accessor method /** Return the sibling of a node */ public Position sibling(Position v) throws InvalidPositionException, BoundaryViolationException { try { Position p = parent(v); Position lc = left(p); if (v == lc) return right(p); else return lc; } catch(BoundaryViolationException e) { throw new BoundaryViolationException("Node has no sibling"); } } // Additional update methods /** Inserts a left child at a given node. */ public Position insertLeft(Position v, Object e) throws InvalidPositionException { if (hasLeft(v)) throw new InvalidPositionException("Node already has a left child"); BTPosition vv = (BTPosition)v; BTPosition ww = createNode(e, vv, null, null); vv.setLeft(ww); size++; return ww; } /** Inserts a right child at a given node. */ public Position insertRight(Position v, Object e) throws InvalidPositionException { if (hasRight(v)) throw new InvalidPositionException("Node already has a right child"); BTPosition vv = (BTPosition)v; BTPosition w = createNode(e, vv, null, null); vv.setRight(w); size++; return w; } /** Removes a node with zero or one child. */ public Object remove(Position v) throws InvalidPositionException { if (hasLeft(v) && hasRight(v)) throw new InvalidPositionException("Cannot remove node w/ 2 children"); BTPosition vv = (BTPosition)v; BTPosition ww; // the only child of v, if any if (hasLeft(v)) ww = (BTPosition) left(v); else if (hasRight(v)) ww = (BTPosition) right(v); else ww = null; if (v == root()) { if (ww != null) ww.setParent(null); root = ww; } else { BTPosition uu = (BTPosition) parent(v); if (hasLeft(uu) && v == left(uu)) uu.setLeft(ww); else uu.setRight(ww); if(ww != null) ww.setParent(uu); } size--; return v.element(); } /** Adds a root node to an empty tree */ public Position addRoot(Object e) throws NonEmptyTreeException { if(!isEmpty()) throw new NonEmptyTreeException("Tree already has a root"); size = 1; root = createNode(e,null,null,null); return root; } /** Attaches two trees to be subtrees of an external node. */ public void attach(Position v, BinaryTree T1, BinaryTree T2) throws InvalidPositionException { if (isInternal(v)) throw new InvalidPositionException("Cannot attach from internal node"); BTPosition vv = (BTPosition)v; if (!T1.isEmpty()) { vv.setLeft((BTPosition) T1.root()); ((BTPosition)T1.root()).setParent(vv); // T1 should be invalidated } if (!T2.isEmpty()) { vv.setRight((BTPosition) T2.root()); ((BTPosition)T2.root()).setParent(vv); // T2 should be invalidated } } /** Swap the elements at two nodes */ public void swapElements(Position v, Position w) throws InvalidPositionException { BTPosition vv = checkPosition(v); BTPosition ww = checkPosition(w); Object temp = w.element(); ww.setElement(v.element()); vv.setElement(temp); } /** Expand an external node into an internal node with two external node children */ public void expandExternal(Position v, Object l, Object r) throws InvalidPositionException { if (!isExternal(v)) throw new InvalidPositionException("Node is not external"); insertLeft(v, l); insertRight(v, r); } /** Remove an external node v and replace its parent with v's sibling */ public void removeAboveExternal(Position v) throws InvalidPositionException { if (!isExternal(v)) throw new InvalidPositionException("Node is not external"); if (isRoot(v)) remove(v); else { Position u = parent(v); remove(v); remove(u); } } // Auxiliary methods /** If v is a good binary tree node, cast to BTPosition, else throw exception */ protected BTPosition checkPosition(Position v) throws InvalidPositionException { if (v == null || !(v instanceof BTPosition)) throw new InvalidPositionException("The position is invalid"); return (BTPosition) v; } /** Creates a new binary tree node */ protected BTPosition createNode(Object element, BTPosition parent, BTPosition left, BTPosition right) { return new BTNode(element,parent,left,right); } /** Creates a list storing the the nodes in the subtree of a node, * ordered according to the inorder traversal of the subtree. */ protected void inorderPositions(Position v, List pos) throws InvalidPositionException { if (hasLeft(v)) inorderPositions(left(v), pos); // recurse on left child pos.insertLast(v); if (hasRight(v)) inorderPositions(right(v), pos); // recurse on right child } }