From 08713b6f710d6e9502924daabe0215fc09ec9e2e Mon Sep 17 00:00:00 2001 From: Terry Kwan Date: Tue, 16 Jan 2024 18:08:39 +0100 Subject: [PATCH] Terry: code modification on top of Pauls Code (Main, Wrapper and RBTree classes. now it works but only for every node an arrow extra ) --- TerryModi/IntComparable.java | 26 +++ TerryModi/Main.java | 38 ++++ TerryModi/RBTree.java | 369 +++++++++++++++++++++++++++++++++++ 3 files changed, 433 insertions(+) create mode 100644 TerryModi/IntComparable.java create mode 100644 TerryModi/Main.java create mode 100644 TerryModi/RBTree.java diff --git a/TerryModi/IntComparable.java b/TerryModi/IntComparable.java new file mode 100644 index 0000000..d71c176 --- /dev/null +++ b/TerryModi/IntComparable.java @@ -0,0 +1,26 @@ +class IntComparable implements Comparable { + 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); + } +} \ No newline at end of file diff --git a/TerryModi/Main.java b/TerryModi/Main.java new file mode 100644 index 0000000..c45a4a8 --- /dev/null +++ b/TerryModi/Main.java @@ -0,0 +1,38 @@ +import java.util.*; + + public class Main { + public static void main(String[] args) { + + Random random = new Random(); + RBTree rbTree = new RBTree<>(); + ArrayList 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 + // ... + } + } + + diff --git a/TerryModi/RBTree.java b/TerryModi/RBTree.java new file mode 100644 index 0000000..59004fc --- /dev/null +++ b/TerryModi/RBTree.java @@ -0,0 +1,369 @@ +import java.io.FileWriter; +import java.io.IOException; +// +class RBTree> { + + 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"); + } + } + */ +}