|
|
// RBTree.java
import java.io.FileWriter; import java.io.IOException;
class RBTree<T extends Comparable<T>> {
private static final boolean RED = true; private static final boolean BLACK = false; private static int step = 0;
private class Node { T key; Node left, right, parent; // Elternknoten hinzugefügt
boolean color;
Node(T key, boolean color) { this.key = key; this.color = color; } }
private Node root;
private Node getUncle(Node parent) { Node grandparent = parent.parent;
if (grandparent.left == parent) { return grandparent.right; // right is the uncle
} else if (grandparent.right == parent) { return grandparent.left; // left is the uncle
} else { throw new IllegalStateException("Parent is not a child of its grandparent"); } }
public void insert(T key) { root = insert(root, null, key); // Parent für die Wurzel ist null
root.color = BLACK; // Ensure the root is always black
}
private Node insert(Node root, Node parent, T key) { Node node = root; // Node parent = null;
// Traverse the tree to the left or right depending on the key
while (node != null) { parent = node; if (key.compareTo(node.key) < 0) { node = node.left; } else if (key.compareTo(node.key) > 0) { node = node.right; } else { throw new IllegalArgumentException("BST already contains a node with key " + key); } } // Insert new node
Node newNode = new Node(key, RED); if (parent == null) { root = newNode; } else if (key.compareTo(parent.key) < 0) { parent.left = newNode; } else { parent.right = newNode; } newNode.parent = parent; fixColorafterInsert(newNode); return newNode; }
/* * Fix any Red-Black tree violations * if (isRed(root.right) && !isRed(root.left)) { * root = rotateLeft(root); * } * if (isRed(root.left) && isRed(root.left.left)) { * root = rotateRight(root); * } * if (isRed(root.left) && isRed(root.right)) { * flipColors(root); * } */
private void fixColorafterInsert(Node node) { Node parent = node.parent;
// Case 1: no root, add node as new root, root is always black
if (parent == null) { node.color = false; return; }
// Parent is black --> nothing to do
if (parent.color == false) { // Abbildung S.312, aber beide Knoten sind schon schwarz gefärbt
return; }
// From here on, parent(vater) is red
Node grandparent = parent.parent;
Node uncle = getUncle(parent); // Get the uncle (maybe nil, in which case its color is BLACK)
// Case 3 (parent uncle red, Abbildung S.313, 314): recolor parent, grandparent
// and uncle
if (uncle != null && uncle.color == true) { parent.color = false; uncle.color = false; grandparent.color = true;
// Call recursively for grandparent, which is now red. fix recursively
fixColorafterInsert(grandparent); }
// Parent is left child of grandparent
// Case 4a: Uncle is black(nil is default black) and node is left --> right
// "inner child" of its grandparent
else if (parent == grandparent.left) {
Node newParent;
if (node == parent.right) { newParent = rotateLeft(parent); parent = newParent;
// Let "parent" point to the new root node of the rotated sub-tree.
// It will be recolored in the next step, which we're going to fall-through to.
// parent = node;
}
// Case 5a: Uncle is black and node is left->left "outer child" of its
// grandparent
rotateRight(grandparent);
// Recolor original parent and grandparent
parent.color = false; grandparent.color = true; }
// Parent is right child of grandparent
else if (parent == grandparent.right) { // Case 4b: Uncle is black and node is right->left "inner child" of its
// grandparent
if (node == parent.left) { rotateRight(parent);
// Let "parent" point to the new root node of the rotated sub-tree.
// It will be recolored in the next step, which we're going to fall-through to.
parent = node; }
// Case 5b: Uncle is black and node is right->right "outer child" of its
// grandparent
rotateLeft(grandparent);
// Recolor original parent and grandparent
parent.color = false; grandparent.color = true; } }
private boolean isRed(Node node) { if (node == null) { return false; } return node.color == RED; }
private Node rotateLeft(Node node) { Node rightChild = node.right; Node parent = node.parent;
node.right = rightChild.left; if (rightChild.left != null) { rightChild.left.parent = node; }
rightChild.left = node; node.parent = rightChild;
rightChild.parent = parent; if (parent == null) { root = rightChild; } else if (node == parent.left) { parent.left = rightChild; } else if (node == parent.right) { parent.right = rightChild; } else { throw new IllegalStateException("Node is not a child of its parent"); }
if (rightChild != null) { rightChild.parent = parent; }
RootBlack();
return rightChild; }
private Node rotateRight(Node node) { Node leftChild = node.left; Node parent = node.parent;
node.left = leftChild.right; if (leftChild.right != null) { leftChild.right.parent = node; }
leftChild.right = node; node.parent = leftChild;
leftChild.parent = parent; if (parent == null) { root = leftChild; } else if (node == parent.left) { parent.left = leftChild; } else if (node == parent.right) { parent.right = leftChild; } else { throw new IllegalStateException("Node is not a child of its parent"); }
if (leftChild != null) { leftChild.parent = parent; }
RootBlack();
return leftChild; }
private void flipColors(Node node) { node.color = !node.color; node.left.color = !node.left.color; node.right.color = !node.right.color; }
private void RootBlack() { root.color = BLACK; }
/* * public void printDOTAfterInsert(String filename, T key) { * try (FileWriter writer = new FileWriter(filename)) { * writer.write("digraph G {\n"); * writer.write("\tnode [style=filled, color=black, shape=circle, width=.6,\n"); * writer.write("\t\tfontname=Helvetica, fontweight=bold, fontcolor=white,\n"); * writer.write("\t\tfontsize=24, fixedsize=true];\n"); * * root = insert(root, null, key); // Füge das Element ein und aktualisiere den
* Baum * printDOTRecursive(root, writer); * * writer.write("}\n"); * } catch (IOException e) { * e.printStackTrace(); * } * } */
public void printDOTAfterInsert(String filename, T key) { try (FileWriter writer = new FileWriter(filename)) { writer.write("digraph G {\n"); writer.write("\tnode [style=filled, color=black, shape=circle, width=.6,\n"); writer.write("\t\tfontname=Helvetica, fontweight=bold, fontcolor=white,\n"); writer.write("\t\tfontsize=24, fixedsize=true];\n"); // Hier wird der gesamte Baum gezeichnet, nicht nur der neu eingefügte Schlüssel
printDOTRecursive(root, writer); writer.write("}\n"); } catch (IOException e) { e.printStackTrace(); } } private void printDOTRecursive(Node node, FileWriter writer) throws IOException { if (node != null) { String keyString = node.key.toString().replaceAll("[^a-zA-Z0-9]", "_"); String fillColor = (node.color == RED) ? "red" : "black"; String fontColor = (node.color == RED) ? "white" : "white"; writer.write("\t\"" + keyString + "\" [fillcolor=" + fillColor + ", fontcolor=" + fontColor + "];\n"); // Print parent link
if (node.parent != null) { String parentKeyString = node.parent.key.toString().replaceAll("[^a-zA-Z0-9]", "_"); writer.write("\t\"" + parentKeyString + "\" -> \"" + keyString + "\" [style=dotted];\n"); } // Print left child
if (node.left != null) { String leftKeyString = node.left.key.toString().replaceAll("[^a-zA-Z0-9]", "_"); writer.write("\t\"" + keyString + "\" -> \"" + leftKeyString + "\";\n"); printDOTRecursive(node.left, writer); } else { String leftNilKeyString = keyString + "_NIL_L"; writer.write("\t\"" + leftNilKeyString + "\" [shape=plaintext, label=\"NIL\", fontsize=16];\n"); writer.write("\t\"" + keyString + "\" -> \"" + leftNilKeyString + "\";\n"); } // Print right child
if (node.right != null) { String rightKeyString = node.right.key.toString().replaceAll("[^a-zA-Z0-9]", "_"); writer.write("\t\"" + keyString + "\" -> \"" + rightKeyString + "\";\n"); printDOTRecursive(node.right, writer); } else { String rightNilKeyString = keyString + "_NIL_R"; writer.write("\t\"" + rightNilKeyString + "\" [shape=plaintext, label=\"NIL\", fontsize=16];\n"); writer.write("\t\"" + keyString + "\" -> \"" + rightNilKeyString + "\";\n"); } } }
/*public void printDOTAfterInsert(String filename, T key) { try (FileWriter writer = new FileWriter(filename)) { writer.write("digraph G {\n"); writer.write("\tnode [style=filled, color=black, shape=circle, width=.6,\n"); writer.write("\t\tfontname=Helvetica, fontweight=bold, fontcolor=white,\n"); writer.write("\t\tfontsize=24, fixedsize=true];\n");
// Hier wird der gesamte Baum gezeichnet, nicht nur der neu eingefügte Schlüssel
printDOTRecursive(root, writer);
writer.write("}\n"); } catch (IOException e) { e.printStackTrace(); } }
private void printDOTRecursive(Node node, FileWriter writer) throws IOException { if (node != null) { String keyString = node.key.toString().replaceAll("[^a-zA-Z0-9]", "_"); String fillColor = (node.color == RED) ? "red" : "black"; String fontColor = (node.color == RED) ? "white" : "white";
writer.write("\t\"" + keyString + "\" [fillcolor=" + fillColor + ", fontcolor=" + fontColor + "];\n");
/* * Print parent link * if (node.parent != null) { * String parentKeyString = * node.parent.key.toString().replaceAll("[^a-zA-Z0-9]", "_"); * writer.write("\t\"" + parentKeyString + "\" -> \"" + keyString + * "\" [style=dotted];\n"); * }
// Print left child
if (node.left != null) { String leftKeyString = node.left.key.toString().replaceAll("[^a-zA-Z0-9]", "_"); writer.write("\t\"" + keyString + "\" -> \"" + leftKeyString + "\";\n"); printDOTRecursive(node.left, writer); } else { String leftNilKeyString = keyString + "_NIL_L"; writer.write("\t\"" + leftNilKeyString + "\" [shape=plaintext, label=\"NIL\", fontsize=16];\n"); writer.write("\t\"" + keyString + "\" -> \"" + leftNilKeyString + "\";\n"); }
// Print right child
if (node.right != null) { String rightKeyString = node.right.key.toString().replaceAll("[^a-zA-Z0-9]", "_"); writer.write("\t\"" + keyString + "\" -> \"" + rightKeyString + "\";\n"); printDOTRecursive(node.right, writer); } else { String rightNilKeyString = keyString + "_NIL_R"; writer.write("\t\"" + rightNilKeyString + "\" [shape=plaintext, label=\"NIL\", fontsize=16];\n"); writer.write("\t\"" + keyString + "\" -> \"" + rightNilKeyString + "\";\n"); } } */ }
|