You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

344 lines
13 KiB

  1. import java.io.FileWriter;
  2. import java.io.IOException;
  3. //
  4. class RBTree<T extends Comparable<T>> {
  5. private static final boolean RED = true;
  6. private static final boolean BLACK = false;
  7. private class Node {
  8. T key; // Node<Integer>, generischer Datentyp, der Schlüssel (key) des Knotens vom Typ Integer ist.
  9. Node left, right, parent; // Elternknoten hinzugefügt
  10. boolean color;
  11. public Node(T key) { // Konstruktor von Node-Class
  12. this.key = key;
  13. left = right = parent = null;
  14. this.color = true; // Neue Knoten sind standardmäßig rot
  15. }
  16. }
  17. private Node root;
  18. public void insertNode(T key) { // hier nicht int-DT, sondern T,
  19. // mit jedem vergleichbaren Datentyp (T)
  20. // verwendet werden kann, solange dieser das Comparable-Interface implementiert.
  21. Node node = root;
  22. Node parent = null;
  23. // Traverse the tree to the left or right depending on the key,
  24. // finding a correct position for the insertion of nodes later + keep the structure of the tree
  25. while (node != null) {
  26. parent = node;
  27. if (key.compareTo(node.key) < 0) { // key < node.key
  28. node = node.left;
  29. } else if (key.compareTo(node.key) > 0) { // key > node.key
  30. node = node.right;
  31. } else {
  32. throw new IllegalArgumentException("BST already contains a node with key " + key);
  33. }
  34. }
  35. // Insert new node
  36. Node newNode = new Node(key);
  37. newNode.color = true; // neuer Knoten immer rot
  38. if (parent == null) {
  39. root = newNode;
  40. } else if (key.compareTo(parent.key) < 0) { // key < parent.key
  41. parent.left = newNode;
  42. } else if (key.compareTo(parent.key) > 0){ // key > parent.key
  43. parent.right = newNode;
  44. }
  45. newNode.parent = parent; // etablieren die Verbindung des Knotens zu seinem Vater
  46. fixAfterInsert(newNode);
  47. }
  48. private Node getUncle(Node parent) { // für spätere Implementierung fix()
  49. Node grandparent = parent.parent;
  50. if (grandparent.left == parent) {
  51. return grandparent.right; // right is the uncle
  52. } else if (grandparent.right == parent) {
  53. return grandparent.left; // left is the uncle
  54. } else {
  55. throw new IllegalStateException("Parent is not a child of its grandparent");
  56. }
  57. }
  58. private void fixAfterInsert(Node node) {
  59. Node parent = node.parent;
  60. // Case 1: no root, add node as new root, root is always black
  61. if (parent == null) {
  62. node.color = false;
  63. return;
  64. }
  65. // Parent is black --> nothing to do
  66. if (parent.color == false) { // Abbildung S.312, aber beide Knoten sind schon schwarz gefärbt
  67. return;
  68. }
  69. // From here on, parent(vater) is red
  70. Node grandparent = parent.parent;
  71. Node uncle = getUncle(parent); // Get the uncle (maybe nil, in which case its color is BLACK)
  72. // Case 3 (parent uncle red, Abbildung S.313, 314): recolor parent, grandparent
  73. // and uncle
  74. if (uncle != null && uncle.color == true) {
  75. parent.color = false;
  76. uncle.color = false;
  77. grandparent.color = true;
  78. // Call recursively for grandparent, which is now red. fix recursively
  79. fixAfterInsert(grandparent);
  80. }
  81. // Parent is left child of grandparent
  82. // Case 4a: Uncle is black(nil is default black) and node is left --> right
  83. // "inner child" of its grandparent
  84. else if (parent == grandparent.left) {
  85. if (node == parent.right) {
  86. rotateLeft(parent);
  87. // Let "parent" point to the new root node of the rotated sub-tree.
  88. // It will be recolored in the next step, which we're going to fall-through to.
  89. parent = node;
  90. }
  91. // Case 5a: Uncle is black and node is left->left "outer child" of its grandparent
  92. rotateRight(grandparent);
  93. // Recolor original parent and grandparent
  94. parent.color = false;
  95. grandparent.color = true;
  96. }
  97. // Parent is right child of grandparent
  98. else if (parent == grandparent.right) {
  99. // Case 4b: Uncle is black and node is right->left "inner child" of its
  100. // grandparent
  101. if (node == parent.left) {
  102. rotateRight(parent);
  103. // Let "parent" point to the new root node of the rotated sub-tree.
  104. // It will be recolored in the next step, which we're going to fall-through to.
  105. parent = node;
  106. }
  107. // Case 5b: Uncle is black and node is right->right "outer child" of its
  108. // grandparent
  109. rotateLeft(grandparent);
  110. // Recolor original parent and grandparent
  111. parent.color = false;
  112. grandparent.color = true;
  113. }
  114. }
  115. private Node rotateLeft(Node node) {
  116. Node rightChild = node.right;
  117. Node parent = node.parent;
  118. node.right = rightChild.left;
  119. if (rightChild.left != null) {
  120. rightChild.left.parent = node;
  121. }
  122. rightChild.left = node;
  123. node.parent = rightChild;
  124. rightChild.parent = parent;
  125. if (parent == null) {
  126. root = rightChild;
  127. } else if (node == parent.left) {
  128. parent.left = rightChild;
  129. } else if (node == parent.right) {
  130. parent.right = rightChild;
  131. } else {
  132. throw new IllegalStateException("Node is not a child of its parent");
  133. }
  134. RootBlack();
  135. return rightChild;
  136. }
  137. private Node rotateRight(Node node) {
  138. Node leftChild = node.left;
  139. Node parent = node.parent;
  140. node.left = leftChild.right;
  141. if (leftChild.right != null) {
  142. leftChild.right.parent = node;
  143. }
  144. leftChild.right = node;
  145. node.parent = leftChild;
  146. leftChild.parent = parent;
  147. if (parent == null) {
  148. root = leftChild;
  149. } else if (node == parent.left) {
  150. parent.left = leftChild;
  151. } else if (node == parent.right) {
  152. parent.right = leftChild;
  153. } else {
  154. throw new IllegalStateException("Node is not a child of its parent");
  155. }
  156. RootBlack();
  157. return leftChild;
  158. }
  159. /* private void flipColors(Node node) {
  160. node.color = !node.color;
  161. node.left.color = !node.left.color;
  162. node.right.color = !node.right.color;
  163. } */
  164. private void RootBlack() {
  165. root.color = BLACK;
  166. }
  167. /*
  168. * public void printDOTAfterInsert(String filename, T key) {
  169. * try (FileWriter writer = new FileWriter(filename)) {
  170. * writer.write("digraph G {\n");
  171. * writer.write("\tnode [style=filled, color=black, shape=circle, width=.6,\n");
  172. * writer.write("\t\tfontname=Helvetica, fontweight=bold, fontcolor=white,\n");
  173. * writer.write("\t\tfontsize=24, fixedsize=true];\n");
  174. *
  175. * root = insert(root, null, key); // Füge das Element ein und aktualisiere den
  176. * Baum
  177. * printDOTRecursive(root, writer);
  178. *
  179. * writer.write("}\n");
  180. * } catch (IOException e) {
  181. * e.printStackTrace();
  182. * }
  183. * }
  184. */
  185. public void printDOTAfterInsert(String filename) {
  186. try (FileWriter writer = new FileWriter(filename)) {
  187. writer.write("digraph G {\n");
  188. writer.write("\tnode [style=filled, color=black, shape=circle, width=.6,\n");
  189. writer.write("\t\tfontname=Helvetica, fontweight=bold, fontcolor=white,\n");
  190. writer.write("\t\tfontsize=24, fixedsize=true];\n");
  191. // Hier wird der gesamte Baum gezeichnet, nicht nur der neu eingefügte Schlüssel
  192. printDOTRecursive(root, writer);
  193. writer.write("}\n");
  194. } catch (IOException e) {
  195. e.printStackTrace();
  196. }
  197. }
  198. private void printDOTRecursive(Node node, FileWriter writer) throws IOException {
  199. if (node != null) {
  200. String keyString = node.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  201. String fillColor = (node.color == RED) ? "red" : "black";
  202. String fontColor = (node.color == RED) ? "white" : "white";
  203. writer.write("\t\"" + keyString + "\" [fillcolor=" + fillColor + ", fontcolor=" + fontColor + "];\n");
  204. // Print parent link
  205. /* if (node.parent != null) {
  206. String parentKeyString = node.parent.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  207. writer.write("\t\"" + parentKeyString + "\" -> \"" + keyString + "\" [style=dotted];\n");
  208. } */
  209. // Print left child
  210. if (node.left != null) {
  211. String leftKeyString = node.left.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  212. writer.write("\t\"" + keyString + "\" -> \"" + leftKeyString + "\";\n");
  213. printDOTRecursive(node.left, writer);
  214. } else {
  215. String leftNilKeyString = keyString + "_NIL_L";
  216. writer.write("\t\"" + leftNilKeyString + "\" [shape=plaintext, label=\"NIL\", fontsize=16];\n");
  217. writer.write("\t\"" + keyString + "\" -> \"" + leftNilKeyString + "\";\n");
  218. }
  219. // Print right child
  220. if (node.right != null) {
  221. String rightKeyString = node.right.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  222. writer.write("\t\"" + keyString + "\" -> \"" + rightKeyString + "\";\n");
  223. printDOTRecursive(node.right, writer);
  224. } else {
  225. String rightNilKeyString = keyString + "_NIL_R";
  226. writer.write("\t\"" + rightNilKeyString + "\" [shape=plaintext, label=\"NIL\", fontsize=16];\n");
  227. writer.write("\t\"" + keyString + "\" -> \"" + rightNilKeyString + "\";\n");
  228. }
  229. }
  230. }
  231. /*public void printDOTAfterInsert(String filename, T key) {
  232. try (FileWriter writer = new FileWriter(filename)) {
  233. writer.write("digraph G {\n");
  234. writer.write("\tnode [style=filled, color=black, shape=circle, width=.6,\n");
  235. writer.write("\t\tfontname=Helvetica, fontweight=bold, fontcolor=white,\n");
  236. writer.write("\t\tfontsize=24, fixedsize=true];\n");
  237. // Hier wird der gesamte Baum gezeichnet, nicht nur der neu eingefügte Schlüssel
  238. printDOTRecursive(root, writer);
  239. writer.write("}\n");
  240. } catch (IOException e) {
  241. e.printStackTrace();
  242. }
  243. }
  244. private void printDOTRecursive(Node node, FileWriter writer) throws IOException {
  245. if (node != null) {
  246. String keyString = node.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  247. String fillColor = (node.color == RED) ? "red" : "black";
  248. String fontColor = (node.color == RED) ? "white" : "white";
  249. writer.write("\t\"" + keyString + "\" [fillcolor=" + fillColor + ", fontcolor=" + fontColor + "];\n");
  250. /*
  251. * Print parent link
  252. * if (node.parent != null) {
  253. * String parentKeyString =
  254. * node.parent.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  255. * writer.write("\t\"" + parentKeyString + "\" -> \"" + keyString +
  256. * "\" [style=dotted];\n");
  257. * }
  258. // Print left child
  259. if (node.left != null) {
  260. String leftKeyString = node.left.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  261. writer.write("\t\"" + keyString + "\" -> \"" + leftKeyString + "\";\n");
  262. printDOTRecursive(node.left, writer);
  263. } else {
  264. String leftNilKeyString = keyString + "_NIL_L";
  265. writer.write("\t\"" + leftNilKeyString + "\" [shape=plaintext, label=\"NIL\", fontsize=16];\n");
  266. writer.write("\t\"" + keyString + "\" -> \"" + leftNilKeyString + "\";\n");
  267. }
  268. // Print right child
  269. if (node.right != null) {
  270. String rightKeyString = node.right.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  271. writer.write("\t\"" + keyString + "\" -> \"" + rightKeyString + "\";\n");
  272. printDOTRecursive(node.right, writer);
  273. } else {
  274. String rightNilKeyString = keyString + "_NIL_R";
  275. writer.write("\t\"" + rightNilKeyString + "\" [shape=plaintext, label=\"NIL\", fontsize=16];\n");
  276. writer.write("\t\"" + keyString + "\" -> \"" + rightNilKeyString + "\";\n");
  277. }
  278. }
  279. */
  280. }