[1716] in java-interest

home help back first fref pref prev next nref lref last post

Re: linked list w/o references or ptrs?

daemon@ATHENA.MIT.EDU (Chuck McManis)
Fri Sep 15 02:07:12 1995

Date: Thu, 14 Sep 1995 20:10:57 -0700
From: cmcmanis@scndprsn.Eng.Sun.COM (Chuck McManis)
To: fgreco@lehman.com, java-interest@java.Eng.Sun.COM


This is an example of implementing "Red-Black" trees from the Rivest
book on algorithms. It creates a balanced binary tree and supports
both adding and deleting nodes. It is a good example of a traditional
linked list. There are two files here, separated by dashed lines
RedBlackTree.java and Main.java, the latter is simply a simple
exerciser for the former to show how it works.

--Chuck

--------------------------- RedBlackTree.java
/*
 * @(#)RedBlackTree.java	1.1 95/09/14  
 *
 * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. Please refer to the file "copyright.html"
 * for further important copyright and licensing information.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

// package sun.misc;

import java.io.PrintStream;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.NoSuchElementException;

/**
 * This class implements a Dictionary using a B-tree. This exact type
 * of tree is called a "red-black" tree since it uses a color algorithim
 * to insure that it is always balanced. Note that the key object must
 * be an object of class String for this class. This is because the
 * implementation uses compareTo() to determine if a node should be on
 * the right or left hand branch.
 *
 * @author	Chuck McManis
 * @version	1.1, 14 Sep 1995
 * @see		Dictionary
 */

public class RedBlackTree {
    private RedBlackTreeNode listRoot;
    private final int RED = 0;
    private final int BLACK = 1;
    private int count = 0;
    private long treeGen = 0; // This tree's generation count.

    /**
     * Returns the number of elements in the tree.
     */
    public int size() {
	return (count);
    }

    /**
     * Returns true if the tree is empty.
     */
    public boolean isEmpty() {
	return (listRoot == null);
    }

    /**
     * Return an enumeration of the trees keys. This will return the
     * keys in sorted order as it does an inorder walk from the minimum
     * valued key to the maximum valued key.
     */
    public Enumeration keys() {
	return new RedBlackTreeEnumerator(this, true);
    }

    /**
     * Return an enumeration of the trees objects. This will return the
     * elements in sorted order as it does an inorder walk from minimum 
     * key to the  maximum key.
     */
    public Enumeration elements() {
	return new RedBlackTreeEnumerator(this, false);
    }

   
    /**
     * Return the value for a given key.
     */
    public Object get(Object key) {
	if (key instanceof String) {
	    RedBlackTreeNode x = lookup((String) key);
	    return ((x != null) ? x.value : null);
	}
	return (null); // throw something?
    }

    /**
     * Add a new object to the tree. Note that if there is already a
     * node in the tree with this key value, the old value is replaced
     * with the new value. The old value is returned. If no previous
     * node exists, this returns null.
     */
    public Object put(Object key, Object value) throws Exception {
	if (key instanceof String) {
	    RedBlackTreeNode x = add((String) key, value);
	    if (x == null)
		count++;	// Added a new element
	    return ((x != null) ? x.value : null);
        }
	throw new Exception("Cannot add");
    }

    /**
     * Remove an object from the tree. This returns null if the key did
     * not reference an object in the tree.
     */
    public Object remove(Object key) {
	if (key instanceof String) {
	    RedBlackTreeNode x = lookup((String) key);
	    if (x != null) {
		delete(x);
		count--;
		return (x.value);
	    }
        }
	return (null); // throw something?
    }

    /* ******************************************* *
     * PRIVATE Implementation of Red-Black B-trees *
     * ******************************************* */

    /**
     * Return the value of the generation variable
     */
    long generation() {
	return treeGen;
    }

    /**
     * Rotate the tree left about node 'x'.
     */
    private void rotateLeft(RedBlackTreeNode x) {
	RedBlackTreeNode y = x.right;
	
	x.right = y.left;
	if (y.left != null) 
	    y.left.parent = x;

	y.parent = x.parent;
	if (x.parent == null)
		listRoot = y;
	else {
	    if (x == x.parent.left)
	        x.parent.left = y;
  	    else
	        x.parent.right = y;
	}
	y.left = x;
	x.parent = y;
    }

    /**
     * Return the color of node x, note if node x is null then
     * the color is presumed to be black.
     */
    private int color(RedBlackTreeNode x) {
	if (x == null)
	    return BLACK;
	else
	    return (x.color);
    }

    /**
     * Rotate the tree right about the node y. Rotations preserve
     * the key ordering of their children nodes.
     */
    private void rotateRight(RedBlackTreeNode y) {
	RedBlackTreeNode x = y.left;

	y.left = x.right;

	if (x.right != null) 
	    x.right.parent = y;

	x.parent = y.parent;

	if (y.parent == null)
	    listRoot = x;
	else {
	    if (y == y.parent.left)
	        y.parent.left = x;
	    else
	        y.parent.right = x;
	}

	x.right = y;
	y.parent = x;
    }

    /**
     * Simple node insertion. After insertion the red-black properties
     * are "fixed" by rotating the correct number of nodes to balance
     * the tree. See add below.
     */
    private RedBlackTreeNode insert(RedBlackTreeNode z) {
	RedBlackTreeNode x = listRoot;
	RedBlackTreeNode y = null;
	int	i;

	while (x != null) {
	    y = x;
	    x = (x.key.compareTo(z.key) > 0) ? x.left : x.right;
	}
	z.parent = y;
	if (y == null) {
	    listRoot = z;
	} else {
	    i = y.key.compareTo(z.key);
	    if (i > 0)
	        y.left = z;
	    else if (i < 0) 
		y.right = z;
	    else { // They have the same key
		Object t;

		t = z.value;		// swap values
		z.value = y.value;
		y.value = t;
		return (z);		// return old value
	    }
	}
	return (null);
    }

    /**
     * Return the lowest key valued node. (needed for enumerations)
     */
    RedBlackTreeNode minimum() {
	return minimum(listRoot);
    }

    /**
     * Return the lowest key value in the subtree under x
     */
    private RedBlackTreeNode minimum(RedBlackTreeNode x) {
	if (x == null)
	    x = listRoot;

	while (x != null) {
	    if (x.left == null)
		return x;
	    x = x.left;
 	}
	return null;
    }

    /**
     * Return the highest value key under the subtree x
     */
    private RedBlackTreeNode maximum(RedBlackTreeNode x) {
	if (x == null)
	    x = listRoot;

	while (x != null) {
	    if (x.right == null)
		return x;
	    x = x.right;
	}
	return null;
    }


    /**
     *  Return the next higher key value following x.
     */
    RedBlackTreeNode successor(RedBlackTreeNode x) {
	if (x.right != null)
	    return (minimum(x.right));
	RedBlackTreeNode y = x.parent;
	while ((y != null) && (x == y.right)) {
	    x = y;
	    y = x.parent;
	}
	return y;
    }

    /**
     *  Return the next lower node to node x in key order 
     */
    private RedBlackTreeNode predecessor(RedBlackTreeNode x) {
	if (x.left != null)
	    return (maximum(x.left));
	RedBlackTreeNode y = x.parent;
	while ((y != null) && (x == y.left)) {
	    x = y;
	    y = x.parent;
	}
	return y;
    }
    
    /**
     * Add a new node to the B-tree. Do the insertion, followed by
     * balancing. If the node is simply a replacement, skip the
     * balancing section.
     */
    private synchronized RedBlackTreeNode add(String key, Object datum) {
	RedBlackTreeNode x = new RedBlackTreeNode(key, datum);
	RedBlackTreeNode y;
	RedBlackTreeNode oldValue = null;

	oldValue = insert(x);
	if (oldValue != null) {
	    return (oldValue);
	}
	treeGen++;	// note that it is a new tree

	x.color = RED;
	
	/* now fix up the tree */
	while ((x != listRoot) && (x.parent.color == RED)) {

	    if (x.parent == x.parent.parent.left) {
		y = x.parent.parent.right;
		if ((y != null) && (y.color == RED)) {
		    x.parent.color = BLACK;
		    y.color = BLACK;
		    x.parent.parent.color = RED;
		    x = x.parent.parent;
		} else {
		    if (x == x.parent.right) {
			x = x.parent;
			rotateLeft(x);
		    }
		    x.parent.color = BLACK;
		    x.parent.parent.color = RED;
		    rotateRight(x.parent.parent);
		}
	    } else {
		y = x.parent.parent.left;
		if ((y != null) && (y.color == RED)) {
		    x.parent.color = BLACK;
		    y.color = BLACK;
		    x.parent.parent.color = RED;
		    x = x.parent.parent;
		} else {
		    if (x == x.parent.left) {
			x = x.parent;
			rotateRight(x);
		    }
		    x.parent.color = BLACK;
		    x.parent.parent.color = RED;
		    rotateLeft(x.parent.parent);
		}
	    }
	}
	listRoot.color = BLACK;
	return null;
    }

    /**
     * Return the node indexed by key.
     */
    private synchronized RedBlackTreeNode lookup(String key) {
	RedBlackTreeNode x = listRoot;
	int	ci;

	while (x != null) {
	    ci = x.key.compareTo(key);
	    if (ci < 0)
		x = x.right;
	    else if (ci > 0)
		x = x.left;
	    else
		return x;
	}
	return (null);
    }

    /**
     * return a string of length 'n' blanks  (helper for printNode)
     */
    private String blanks(int n) {
	StringBuffer z = new StringBuffer(n);
	for (int q = 0; q < n; q++)
	    z.append(" ");
	return (z.toString());
    }

    /**
     * Print a nice graphical representation of the tree on the 
     * PrintStream passed. See printTree for the output.
     */
    private void printNode(RedBlackTreeNode x, String indent, PrintStream out) {

	if (x == null)
	    return;

	if (x.right != null) {
	    if ((x.parent != null) && (x == x.parent.left))
	        printNode(x.right, indent+"|"+blanks(x.key.length()+2), out);
	    else
	        printNode(x.right, indent+blanks(x.key.length()+3), out);
	}

	out.print(indent);
	out.print((x.color == BLACK) ? "@ " : "O ");
	out.print(x.key);
	if ((x.right != null) || (x.left != null))
	    out.println(" +");
	else
	    out.println();

	if (x.left == null) {
	    if (x.parent != null) {
		if (x == x.parent.right)
	          indent = indent + "|";
		else
	          indent = indent + " ";
	    }
	    out.println(indent);
	} else {
	    if ((x.parent != null) && (x == x.parent.right)) 
		indent = indent + "|" + blanks(x.key.length()+2);
	    else
		indent = indent + " " + blanks(x.key.length()+2);
	    
	    out.println(indent+"|");
	    printNode(x.left, indent, out);
	}
    }
    
    /**
     * Print a graphical version of the tree (ignores the 80 column limit)
     * on terminals. This function walks the tree and outputs a graphical
     * form with the keys in place. The format is as follows:
     * <pre>
     *                O fraz
     *                |
     *          @ foo +
     *          |
     * @ bletch +
     *          |
     *          |     O baz
     *          |     |
     *          @ bar +
     *</pre>
     * Each node is represented by '@' if it is black, and 'O' if it is
     * red, followed by its key, and its children. Nodes to the right in 
     * output are lower in the tree than nodes to the left, the root node
     * is in column 1. Nodes lower on the output are to the "left" of 
     * nodes earlier or higher in the output. (rotate 90 degrees clockwise)
     */
    public void printTree(PrintStream o) {
	printNode(listRoot, "", o);	
    }

    /**
     * Print the tree to System.out
     */
    public void printTree() {
	printNode(listRoot, "", System.out);	
    }

    /**
     * Fix up the coloring of the tree after a remove operation.
     */
    private void delete_fixup(RedBlackTreeNode x) {
	RedBlackTreeNode w;

	if (x.parent == null) // deleted last node in tree
	    return;

	while ((x != listRoot) && (x.color == BLACK)) {
	    if (((x == nilNode) && (x.parent.left == null)) ||
		(x == x.parent.left)) {
		w = x.parent.right;
		if (color(w) == RED) {
		    w.color = BLACK;
		    w.parent.color = RED;
		    rotateLeft(x.parent);
		    w = x.parent.right;
		}
		if ((color(w.left) == BLACK) && (color(w.right) == BLACK)) {
		    w.color = RED;
		    x = x.parent;
		} else {
		    if (color(w.right) == BLACK) {
		    	w.left.color = BLACK;
		    	w.color = RED;
		    	rotateRight(w);
		    	w = x.parent.right;
		    }
		    w.color = x.parent.color;
		    x.parent.color = BLACK;
		    w.right.color = BLACK;
		    rotateLeft(x.parent);
		    x = listRoot;
		}
	    } else {
		w = x.parent.left;
		if (color(w) == RED) {
		    w.color = BLACK;
		    w.parent.color = RED;
		    rotateRight(x.parent);
		    w = x.parent.left;
		}
		if ((color(w.left) == BLACK) && (color(w.right) == BLACK)) {
		    w.color = RED;
		    x = x.parent;
		} else {
		    if (color(w.left) == BLACK) {
		    	w.right.color = BLACK;
		    	w.color = RED;
		    	rotateLeft(w);
		    	w = x.parent.left;
		    }
		    w.color = x.parent.color;
		    x.parent.color = BLACK;
		    w.left.color = BLACK;
		    rotateRight(x.parent);
		    x = listRoot;
		}

	    }
	}
	x.color = BLACK;
    }

    // makes the delete algorithm cleaner
    private RedBlackTreeNode nilNode = new RedBlackTreeNode("Nil", "Nil");

    /**
     * Remove a node from the tree and if it is black, fix up the
     * tree so that the black height remains balanced.
     */
    private synchronized RedBlackTreeNode delete(RedBlackTreeNode z) {
	RedBlackTreeNode x = null, y = null;

	nilNode.color = BLACK;
	treeGen++; // tree has changed, flag enumerators
	if ((z.left == null) || (z.right == null))
	    y = z;		// Z has only one child
	else
	    y = successor(z);
	
	if (y.left != null)
	    x = y.left;
	else
	    x = (y.right == null) ? nilNode : y.right;	

	x.parent = y.parent;

	if (y.parent == null)
	    listRoot = (x == nilNode) ? null : x;
	else {
	    if (y == y.parent.left)
		y.parent.left = (x == nilNode) ? null : x;
	    else
		y.parent.right = (x == nilNode) ? null : x;
	}
	if (y != z) {
	    z.key = y.key;
	    z.value = y.value;
	}

	if (y.color == BLACK) 
	    delete_fixup(x);

	return (y);
    }

    public String toString() {
	return ("RedBlackTree object with "+count+" elements.");
    }
}

/**
 * This class defines an individual node on the B-tree itself. It is
 * private to btrees and has no methods. 
 */
class RedBlackTreeNode {
    int color;
    Object value;
    String key;

    RedBlackTreeNode right, left, parent;

    public RedBlackTreeNode(String nodeKey, Object nodeValue) {
	super();
	key = nodeKey;
	value = nodeValue;
    }

    public String toString() {
	return ("K("+((color == 0)?"R":"B")+"):"+key);
    }
}

/**
 * This is the enumerator class. It implements the enumeration interface
 * and is very straightforward.
 *
 * Note that there is a race condition whereby an enumerator becomes
 * "invalid" should the tree be changed while it is held.
 */
class RedBlackTreeEnumerator implements Enumeration {
    // state for the enumerator
    private boolean keys;
    private RedBlackTree tree;
    private RedBlackTreeNode x;
    private long generation;

    /**
     * Create a new enumerator.
     */
    RedBlackTreeEnumerator(RedBlackTree tree, boolean keys) {
	this.tree = tree;
	this.keys = keys;
	this.x = tree.minimum();
	this.generation = tree.generation();
    }

    /**
     * Returns true if there is another element in the list.
     */
    public boolean hasMoreElements() {
	return (x != null);
    }

    /**
     * Returns the next element.
     * @exception NoSuchElementException thrown if it is out of elements.
     * @exception Exception thrown if the tree has been modified.
     */
    public Object nextElement() {
	RedBlackTreeNode y = x;

	if (x == null)
	    throw new NoSuchElementException("RedBlackTreeEnumerator");
	if (generation != tree.generation()) 
	    throw new NoSuchElementException(
			"RedBlackTreeEnumerator: Tree modified during enumeration");

	x = tree.successor(x);
	return ((keys) ? y.key : y.value);
    }
}

--------------------------- Main.java
/*
 * @(#)Main.java	1.1 95/09/14  
 *
 * Copyright (c) 1995 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. Please refer to the file "copyright.html"
 * for further important copyright and licensing information.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

import java.util.Enumeration;

/**
 * This class exercises a red-black tree.
 *
 * @author	Chuck McManis
 * @version	1.1, 14 Sep 1995
 */

public class Main {
	static String unsortedList[] = {
		"Red",
		"Blue",
		"Violet",
		"Magenta",
		"Cyan",
		"Grey",
		"Purple",
		"Chartruese",
		"Pink",
		"Orange",
		"Yellow",
		"White",
		"Green",
		"Mauve",
		"Periwinkle",
		"Maroon",
		"Gold",
		"Silver",
		"Bronze",
		"Black" };

    static void remove(String name, RedBlackTree tree) {
	System.out.println("Remove : "+ name);
	tree.remove(name);
	tree.printTree();
	System.out.println("=======================");
    }
	
	
    public static void main(String args[]) {
	RedBlackTree tree = new RedBlackTree();

	for (int i = 0; i < unsortedList.length; i++) {
	    tree.put(unsortedList[i], unsortedList[i]);
	}
	System.out.println("Final tree: ");
	tree.printTree();
	System.out.println("=========================");
	System.out.println("Enumerated list:");
	int q = 1;
	for (Enumeration e = tree.keys(); e.hasMoreElements(); q++)
	    System.out.println("["+ q +"] - "+e.nextElement());
	System.out.println("-------------------------");

	
	System.out.println("Removing nodes from the tree:.\n");
	Main.remove("Cyan", tree);
	Main.remove("Black", tree);
	Main.remove("Blue", tree);
	Main.remove("Gold", tree);
	Main.remove("Green", tree);
	Main.remove("Chartruese", tree);
    }
}
--------------------------- end
-
Note to Sun employees: this is an EXTERNAL mailing list!
Info: send 'help' to java-interest-request@java.sun.com

home help back first fref pref prev next nref lref last post