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 Kwan 4 months ago
parent
commit
08713b6f71
  1. 26
      TerryModi/IntComparable.java
  2. 38
      TerryModi/Main.java
  3. 369
      TerryModi/RBTree.java

26
TerryModi/IntComparable.java

@ -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);
}
}

38
TerryModi/Main.java

@ -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
// ...
}
}

369
TerryModi/RBTree.java

@ -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");
}
}
*/
}
Loading…
Cancel
Save