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.

351 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 postion 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. if (rightChild != null) {
  135. rightChild.parent = parent;
  136. }
  137. RootBlack();
  138. return rightChild;
  139. }
  140. private Node rotateRight(Node node) {
  141. Node leftChild = node.left;
  142. Node parent = node.parent;
  143. node.left = leftChild.right;
  144. if (leftChild.right != null) {
  145. leftChild.right.parent = node;
  146. }
  147. leftChild.right = node;
  148. node.parent = leftChild;
  149. leftChild.parent = parent;
  150. if (parent == null) {
  151. root = leftChild;
  152. } else if (node == parent.left) {
  153. parent.left = leftChild;
  154. } else if (node == parent.right) {
  155. parent.right = leftChild;
  156. } else {
  157. throw new IllegalStateException("Node is not a child of its parent");
  158. }
  159. if (leftChild != null) {
  160. leftChild.parent = parent;
  161. }
  162. RootBlack();
  163. return leftChild;
  164. }
  165. /* private void flipColors(Node node) {
  166. node.color = !node.color;
  167. node.left.color = !node.left.color;
  168. node.right.color = !node.right.color;
  169. } */
  170. private void RootBlack() {
  171. root.color = BLACK;
  172. }
  173. /*
  174. * public void printDOTAfterInsert(String filename, T key) {
  175. * try (FileWriter writer = new FileWriter(filename)) {
  176. * writer.write("digraph G {\n");
  177. * writer.write("\tnode [style=filled, color=black, shape=circle, width=.6,\n");
  178. * writer.write("\t\tfontname=Helvetica, fontweight=bold, fontcolor=white,\n");
  179. * writer.write("\t\tfontsize=24, fixedsize=true];\n");
  180. *
  181. * root = insert(root, null, key); // Füge das Element ein und aktualisiere den
  182. * Baum
  183. * printDOTRecursive(root, writer);
  184. *
  185. * writer.write("}\n");
  186. * } catch (IOException e) {
  187. * e.printStackTrace();
  188. * }
  189. * }
  190. */
  191. public void printDOTAfterInsert(String filename) {
  192. try (FileWriter writer = new FileWriter(filename)) {
  193. writer.write("digraph G {\n");
  194. writer.write("\tnode [style=filled, color=black, shape=circle, width=.6,\n");
  195. writer.write("\t\tfontname=Helvetica, fontweight=bold, fontcolor=white,\n");
  196. writer.write("\t\tfontsize=24, fixedsize=true];\n");
  197. // Hier wird der gesamte Baum gezeichnet, nicht nur der neu eingefügte Schlüssel
  198. printDOTRecursive(root, writer);
  199. writer.write("}\n");
  200. } catch (IOException e) {
  201. e.printStackTrace();
  202. }
  203. }
  204. private void printDOTRecursive(Node node, FileWriter writer) throws IOException {
  205. if (node != null) {
  206. String keyString = node.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  207. String fillColor = (node.color == RED) ? "red" : "black";
  208. String fontColor = (node.color == RED) ? "white" : "white";
  209. writer.write("\t\"" + keyString + "\" [fillcolor=" + fillColor + ", fontcolor=" + fontColor + "];\n");
  210. // Print parent link
  211. /* if (node.parent != null) {
  212. String parentKeyString = node.parent.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  213. writer.write("\t\"" + parentKeyString + "\" -> \"" + keyString + "\" [style=dotted];\n");
  214. } */
  215. // Print left child
  216. if (node.left != null) {
  217. String leftKeyString = node.left.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  218. writer.write("\t\"" + keyString + "\" -> \"" + leftKeyString + "\";\n");
  219. printDOTRecursive(node.left, writer);
  220. } else {
  221. String leftNilKeyString = keyString + "_NIL_L";
  222. writer.write("\t\"" + leftNilKeyString + "\" [shape=plaintext, label=\"NIL\", fontsize=16];\n");
  223. writer.write("\t\"" + keyString + "\" -> \"" + leftNilKeyString + "\";\n");
  224. }
  225. // Print right child
  226. if (node.right != null) {
  227. String rightKeyString = node.right.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  228. writer.write("\t\"" + keyString + "\" -> \"" + rightKeyString + "\";\n");
  229. printDOTRecursive(node.right, writer);
  230. } else {
  231. String rightNilKeyString = keyString + "_NIL_R";
  232. writer.write("\t\"" + rightNilKeyString + "\" [shape=plaintext, label=\"NIL\", fontsize=16];\n");
  233. writer.write("\t\"" + keyString + "\" -> \"" + rightNilKeyString + "\";\n");
  234. }
  235. }
  236. }
  237. /*public void printDOTAfterInsert(String filename, T key) {
  238. try (FileWriter writer = new FileWriter(filename)) {
  239. writer.write("digraph G {\n");
  240. writer.write("\tnode [style=filled, color=black, shape=circle, width=.6,\n");
  241. writer.write("\t\tfontname=Helvetica, fontweight=bold, fontcolor=white,\n");
  242. writer.write("\t\tfontsize=24, fixedsize=true];\n");
  243. // Hier wird der gesamte Baum gezeichnet, nicht nur der neu eingefügte Schlüssel
  244. printDOTRecursive(root, writer);
  245. writer.write("}\n");
  246. } catch (IOException e) {
  247. e.printStackTrace();
  248. }
  249. }
  250. private void printDOTRecursive(Node node, FileWriter writer) throws IOException {
  251. if (node != null) {
  252. String keyString = node.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  253. String fillColor = (node.color == RED) ? "red" : "black";
  254. String fontColor = (node.color == RED) ? "white" : "white";
  255. writer.write("\t\"" + keyString + "\" [fillcolor=" + fillColor + ", fontcolor=" + fontColor + "];\n");
  256. /*
  257. * Print parent link
  258. * if (node.parent != null) {
  259. * String parentKeyString =
  260. * node.parent.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  261. * writer.write("\t\"" + parentKeyString + "\" -> \"" + keyString +
  262. * "\" [style=dotted];\n");
  263. * }
  264. // Print left child
  265. if (node.left != null) {
  266. String leftKeyString = node.left.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  267. writer.write("\t\"" + keyString + "\" -> \"" + leftKeyString + "\";\n");
  268. printDOTRecursive(node.left, writer);
  269. } else {
  270. String leftNilKeyString = keyString + "_NIL_L";
  271. writer.write("\t\"" + leftNilKeyString + "\" [shape=plaintext, label=\"NIL\", fontsize=16];\n");
  272. writer.write("\t\"" + keyString + "\" -> \"" + leftNilKeyString + "\";\n");
  273. }
  274. // Print right child
  275. if (node.right != null) {
  276. String rightKeyString = node.right.key.toString().replaceAll("[^a-zA-Z0-9]", "_");
  277. writer.write("\t\"" + keyString + "\" -> \"" + rightKeyString + "\";\n");
  278. printDOTRecursive(node.right, writer);
  279. } else {
  280. String rightNilKeyString = keyString + "_NIL_R";
  281. writer.write("\t\"" + rightNilKeyString + "\" [shape=plaintext, label=\"NIL\", fontsize=16];\n");
  282. writer.write("\t\"" + keyString + "\" -> \"" + rightNilKeyString + "\";\n");
  283. }
  284. }
  285. */
  286. }