package edu.princeton.cs.algs4.growingtree.framework; import edu.princeton.cs.algs4.growingtree.experiments.ExperimentTree; import edu.princeton.cs.algs4.growingtree.experiments.IExperimentLogger; import edu.princeton.cs.algs4.growingtree.interfaces.IDeletingNode; import edu.princeton.cs.algs4.growingtree.interfaces.IInsertingNode; import edu.princeton.cs.algs4.growingtree.interfaces.INode; import edu.princeton.cs.algs4.growingtree.interfaces.ISearchingNode; /** * This class defines the nodes that interact directly with the * operators defined by the client. Primitives such as rotation * happen synchronously within the tree composed of these nodes, * whereas they occur asynchronously within the * GrowingTreeNode tree. * * Operators receive instances of these objects, though they are * cast to the appropriate interface. When an operator calls a * primitive, such as rotateLeft, it passed along to the * GrowingTreeNode associated with this ShadowNode, so * that the animation can be queued up. * @author Josh Israel * * @param

NodeProperties subclass that parameterizes the tree */ public class ShadowNode

implements IInsertingNode

, ISearchingNode

, IDeletingNode

{ /** * Are we animating these operations? (is it a TreeVisualization or a TreeExperiment?) */ private boolean animating; /** * Are we logging these operations? */ private boolean logging; /** * Visual node associated with this node */ private GrowingTreeNode

node; // ONLY USED FOR ANIMATING private P nodeProperties; private ShadowNode

parent; private ShadowNode

left; private ShadowNode

right; /** * Key to be used when running experiments. Admittedly, a bit kludgey */ private Double key; // ONLY USED FOR RUNNING EXPERIMENTS public IExperimentLogger

logger; public ShadowNode(GrowingTreeNode

node, P np) { this.node = node; this.nodeProperties = np; animating = true; logging = false; } public ShadowNode(GrowingTreeNode

node, P np, IExperimentLogger

logger) { this(node, np); logging = true; this.logger = logger; } public ShadowNode(double key, P np, ExperimentTree

et) { this.key = key; this.nodeProperties = np; this.logger = et; logging = true; } public Double getKey() { return key; } public GrowingTreeNode

getGrowingNode() { return node; } public IExperimentLogger

getLogger() { return logger; } private static int height(ShadowNode n) { if (n == null) return -1; return n.getNodeProperties().getHeight(); } private static int size(ShadowNode n) { if (n == null) return 0; return n.getNodeProperties().getSize(); } private void updateHeight() { int newHeight = 1 + Math.max(height(getLeft()), height(getRight())); if (newHeight != getNodeProperties().getHeight()) { if (getLogger() != null) getLogger().logOther(this, IExperimentLogger.HEIGHT_UPDATE); getNodeProperties().setHeight(newHeight); if (getParent() != null) { getParent().updateHeight(); } } } private void updateSize() { int newSize = 1 + size(getLeft()) + size(getRight()); if (newSize != getNodeProperties().getSize()) { if (getLogger() != null) getLogger().logOther(this, IExperimentLogger.SIZE_UPDATE); getNodeProperties().setSize(newSize); if (getParent() != null) { getParent().updateSize(); } } } public ShadowNode

getRight() { assert (right != this); if (right != null) { if (right.parent != this) System.out.println("The key is: " + getKey() + " " + right.getKey() + " " + right.parent.getKey()); assert (right.parent==this); } return right; } public ShadowNode

getLeft() { assert (left != this); if (left != null) assert (left.parent==this); return left; } public ShadowNode

getParent() { assert (parent != this); if (parent != null) assert (parent.left == this || parent.right == this); return parent; } public ShadowNode

getRoot() { ShadowNode

root = this; while (root.getParent() != null) { root = root.getParent(); } return root; } public int compareTo(INode

other) { ShadowNode

that = (ShadowNode

) other; if (animating) return getGrowingNode().compareTo(that.getGrowingNode()); return getKey().compareTo(that.getKey()); } public int compareTo(Double other) { return getKey().compareTo(other); } public P getNodeProperties() { return nodeProperties; } public ShadowNode

rotateLeft() { assert (right != null); assert(getRoot().isBST()); if (animating) right.getGrowingNode().rotateUp(); if (logging) logger.logRotation(this); ShadowNode

x = right; right = x.left; if (right != null) { right.parent = this; } x.left = this; if (parent != null) { if (parent.right == this) parent.right = x; else if (parent.left == this) parent.left = x; else { assert(false); } } x.parent = parent; parent = x; assert(getRoot().isBST()); updateHeight(); x.updateHeight(); updateSize(); x.updateSize(); if (x.getParent() != null) { x.getParent().updateHeight(); } return right; } public ShadowNode

rotateRight() { assert (left != null); assert(getRoot().isBST()); if (animating) left.getGrowingNode().rotateUp(); if (logging) logger.logRotation(this); ShadowNode

x = left; left = x.right; if (left != null) { left.parent = this; } x.right = this; if (parent != null) { if (parent.right == this) parent.right = x; else if (parent.left == this) parent.left = x; else { assert(false); } } x.parent = parent; parent = x; assert(getRoot().isBST()); updateHeight(); x.updateHeight(); updateSize(); x.updateSize(); if (x.getParent() != null) { x.getParent().updateHeight(); } return x; } public ShadowNode

insertLeft(INode

n) { assert(n != null); ShadowNode

ns = (ShadowNode

) n; assert(ns.parent == null && ns.left == null && ns.right == null); ns.parent = this; this.left = ns; if (animating) getGrowingNode().insertNode(((ShadowNode

) n).node); if (logging) logger.logInsertion(this); left = (ShadowNode

) n; assert (getRoot().isBST()); updateHeight(); updateSize(); return left; } public ShadowNode

insertRight(INode

n) { assert (n != null); ShadowNode

ns = (ShadowNode

) n; if (ns.parent != null) assert(false); ns.parent = this; this.right = ns; if (animating) getGrowingNode().insertNode(ns.getGrowingNode()); if (logging) logger.logInsertion(this); right = ns; assert (getRoot().isBST()); updateHeight(); updateSize(); return right; } public ShadowNode

getSuccessor() { ShadowNode

succ = right; while (succ.left != null) { succ = succ.left; } return succ; } public ShadowNode

getPredecessor() { ShadowNode

pred = left; while (pred.right != null) { pred = pred.right; } return pred; } private static

int rank(ShadowNode

key, ShadowNode

n) { if (n == null) return 0; int cmp = key.compareTo(n); if (cmp < 0) return rank(key, n.getLeft()); else if (cmp > 0) return 1 + size(n.getLeft()) + rank(key, n.getRight()); else return size(n.getLeft()); } private void swapNodes(ShadowNode

other) { if (animating) { int rank = rank(other, this); getGrowingNode().getHead().swapNodes(getGrowingNode(), rank); GrowingTreeNode

temp = getGrowingNode(); node = other.node; other.node = temp; node.shadowNode = this; other.node.shadowNode = other; } ShadowNode

tempParent = getParent(); ShadowNode

tempLeft = getLeft(); ShadowNode

tempRight = getRight(); if (tempParent != null && this == tempParent.left) { tempParent.left = other; } else { if (tempParent != null) tempParent.right = other; } if (other.parent != null && other.parent.left == other) { other.parent.left = this; } else { other.parent.right = this; } right = other.right; left = other.left; if (other.parent == this) parent = other; else parent = other.parent; other.parent = tempParent; if (tempLeft == other) other.left = this; else other.left = tempLeft; if (tempRight == other) other.right = this; else other.right = tempRight; if (right != null) right.parent = this; if (left != null) left.parent = this; if (other.left != null) other.left.parent = other; if (other.right != null) other.right.parent = other; other.updateHeight(); other.updateSize(); updateHeight(); updateSize(); assert(getRoot().isBST()); } private ShadowNode

leafDelete() { if (animating) getGrowingNode().getHead().delete(getGrowingNode(), true); if (parent == null) { return null; } if (parent.left == this) { parent.left = null; } if (parent.right == this) { parent.right = null; } parent.updateHeight(); parent.updateSize(); return null; } // replace a node with 1 child with that child private ShadowNode

replaceWithChild() { assert (left == null || right == null); if (animating) getGrowingNode().getHead().delete(getGrowingNode(), true); ShadowNode

child; if (left == null) { child = right; } else { child = left; } assert (child != null); child.parent = parent; if (parent == null) return child; if (parent.right == this) { parent.right = child; } else { parent.left = child; } parent.updateHeight(); parent.updateSize(); return child; } public ShadowNode

successorHibbardDelete() { ShadowNode

ret; if (logging) logger.logDeletion(this); if (left == null && right == null) { ret = leafDelete(); } else if (right == null || left == null) { ret = replaceWithChild(); } else { assert (right != null); ShadowNode

succ = getSuccessor(); swapNodes(succ); if (right == null && left == null) { ret = leafDelete(); } else { ret = replaceWithChild(); } assert(succ.getRoot().isBST()); } return ret; } public ShadowNode

predecessorHibbardDelete() { ShadowNode

ret; if (logging) logger.logDeletion(this); if (left == null && right == null) { ret = leafDelete(); } else if (right == null || left == null) { ret = replaceWithChild(); } else { assert (right != null); ShadowNode

succ = getPredecessor(); swapNodes(succ); if (succ.right == null && succ.left == null) { ret = succ.leafDelete(); } else { ret = succ.replaceWithChild(); } } return ret; } public void markFound() { if (animating) getGrowingNode().getHead().searchAnimatingType(getGrowingNode().getKey()); if (logging) logger.logSearchHit(this); } public boolean isBST() { assert(parent != this); if (right != null) { assert (this == right.parent); assert (right.isBST()); } if (left != null) { assert (this == left.parent); assert (left.isBST()); } return true; } public void freeze() { freeze(1); } public void freeze(double lengthMult) { if (animating) getGrowingNode().getHead().freeze(lengthMult); } }