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.

374 lines
13 KiB

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