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.

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