/* * esodata - data structures and other things, of varying utility * Copyright 2022, Ben Culkin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package bjc.funcdata.bst; import static bjc.funcdata.bst.DirectedWalkFunction.DirectedWalkResult.FAILURE; import static bjc.funcdata.bst.DirectedWalkFunction.DirectedWalkResult.LEFT; import static bjc.funcdata.bst.DirectedWalkFunction.DirectedWalkResult.RIGHT; import static bjc.funcdata.bst.DirectedWalkFunction.DirectedWalkResult.SUCCESS; import java.util.Comparator; import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; /** * A binary node in a tree. * * @author ben * * @param * The data type stored in the tree. */ public class BinarySearchTreeNode extends BinarySearchTreeLeaf { /* The left child of this node */ private TreePart left; /* The right child of this node */ private TreePart right; /** * Create a new node with the specified data and children. * * @param element * The data to store in this node. * * @param lft * The left child of this node. * * @param rght * The right child of this node. */ public BinarySearchTreeNode(final T element, final TreePart lft, final TreePart rght) { super(element); this.left = lft; this.right = rght; } @Override public void add(final T element, final Comparator comparator) { if (comparator == null) throw new NullPointerException("Comparator must not be null"); switch (comparator.compare(data, element)) { case -1: if (left == null) { left = new BinarySearchTreeNode<>(element, null, null); } else { left.add(element, comparator); } break; case 0: if (isDeleted) { isDeleted = false; } else throw new IllegalArgumentException("Can't add duplicate values"); break; case 1: if (right == null) { right = new BinarySearchTreeNode<>(element, null, null); } else { right.add(element, comparator); } break; default: throw new IllegalStateException("Error: Comparator yielded invalid value"); } } @Override public E collapse(final Function nodeCollapser, final BiFunction branchCollapser) { if (nodeCollapser == null || branchCollapser == null) throw new NullPointerException("Collapser must not be null"); final E collapsedNode = nodeCollapser.apply(data); if (left != null) { final E collapsedLeftBranch = left.collapse(nodeCollapser, branchCollapser); if (right != null) { final E collapsedRightBranch = right.collapse(nodeCollapser, branchCollapser); final E collapsedBranches = branchCollapser.apply(collapsedLeftBranch, collapsedRightBranch); return branchCollapser.apply(collapsedNode, collapsedBranches); } return branchCollapser.apply(collapsedNode, collapsedLeftBranch); } if (right != null) { final E collapsedRightBranch = right.collapse(nodeCollapser, branchCollapser); return branchCollapser.apply(collapsedNode, collapsedRightBranch); } return collapsedNode; } @Override public boolean contains(final T element, final Comparator comparator) { if (comparator == null) throw new NullPointerException("Comparator must not be null"); return directedWalk(currentElement -> { switch (comparator.compare(element, currentElement)) { case -1: return LEFT; case 0: return isDeleted ? FAILURE : SUCCESS; case 1: return RIGHT; default: return FAILURE; } }); } @Override public void delete(final T element, final Comparator comparator) { if (comparator == null) throw new NullPointerException("Comparator must not be null"); directedWalk(currentElement -> { switch (comparator.compare(data, element)) { case -1: return left == null ? FAILURE : LEFT; case 0: isDeleted = true; return FAILURE; case 1: return right == null ? FAILURE : RIGHT; default: return FAILURE; } }); } @Override public boolean directedWalk(final DirectedWalkFunction treeWalker) { if (treeWalker == null) throw new NullPointerException("Walker must not be null"); switch (treeWalker.walk(data)) { case SUCCESS: return true; case LEFT: return left.directedWalk(treeWalker); case RIGHT: return right.directedWalk(treeWalker); case FAILURE: default: return false; } } @Override public boolean forEach(final TreeLinearizationMethod linearizationMethod, final Predicate traversalPredicate) { if (linearizationMethod == null) { throw new NullPointerException("Linearization method must not be null"); } else if (traversalPredicate == null) { throw new NullPointerException("Predicate must not be null"); } switch (linearizationMethod) { case PREORDER: return preorderTraverse(linearizationMethod, traversalPredicate); case INORDER: return inorderTraverse(linearizationMethod, traversalPredicate); case POSTORDER: return postorderTraverse(linearizationMethod, traversalPredicate); default: String msg = String.format("Passed an incorrect TreeLinearizationMethod %s. WAT", linearizationMethod); throw new IllegalArgumentException(msg); } } /* Do an in-order traversal. */ private boolean inorderTraverse(final TreeLinearizationMethod linearizationMethod, final Predicate traversalPredicate) { if (!traverseLeftBranch(linearizationMethod, traversalPredicate)) return false; if (!traverseElement(traversalPredicate)) return false; if (!traverseRightBranch(linearizationMethod, traversalPredicate)) return false; return true; } /* Do a post-order traversal. */ private boolean postorderTraverse(final TreeLinearizationMethod linearizationMethod, final Predicate traversalPredicate) { if (!traverseLeftBranch(linearizationMethod, traversalPredicate)) return false; if (!traverseRightBranch(linearizationMethod, traversalPredicate)) return false; if (!traverseElement(traversalPredicate)) return false; return true; } /* Do a pre-order traversal. */ private boolean preorderTraverse(final TreeLinearizationMethod linearizationMethod, final Predicate traversalPredicate) { if (!traverseElement(traversalPredicate)) return false; if (!traverseLeftBranch(linearizationMethod, traversalPredicate)) return false; if (!traverseRightBranch(linearizationMethod, traversalPredicate)) return false; return true; } /* Traverse an element. */ private boolean traverseElement(final Predicate traversalPredicate) { boolean nodeSuccesfullyTraversed; if (isDeleted) { nodeSuccesfullyTraversed = true; } else { nodeSuccesfullyTraversed = traversalPredicate.test(data); } return nodeSuccesfullyTraversed; } /* Traverse the left branch of a tree. */ private boolean traverseLeftBranch(final TreeLinearizationMethod linearizationMethod, final Predicate traversalPredicate) { boolean leftSuccesfullyTraversed; if (left == null) { leftSuccesfullyTraversed = true; } else { leftSuccesfullyTraversed = left.forEach(linearizationMethod, traversalPredicate); } return leftSuccesfullyTraversed; } /* Traverse the right branch of a tree. */ private boolean traverseRightBranch(final TreeLinearizationMethod linearizationMethod, final Predicate traversalPredicate) { boolean rightSuccesfullyTraversed; if (right == null) { rightSuccesfullyTraversed = true; } else { rightSuccesfullyTraversed = right.forEach(linearizationMethod, traversalPredicate); } return rightSuccesfullyTraversed; } @Override public String toString() { return String.format("BinarySearchTreeNode [left='%s', right='%s']", left, right); } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + (left == null ? 0 : left.hashCode()); result = prime * result + (right == null ? 0 : right.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) return true; if (!super.equals(obj)) return false; if (!(obj instanceof BinarySearchTreeNode)) return false; final BinarySearchTreeNode other = (BinarySearchTreeNode) obj; if (left == null) { if (other.left != null) return false; } else if (!left.equals(other.left)) return false; if (right == null) { if (other.right != null) return false; } else if (!right.equals(other.right)) return false; return true; } }