Browse Source
Terry: code modification on top of Pauls Code (Main, Wrapper and RBTree classes. now it works but only for every node an arrow extra )
master
Terry: code modification on top of Pauls Code (Main, Wrapper and RBTree classes. now it works but only for every node an arrow extra )
master
Terry Kwan
10 months ago
3 changed files with 433 additions and 0 deletions
@ -0,0 +1,26 @@ |
|||||
|
class IntComparable implements Comparable<IntComparable> { |
||||
|
private int value; |
||||
|
|
||||
|
IntComparable(int value) { |
||||
|
this.value = value; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public int compareTo(IntComparable other) { |
||||
|
return Integer.compare(this.value, other.value); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
// Terry newly added |
||||
|
public int getValue() { |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
// Verwenden Sie eine geeignete String-Darstellung, zum Beispiel den Wert selbst |
||||
|
return Integer.toString(value); |
||||
|
} |
||||
|
} |
@ -0,0 +1,38 @@ |
|||||
|
import java.util.*; |
||||
|
|
||||
|
public class Main { |
||||
|
public static void main(String[] args) { |
||||
|
|
||||
|
Random random = new Random(); |
||||
|
RBTree<IntComparable> rbTree = new RBTree<>(); |
||||
|
ArrayList<Integer> numbers = new ArrayList<>(); |
||||
|
|
||||
|
// ein eingebauter Mechanismus, der sicherstellt, dass eine Zahl nicht 2-mal vorkommt, sonst würde der Baum nicht funktionieren |
||||
|
// Füllen der Liste mit Zahlen |
||||
|
for (int j = 1; j <= 100; j++) { |
||||
|
numbers.add(j); |
||||
|
} |
||||
|
|
||||
|
// Terry newly added |
||||
|
Collections.shuffle(numbers); // Liste mischen |
||||
|
|
||||
|
for (int i = 0; i < 15; i++) { |
||||
|
|
||||
|
int pickedNumber = numbers.remove(0); // Entfernt das Element an dem "Index", return es und speichert es in 'pickedNumber' |
||||
|
IntComparable randomNum = new IntComparable(pickedNumber); |
||||
|
|
||||
|
System.out.println("Einfügen: " + randomNum.getValue()); |
||||
|
rbTree.insertNode(randomNum); |
||||
|
|
||||
|
// Füge das Element ein und aktualisiere die DOT-Datei nach jedem Schritt |
||||
|
rbTree.printDOTAfterInsert("output_step_" + i + ".dot"); |
||||
|
} |
||||
|
|
||||
|
// Jetzt können Sie die DOT-Dateien in SVG umwandeln und anzeigen |
||||
|
// dot -Tsvg output_step_0.dot > output_step_0.svg |
||||
|
// dot -Tsvg output_step_1.dot > output_step_1.svg |
||||
|
// ... |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
@ -0,0 +1,369 @@ |
|||||
|
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 class Node { |
||||
|
T key; |
||||
|
Node left, right, parent; // Elternknoten hinzugefügt |
||||
|
boolean color; |
||||
|
|
||||
|
public Node(T key) { // Konstruktor von Node-Class |
||||
|
this.key = key; |
||||
|
left = right = parent = null; |
||||
|
this.color = true; // Neue Knoten sind standardmäßig rot |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private Node root; |
||||
|
|
||||
|
|
||||
|
public void insertNode(T key) { // hier nicht int-DT, sondern T, |
||||
|
// mit jedem vergleichbaren Datentyp (T) |
||||
|
// verwendet werden kann, solange dieser das Comparable-Interface implementiert. |
||||
|
|
||||
|
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) { // key < node.key |
||||
|
node = node.left; |
||||
|
} else if (key.compareTo(node.key) > 0) { // key > node.key |
||||
|
node = node.right; |
||||
|
} else { |
||||
|
throw new IllegalArgumentException("BST already contains a node with key " + key); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Insert new node |
||||
|
Node newNode = new Node(key); |
||||
|
newNode.color = true; // neuer Knoten immer rot |
||||
|
if (parent == null) { |
||||
|
root = newNode; |
||||
|
} else if (key.compareTo(parent.key) < 0) { // key < parent.key |
||||
|
parent.left = newNode; |
||||
|
} else { // key > parent.key |
||||
|
parent.right = newNode; |
||||
|
} |
||||
|
newNode.parent = parent; |
||||
|
fixColorafterInsert(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 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"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
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) { |
||||
|
|
||||
|
if (node == parent.right) { |
||||
|
rotateLeft(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 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) { |
||||
|
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"); |
||||
|
} |
||||
|
} |
||||
|
*/ |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue