Libraryless. Click here for Pure Java version (3335L/20K).
1 | // based on: https://algs4.cs.princeton.edu/33balanced/RedBlackBST.java.html |
2 | // TODO: implement NavigableSet |
3 | sclass CompactTreeSet<A> extends AbstractSet<A> { |
4 | |
5 | // A symbol table implemented using a left-leaning red-black BST. |
6 | // This is the 2-3 version. |
7 | |
8 | private static final boolean RED = true; |
9 | private static final boolean BLACK = false; |
10 | |
11 | Comparator<A> comparator; // optional |
12 | private Node<A> root; // root of the BST |
13 | int size; // size of tree set |
14 | |
15 | // BST helper node data type |
16 | private sclass Node<A> { |
17 | private A val; // associated data |
18 | private Node<A> left, right; // links to left and right subtrees |
19 | private boolean color; // color of parent link |
20 | |
21 | public Node(A val, boolean color) { |
22 | this.val = val; |
23 | this.color = color; |
24 | } |
25 | } |
26 | |
27 | *() {} |
28 | *(Comparator<A> *comparator) {} |
29 | *(Cl<? extends A> cl) { addAll(cl); } |
30 | |
31 | // is node x red; false if x is null ? |
32 | private boolean isRed(Node<A> x) { |
33 | if (x == null) return false; |
34 | return x.color == RED; |
35 | } |
36 | |
37 | public int size() { |
38 | ret size; |
39 | } |
40 | |
41 | public boolean isEmpty() { |
42 | return root == null; |
43 | } |
44 | |
45 | public bool add(A val) { |
46 | int oldSize = size; |
47 | root = put(root, val); |
48 | root.color = BLACK; |
49 | ifdef CompactTreeSet_debug assertTrue(check()); endifdef |
50 | ret size > oldSize; |
51 | } |
52 | |
53 | // insert the value in the subtree rooted at h |
54 | private Node<A> put(Node<A> h, A val) { |
55 | if (h == null) { ++size; ret new Node(val, RED); } |
56 | |
57 | int cmp = compare(val, h.val); |
58 | if (cmp < 0) h.left = put(h.left, val); |
59 | else if (cmp > 0) h.right = put(h.right, val); |
60 | else { /*h.val = val;*/ } // no overwriting |
61 | |
62 | // fix-up any right-leaning links |
63 | if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h); |
64 | if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h); |
65 | if (isRed(h.left) && isRed(h.right)) flipColors(h); |
66 | |
67 | ret h; |
68 | } |
69 | |
70 | int compare(A a, A b) { |
71 | ret comparator != null ? comparator.compare(a, b) : ((Comparable) a).compareTo(b); |
72 | } |
73 | |
74 | public bool remove(O key) { |
75 | if (!contains(key)) false; |
76 | |
77 | // if both children of root are black, set root to red |
78 | if (!isRed(root.left) && !isRed(root.right)) |
79 | root.color = RED; |
80 | |
81 | root = delete(root, (A) key); |
82 | if (!isEmpty()) root.color = BLACK; |
83 | // assert check(); |
84 | true; |
85 | } |
86 | |
87 | // delete the key-value pair with the given key rooted at h |
88 | private Node delete(Node<A> h, A key) { |
89 | // assert get(h, key) != null; |
90 | |
91 | if (compare(key, h.val) < 0) { |
92 | if (!isRed(h.left) && !isRed(h.left.left)) |
93 | h = moveRedLeft(h); |
94 | h.left = delete(h.left, key); |
95 | } |
96 | else { |
97 | if (isRed(h.left)) |
98 | h = rotateRight(h); |
99 | if (compare(key, h.val) == 0 && (h.right == null)) { |
100 | --size; null; |
101 | } if (!isRed(h.right) && !isRed(h.right.left)) |
102 | h = moveRedRight(h); |
103 | if (compare(key, h.val) == 0) { |
104 | --size; |
105 | Node<A> x = min(h.right); |
106 | h.val = x.val; |
107 | // h.val = get(h.right, min(h.right).val); |
108 | // h.val = min(h.right).val; |
109 | h.right = deleteMin(h.right); |
110 | } |
111 | else h.right = delete(h.right, key); |
112 | } |
113 | return balance(h); |
114 | } |
115 | |
116 | // make a left-leaning link lean to the right |
117 | private Node<A> rotateRight(Node<A> h) { |
118 | // assert (h != null) && isRed(h.left); |
119 | Node<A> x = h.left; |
120 | h.left = x.right; |
121 | x.right = h; |
122 | x.color = x.right.color; |
123 | x.right.color = RED; |
124 | return x; |
125 | } |
126 | |
127 | // make a right-leaning link lean to the left |
128 | private Node<A> rotateLeft(Node<A> h) { |
129 | // assert (h != null) && isRed(h.right); |
130 | Node<A> x = h.right; |
131 | h.right = x.left; |
132 | x.left = h; |
133 | x.color = x.left.color; |
134 | x.left.color = RED; |
135 | return x; |
136 | } |
137 | |
138 | // flip the colors of a node and its two children |
139 | private void flipColors(Node<A> h) { |
140 | // h must have opposite color of its two children |
141 | // assert (h != null) && (h.left != null) && (h.right != null); |
142 | // assert (!isRed(h) && isRed(h.left) && isRed(h.right)) |
143 | // || (isRed(h) && !isRed(h.left) && !isRed(h.right)); |
144 | h.color = !h.color; |
145 | h.left.color = !h.left.color; |
146 | h.right.color = !h.right.color; |
147 | } |
148 | |
149 | // Assuming that h is red and both h.left and h.left.left |
150 | // are black, make h.left or one of its children red. |
151 | private Node<A> moveRedLeft(Node<A> h) { |
152 | // assert (h != null); |
153 | // assert isRed(h) && !isRed(h.left) && !isRed(h.left.left); |
154 | |
155 | flipColors(h); |
156 | if (isRed(h.right.left)) { |
157 | h.right = rotateRight(h.right); |
158 | h = rotateLeft(h); |
159 | flipColors(h); |
160 | } |
161 | return h; |
162 | } |
163 | |
164 | // Assuming that h is red and both h.right and h.right.left |
165 | // are black, make h.right or one of its children red. |
166 | private Node<A> moveRedRight(Node<A> h) { |
167 | // assert (h != null); |
168 | // assert isRed(h) && !isRed(h.right) && !isRed(h.right.left); |
169 | flipColors(h); |
170 | if (isRed(h.left.left)) { |
171 | h = rotateRight(h); |
172 | flipColors(h); |
173 | } |
174 | return h; |
175 | } |
176 | |
177 | // restore red-black tree invariant |
178 | private Node<A> balance(Node<A> h) { |
179 | // assert (h != null); |
180 | |
181 | if (isRed(h.right)) h = rotateLeft(h); |
182 | if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h); |
183 | if (isRed(h.left) && isRed(h.right)) flipColors(h); |
184 | |
185 | return h; |
186 | } |
187 | |
188 | |
189 | /** |
190 | * Returns the height of the BST (for debugging). |
191 | * @return the height of the BST (a 1-node tree has height 0) |
192 | */ |
193 | public int height() { |
194 | return height(root); |
195 | } |
196 | |
197 | private int height(Node<A> x) { |
198 | if (x == null) return -1; |
199 | return 1 + Math.max(height(x.left), height(x.right)); |
200 | } |
201 | |
202 | public bool contains(O val) { |
203 | ret find(root, (A) val) != null; |
204 | } |
205 | |
206 | public A find(A probeVal) { |
207 | Node<A> n = find(root, probeVal); |
208 | ret n == null ? null : n.val; |
209 | } |
210 | |
211 | // value associated with the given key in subtree rooted at x; null if no such key |
212 | private A get(Node<A> x, A key) { |
213 | x = find(x, key); |
214 | ret x == null ? null : x.val; |
215 | } |
216 | |
217 | Node<A> find(Node<A> x, A key) { |
218 | while (x != null) { |
219 | int cmp = compare(key, x.val); |
220 | if (cmp < 0) x = x.left; |
221 | else if (cmp > 0) x = x.right; |
222 | else ret x; |
223 | } |
224 | null; |
225 | } |
226 | |
227 | private boolean check() { |
228 | if (!is23()) println("Not a 2-3 tree"); |
229 | if (!isBalanced()) println("Not balanced"); |
230 | return is23() && isBalanced(); |
231 | } |
232 | |
233 | // Does the tree have no red right links, and at most one (left) |
234 | // red links in a row on any path? |
235 | private boolean is23() { return is23(root); } |
236 | private boolean is23(Node<A> x) { |
237 | if (x == null) return true; |
238 | if (isRed(x.right)) return false; |
239 | if (x != root && isRed(x) && isRed(x.left)) |
240 | return false; |
241 | return is23(x.left) && is23(x.right); |
242 | } |
243 | |
244 | // do all paths from root to leaf have same number of black edges? |
245 | private boolean isBalanced() { |
246 | int black = 0; // number of black links on path from root to min |
247 | Node<A> x = root; |
248 | while (x != null) { |
249 | if (!isRed(x)) black++; |
250 | x = x.left; |
251 | } |
252 | return isBalanced(root, black); |
253 | } |
254 | |
255 | // does every path from the root to a leaf have the given number of black links? |
256 | private boolean isBalanced(Node<A> x, int black) { |
257 | if (x == null) return black == 0; |
258 | if (!isRed(x)) black--; |
259 | return isBalanced(x.left, black) && isBalanced(x.right, black); |
260 | } |
261 | |
262 | public void clear() { root = null; size = 0; } |
263 | |
264 | // the smallest key in subtree rooted at x; null if no such key |
265 | private Node<A> min(Node<A> x) { |
266 | // assert x != null; |
267 | while (x.left != null) x = x.left; |
268 | ret x; |
269 | } |
270 | |
271 | private Node<A> deleteMin(Node<A> h) { |
272 | if (h.left == null) |
273 | return null; |
274 | |
275 | if (!isRed(h.left) && !isRed(h.left.left)) |
276 | h = moveRedLeft(h); |
277 | |
278 | h.left = deleteMin(h.left); |
279 | ret balance(h); |
280 | } |
281 | |
282 | public Iterator<A> iterator() { |
283 | ret new MyIterator; |
284 | } |
285 | |
286 | class MyIterator extends ItIt<A> { |
287 | new L<Node<A>> path; |
288 | |
289 | *() { |
290 | fetch(root); |
291 | } |
292 | |
293 | void fetch(Node<A> node) { |
294 | while (node != null) { |
295 | path.add(node); |
296 | node = node.left; |
297 | } |
298 | } |
299 | |
300 | public bool hasNext() { ret !path.isEmpty(); } |
301 | |
302 | public A next() { |
303 | if (path.isEmpty()) fail("no more elements"); |
304 | Node<A> node = popLast(path); |
305 | // last node is always a leaf, so left is null |
306 | // so proceed to fetch right branch |
307 | fetch(node.right); |
308 | ret node.val; |
309 | } |
310 | } |
311 | |
312 | // Returns the smallest key in the symbol table greater than or equal to {@code key}. |
313 | public A ceiling(A key) { |
314 | Node<A> x = ceiling(root, key); |
315 | ret x == null ? null : x.val; |
316 | } |
317 | |
318 | // the smallest key in the subtree rooted at x greater than or equal to the given key |
319 | Node<A> ceiling(Node<A> x, A key) { |
320 | if (x == null) null; |
321 | int cmp = compare(key, x.val); |
322 | if (cmp == 0) ret x; |
323 | if (cmp > 0) ret ceiling(x.right, key); |
324 | Node<A> t = ceiling(x.left, key); |
325 | if (t != null) ret t; |
326 | else ret x; |
327 | } |
328 | |
329 | public A floor(A key) { |
330 | Node<A> x = floor(root, key); |
331 | ret x == null ? null : x.val; |
332 | } |
333 | |
334 | // the largest key in the subtree rooted at x less than or equal to the given key |
335 | Node<A> floor(Node<A> x, A key) { |
336 | if (x == null) null; |
337 | int cmp = compare(key, x.val); |
338 | if (cmp == 0) ret x; |
339 | if (cmp < 0) ret floor(x.left, key); |
340 | Node<A> t = floor(x.right, key); |
341 | if (t != null) ret t; |
342 | else ret x; |
343 | } |
344 | } |
Began life as a copy of #1024413
download show line numbers debug dex old transpilations
Travelled to 7 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv
No comments. add comment
Snippet ID: | #1029166 |
Snippet name: | CompactTreeSet [probably OK. 32 bytes per node as opposed to 40 in TreeSet] |
Eternal ID of this version: | #1029166/45 |
Text MD5: | 60284221ea95064f45637755865d0cd6 |
Transpilation MD5: | 0a99d09c05c66e3a2426d80253c3aebb |
Author: | stefan |
Category: | javax |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2021-06-24 22:08:32 |
Source code size: | 9899 bytes / 344 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 373 / 779 |
Version history: | 44 change(s) |
Referenced in: | [show references] |