/* * @(#)RedBlackTree.java * * Last Modified: 9/15/01 */ import java.util.*; import java.lang.*; import java.awt.*; import java.awt.font.*; import java.awt.geom.*; /** * The class provides the base structure of a RedBlackTree, a node of a Red-Black tree. * It uses the elements natural ordering. It uses everything defined within the AbstractTree *

The BSTTree cannot be the head of the tree, it is only the nodes. It defines recursive methods * which aren't defined in the head node and it does not define the methods for the head node. * *


The Red-Black tree is a natural extension of a Binary Search Tree, because it is in fact * a specialized Binary Search Tree. *

Note that this implementation is not synchronized. If multiple * threads access this tree concurrently, and at least one of the threads modifies * the tree structurally, it must be synchronized externally. * @author Corey Sanders * @version 3.4 9/15/01 */ public class RedBlackTree extends BSTTree { /*************************************************************************/ /* BSTTree constructor, methods, and variables for all types of BSTTrees */ /*************************************************************************/ /** * Constructs a new, empty RedBlackTree. * *

Default type is BST_TREE_TYPE. */ public RedBlackTree() { this(0); } /** * Constructs a new, empty RedBlackTree according to the type passed. * *@param treeType type of tree that should be implemented. */ public RedBlackTree(int treeType) { super(treeType); } /** * Copies the fields of this RedBlackTree into the RedBlackTree copy. * The only additional information is the red bit. * * @param copy the BSTTree that takes the fields of the BSTTree. */ protected void copyBSTTree(BSTTree copy) { ((RedBlackTree)copy).setRedLink(isRedLink()); super.copyBSTTree(copy); } /*-----------------------------------------------------------------------*/ /*-------------------------------BST_TREE_TYPE---------------------------*/ /*-----------------------------------START-------------------------------*/ /*-----------------------------------------------------------------------*/ /*************************************************************************/ /* BST_TREE_TYPE : Methods and Variables specific to the BST_TREE_TYPE. */ /* These methods are universally used, regardless of the type of BST. */ /*************************************************************************/ /** * Bit to determine whether the link to the node is red or black. * This bit will be modified numerous times throughout insertion. */ private boolean redLink = false; /** ********************************** * BST_TREE_TYPE Accesssor methods* ********************************** */ /** * Gets the red link for the node. * * @return true if the link to the current node is red. */ public boolean isRedLink() { return redLink; } /** ********************************** * BST_TREE_TYPE Mutator methods * ********************************** */ /** * Sets the red link for the node. * * @param redLink true if the link to the current node is red. */ public void setRedLink(boolean redLink) { this.redLink = redLink; } /** * Sets the values of a new Node left and right tress, to refer to null trees. Both the left and right trees are * instantiated and their values are set to the default null values. */ protected void setLeaf() { setLeftTree(new RedBlackTree(getTreeType())); setRightTree(new RedBlackTree(getTreeType())); getLeftTree().setParentTree(this); getRightTree().setParentTree(this); ((RedBlackTree)getLeftTree()).setRedLink(false); ((RedBlackTree)getRightTree()).setRedLink(false); } /** ********************************** * BST_TREE_TYPE Tree Methods * ********************************** */ /** * Inserts the given object into the tree, using recursive calls and is called from the Head node. This method is recursive until it * finds a subtree that is null and sets the node. * *

Before Recursive calls (on the way down) 4-nodes are split up. Then the node is * recursively inserted and on returning calls (on the way back up), Red-Black link integrity * is maintained. * * @param newTree the tree to be inserted, with key and value already set. * * @param currentLevel keeps track of the recursive call, and sets the new level if it changes. * * @throws ClassCastException key cannot be compared with the keys * currently in the tree. */ protected void insert(BSTTree newTree, int currentLevel) throws ClassCastException { if (!((RedBlackTreeHead)getHead()).isTreeAnimating()) { // Set insertion color link. if (currentLevel == 0) { ((RedBlackTree)newTree).setRedLink(true); } // Fixes 4-nodes if (((RedBlackTree)getLeftTree()).isRedLink() && ((RedBlackTree)getRightTree()).isRedLink()) { setRedLink(true); ((RedBlackTree)getLeftTree()).setRedLink(false); ((RedBlackTree)getRightTree()).setRedLink(false); } } super.insert(newTree, currentLevel); if (!((RedBlackTreeHead)getHead()).isTreeAnimating()) { insertRedBlackTree((RedBlackTree)newTree); } } /** * Inserts the given object into the tree, using recursive calls and is called from the Head node. This method is recursive until it * finds a subtree that is null and sets the node. * *

Before Recursive calls (on the way down) 4-nodes are split up. Then the node is * recursively inserted and on returning calls (on the way back up), Red-Black link integrity * is maintained. * * @param newTree the tree to be inserted, with key and value already set. * * @param currentLevel keeps track of the recursive call, and sets the new level if it changes. * * @throws ClassCastException key cannot be compared with the keys * currently in the tree. */ protected void insertRedBlackTree(RedBlackTree newTree) throws ClassCastException { // Keeps Red-Link integrity if ((newTree.getKey()).compareTo(getKey()) < 0) { // Go to the left if (this != getHead().getChild()) { // Two consequetive red links. if ((isRedLink()) && (((RedBlackTree)getLeftTree()).isRedLink())) { // Same Direction if (this != ((RedBlackTree)getParentTree()).getLeftTree()) { ((RedBlackTreeHead)getHead()).rotateUp((RedBlackTree)getLeftTree()); } } } if (!getLeftTree().isEmpty()) { if ((((RedBlackTree)getLeftTree()).isRedLink()) && (((RedBlackTree)getLeftTree().getLeftTree()).isRedLink()) ) { // Reference to rotated up parent. RedBlackTree newParent = (RedBlackTree)getLeftTree(); ((RedBlackTreeHead)getHead()).rotateUp((RedBlackTree)getLeftTree()); newParent.setRedLink(false); ((RedBlackTree)newParent.getRightTree()).setRedLink(true); } } } else { if (this != getHead().getChild()) { // Two consequetive red links. if ((isRedLink()) && (((RedBlackTree)getRightTree()).isRedLink())) { // Same Direction if (this != ((RedBlackTree)getParentTree()).getRightTree()) { ((RedBlackTreeHead)getHead()).rotateUp((RedBlackTree)getRightTree()); } } } if (!getRightTree().isEmpty()) { if ((((RedBlackTree)getRightTree()).isRedLink()) && (((RedBlackTree)getRightTree().getRightTree()).isRedLink()) ) { // Reference to rotated up parent. RedBlackTree newParent = (RedBlackTree)getRightTree(); ((RedBlackTreeHead)getHead()).rotateUp((RedBlackTree)getRightTree()); newParent.setRedLink(false); ((RedBlackTree)newParent.getLeftTree()).setRedLink(true); } } } } /** * Finds the replacing node for a deletion. It simply searches down to the bottom of the tree * Looking for the smallest element from the initial call. * * @return RedBlackTree that should be the smallest node. */ protected RedBlackTree findReplacingNode() { if (getLeftTree().isEmpty()) { return this; } else { return ((RedBlackTree)getLeftTree()).findReplacingNode(); } } /** * Removes the BSTTree from the tree. The call bypasses the BSTTree by * partitioning the right subTree and replacing itself with the smallest of the right tree. */ protected void remove() { BSTTree currentParent = (BSTTree)getParentTree(); boolean left; if (this == currentParent.getLeftTree()) { left = true; } else { left = false; } BSTTree replacedNode; BSTTree childNode; if (getRightTree().isEmpty() || getLeftTree().isEmpty()) { replacedNode = this; } else { replacedNode = ((RedBlackTree)getRightTree()).findReplacingNode(); } if (!replacedNode.getLeftTree().isEmpty()) { childNode = (BSTTree)replacedNode.getLeftTree(); } else { childNode = (BSTTree)replacedNode.getRightTree(); } childNode.setParentTree((BSTTree)replacedNode.getParentTree()); if (replacedNode == ((BSTTree)replacedNode.getParentTree()).getLeftTree()) { ((BSTTree)replacedNode.getParentTree()).setLeftTree(childNode); } else { ((BSTTree)replacedNode.getParentTree()).setRightTree(childNode); } if (replacedNode != this) { // Actually copying. replacedNode.setParentTree(currentParent); if (left) { currentParent.setLeftTree(replacedNode); } else { currentParent.setRightTree(replacedNode); } replacedNode.setRightTree(this.getRightTree()); ((RedBlackTree)replacedNode.getRightTree()).setParentTree(replacedNode); replacedNode.setLeftTree(this.getLeftTree()); ((RedBlackTree)replacedNode.getLeftTree()).setParentTree(replacedNode); ((RedBlackTree)replacedNode).setRedLink(this.isRedLink()); } if (!((RedBlackTree)replacedNode).isRedLink() && !((RedBlackTreeHead)getHead()).isTreeAnimating()) { ((RedBlackTree)childNode).fixUpRedBlackLinks(); } } /** * Fixes down to the bottom of the node passed, the red-black links. The pass down separates * any 4-nodes and the way back up rotates if necessary. */ protected void fixUpRedBlackLinks() { RedBlackTree currentNode = this; RedBlackTree currentNodeSibling; while ((currentNode.getParentTree() != getHead()) && (!currentNode.isRedLink())) { if (currentNode == ((RedBlackTree)currentNode.getParentTree()).getLeftTree()) { currentNodeSibling = (RedBlackTree)((RedBlackTree)currentNode.getParentTree()).getRightTree(); if (currentNodeSibling.isRedLink()) { currentNodeSibling.setRedLink(false); ((RedBlackTree)currentNode.getParentTree()).setRedLink(true); ((RedBlackTreeHead)getHead()).rotateUp(currentNodeSibling); currentNodeSibling = (RedBlackTree)((RedBlackTree)currentNode.getParentTree()).getRightTree(); } if (currentNodeSibling.isEmpty()) { break; } if ( (!((RedBlackTree)currentNodeSibling.getLeftTree()).isRedLink()) && (!((RedBlackTree)currentNodeSibling.getRightTree()).isRedLink()) ){ currentNodeSibling.setRedLink(true); currentNode = (RedBlackTree)currentNode.getParentTree(); } else { if (!((RedBlackTree)currentNodeSibling.getRightTree()).isRedLink()) { ((RedBlackTree)currentNodeSibling.getLeftTree()).setRedLink(false); currentNodeSibling.setRedLink(true); ((RedBlackTreeHead)getHead()).rotateUp((RedBlackTree)currentNodeSibling.getLeftTree()); currentNodeSibling = (RedBlackTree)((RedBlackTree)currentNode.getParentTree()).getRightTree(); } currentNodeSibling.setRedLink(((RedBlackTree)currentNode.getParentTree()).isRedLink()); ((RedBlackTree)currentNode.getParentTree()).setRedLink(false); ((RedBlackTree)currentNodeSibling.getRightTree()).setRedLink(false); ((RedBlackTreeHead)getHead()).rotateUp(currentNodeSibling); break; } } else { currentNodeSibling = (RedBlackTree)((RedBlackTree)currentNode.getParentTree()).getLeftTree(); if (currentNodeSibling.isRedLink()) { currentNodeSibling.setRedLink(false); ((RedBlackTree)currentNode.getParentTree()).setRedLink(true); ((RedBlackTreeHead)getHead()).rotateUp(currentNodeSibling); currentNodeSibling = (RedBlackTree)((RedBlackTree)currentNode.getParentTree()).getLeftTree(); } if (currentNodeSibling.isEmpty()) { break; } if ( !((RedBlackTree)currentNodeSibling.getRightTree()).isRedLink() && !((RedBlackTree)currentNodeSibling.getLeftTree()).isRedLink() ){ currentNodeSibling.setRedLink(true); currentNode = (RedBlackTree)currentNode.getParentTree(); } else { if (!((RedBlackTree)currentNodeSibling.getLeftTree()).isRedLink()) { ((RedBlackTree)currentNodeSibling.getRightTree()).setRedLink(false); currentNodeSibling.setRedLink(true); ((RedBlackTreeHead)getHead()).rotateUp((RedBlackTree)currentNodeSibling.getRightTree()); currentNodeSibling = (RedBlackTree)((RedBlackTree)currentNode.getParentTree()).getLeftTree(); } currentNodeSibling.setRedLink(((RedBlackTree)currentNode.getParentTree()).isRedLink()); ((RedBlackTree)currentNode.getParentTree()).setRedLink(false); ((RedBlackTree)currentNodeSibling.getLeftTree()).setRedLink(false); ((RedBlackTreeHead)getHead()).rotateUp(currentNodeSibling); break; } } } currentNode.setRedLink(false); } /*-----------------------------------------------------------------------*/ /*-------------------------------BST_TREE_TYPE---------------------------*/ /*------------------------------------END--------------------------------*/ /*-----------------------------------------------------------------------*/ /*/////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ /*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\//////////////////////////////////*/ /*-----------------------------------------------------------------------*/ /*----------------------------DRAWING_BST_TREE_TYPE----------------------*/ /*-----------------------------------START-------------------------------*/ /*-----------------------------------------------------------------------*/ /*************************************************************************/ /* DRAWING_BST_TREE_TYPE : Methods and Variables specific to the */ /* DRAWING_BST_TREE_TYPE. These methods are specificaly used for drawing*/ /* to a Graphics2D. Therefore, do not use this type unless that is your */ /* purpose. */ /*************************************************************************/ /** * The current redLinkPaint (Red Default...heh heh OBVIOUSLY) */ private PaintSettings redLinkPaint = PaintSettings.getScheme(PaintSettings.RED); /** * The current redLinkStroke (BasicStroke(2.0F) Default) */ private Stroke redLinkStroke = new BasicStroke(2.0F); /** ******************************************* * DRAWING_BST_TREE_TYPE Accesssor methods * ******************************************* */ /** * Gets the red link paint for the current RedBlackTree. * * @return the paint for the red links of the drawing node. */ public PaintSettings getRedLinkPaint() { return redLinkPaint; } /** * Gets the red link stroke for the current RedBlackTree. * * @return the stroke for the red links of the drawing node. */ public Stroke getRedLinkStroke() { return redLinkStroke; } /** ******************************************* * DRAWING_BST_TREE_TYPE Mutator methods * ******************************************* */ /** * Sets the red link paint for the current RedBlackTree. * * @param redLinkPaint the paint for the red links of the drawing node. */ public void setRedLinkPaint(PaintSettings redLinkPaint) { this.redLinkPaint = redLinkPaint; } /** * Gets the red link stroke for the current RedBlackTree. * * @return the stroke for the red links of the drawing node. */ public void setRedLinkStroke(Stroke redLinkStroke) { this.redLinkStroke = redLinkStroke; } /** * Sets the settings of the entire tree. This represents a recursive call through the tree, * setting the tree's NodeSettings and then calling both children. * * @param s NodeSettings being set for the entire tree. */ protected void setTreeSettings(NodeSettings s, KeySettings k, PaintSettings redLinkPaint, Stroke redLinkStroke) { if (!isDrawingBSTTree()) return; if(isEmpty()) return; setSettings(s); ((DrawableKey)getValue()).setSettings(k); setRedLinkPaint(redLinkPaint); setRedLinkStroke(redLinkStroke); ((RedBlackTree)getLeftTree()).setTreeSettings(s, k, redLinkPaint, redLinkStroke); ((RedBlackTree)getRightTree()).setTreeSettings(s, k, redLinkPaint, redLinkStroke); } /** ***************************************************************** * DRAWING_BST_TREE_TYPE Drawing methods (DrawingTree Interface) * ***************************************************************** */ /** * Draws just the right link according to the NodeSettings currently set. * * @param g2 graphics to which the node and links are drawn. * @param sectionHeight the height of the tree' section, to draw the correct lengths for the links. * @param a transfrom to draw the node and links. * @param drawingLevel the level in the tree to which the node is currently being drawn. * @param powerLevel the power to which the links extend, depending on how many links are present. * @param bottomLinks the boolean to declare whether the bottom links should be drawn. */ protected void drawRightLink(Graphics2D g2, double sectionHeight, AffineTransform a, double drawingLevel, double powerLevel, boolean bottomLinks) { Rectangle2D bounds = g2.getClipBounds(); // Right line Line2D.Double right = new Line2D.Double( (a.getTranslateX() + (3*a.getScaleX())/4.0) , (a.getTranslateY() + a.getScaleY()/2.0) , (a.getTranslateX() + a.getScaleX()/2.0 + bounds.getWidth()/powerLevel) , (sectionHeight * drawingLevel)); // Set graphics information if (!(getRightTree().isEmpty()) && ((RedBlackTree)getRightTree()).isRedLink()) { g2.setStroke(getRedLinkStroke()); g2.setPaint(getRedLinkPaint().getPaint()); } else { g2.setStroke(getSettings().getRightLinkStroke()); g2.setPaint(getSettings().getRightLinkPaint()); } g2.setComposite(getSettings().getRightLinkComposite()); g2.draw(right); } /** * Draws just the left link according to the NodeSettings currently set. * * @param g2 graphics to which the node and links are drawn. * @param sectionHeight the height of the tree' section, to draw the correct lengths for the links. * @param a transfrom to draw the node and links. * @param drawingLevel the level in the tree to which the node is currently being drawn. * @param powerLevel the power to which the links extend, depending on how many links are present. * @param bottomLinks the boolean to declare whether the bottom links should be drawn. */ protected void drawLeftLink(Graphics2D g2, double sectionHeight, AffineTransform a, double drawingLevel, double powerLevel, boolean bottomLinks) { Rectangle2D bounds = g2.getClipBounds(); // Draw the left and right links Line2D.Double left = new Line2D.Double( (a.getTranslateX() + a.getScaleX()/4.0) , (a.getTranslateY() + a.getScaleY()/2.0) , (a.getTranslateX() + a.getScaleX()/2.0 - bounds.getWidth()/powerLevel) , (sectionHeight * drawingLevel)); // Set graphics information if (!(getLeftTree().isEmpty()) && ((RedBlackTree)getLeftTree()).isRedLink()) { g2.setStroke(getRedLinkStroke()); g2.setPaint(getRedLinkPaint().getPaint()); } else { g2.setStroke(getSettings().getRightLinkStroke()); g2.setPaint(getSettings().getRightLinkPaint()); } g2.setComposite(getSettings().getRightLinkComposite()); g2.draw(left); } /*-----------------------------------------------------------------------*/ /*----------------------------DRAWING_BST_TREE_TYPE----------------------*/ /*------------------------------------END--------------------------------*/ /*-----------------------------------------------------------------------*/ /*/////////////////////////////////////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/ /*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\//////////////////////////////////*/ /*-----------------------------------------------------------------------*/ /*---------------------------ANIMATING_BST_TREE_TYPE---------------------*/ /*-----------------------------------START-------------------------------*/ /*-----------------------------------------------------------------------*/ /*************************************************************************/ /* ANIMATING_BST_TREE_TYPE : Methods and Variables specific to the */ /* ANIMATING_BST_TREE_TYPE. These methods are specificaly used for */ /* animaitng to a Graphics2D. Therefore, do not use this type unless */ /* that is your purpose. */ /*************************************************************************/ /** ********************************************* * ANIMATING_BST_TREE_TYPE Accesssor methods * ********************************************* */ /**********************************/ /* Recursive Constructing methods */ /**********************************/ /** * Insert the BSTTree down from the current node. This is a recursive call, * defined in BSTTree. It is overiden in this class to build the animation * of the insertion. The reason this animation is overiden and not simply added in the AnimatingBSTTreeHead, * is to allow multiple insertions to occur at once. Therefore, insertion never goes through * WaitingActionList. * * @param node the node being inserted into the tree. * @param newAnimator the InsertBSTAnimation to which the nodes are being added. * * @throws ClassCastException key cannot be compared with the keys * currently in the tree. */ protected void makeInsertAnimation(BSTTree insertNode, InsertRedBlackAnimation newAnimator) throws ClassCastException { if (!isAnimatingBSTTree()) { return; } if (isEmpty()) { return; } // Add the animator to the Insertion animation. newAnimator.add(this); if ((insertNode.getKey()).compareTo(getKey()) < 0) { // Go to the left ((RedBlackTree)getLeftTree()).makeInsertAnimation(insertNode, newAnimator); } else { // Go to the right ((RedBlackTree)getRightTree()).makeInsertAnimation(insertNode, newAnimator); } } }