Libraryless. Click here for Pure Java version (482L/4K).
1 | // see: https://en.wikipedia.org/wiki/Order_statistic_tree |
2 | sclass LogNArray<A> extends RandomAccessAbstractList<A> { |
3 | |
4 | private transient Entry<A> root; |
5 | |
6 | /** |
7 | * The number of entries in the tree |
8 | */ |
9 | //private transient int size = 0; |
10 | |
11 | /** |
12 | * The number of structural modifications to the tree. |
13 | */ |
14 | private transient int modCount = 0; |
15 | |
16 | // Query Operations |
17 | |
18 | public int size() { |
19 | ret root == null ? 0 : root.size; |
20 | } |
21 | |
22 | public bool contains(Object value) { |
23 | for (Entry<A> e = getFirstEntry(); e != null; e = successor(e)) |
24 | if (eq(value, e.value)) |
25 | true; |
26 | false; |
27 | } |
28 | |
29 | public A get(int idx) { |
30 | Entry<A> p = getEntry(idx); |
31 | return p == null ? null : p.value; |
32 | } |
33 | |
34 | int subTreeSize(Entry<A> e) { ret e == null ? 0 : e.size; } |
35 | |
36 | final Entry<A> getEntry(int idx) { |
37 | if (idx < 0 || idx >= size()) |
38 | throw new IndexOutOfBoundsException(idx + " / " + size()); |
39 | Entry<A> p = root; |
40 | while true { |
41 | int sizeLeft = subTreeSize(p.left); |
42 | if (idx < sizeLeft) |
43 | continue with p = p.left; |
44 | idx -= sizeLeft; |
45 | if (idx == 0) ret p; |
46 | idx--; |
47 | p = p.right; |
48 | } |
49 | } |
50 | |
51 | /*public A put(Int key, A value) { |
52 | Entry<A> t = root; |
53 | if (t == null) { |
54 | compare(key, key); // type (and possibly null) check |
55 | |
56 | root = new Entry<>(key, value, null); |
57 | //size = 1; |
58 | modCount++; |
59 | return null; |
60 | } |
61 | int cmp; |
62 | Entry<A> parent; |
63 | // split comparator and comparable paths |
64 | Comparator<? super Int> cpr = comparator; |
65 | if (cpr != null) { |
66 | do { |
67 | parent = t; |
68 | cmp = cpr.compare(key, t.key); |
69 | if (cmp < 0) |
70 | t = t.left; |
71 | else if (cmp > 0) |
72 | t = t.right; |
73 | else |
74 | return t.setValue(value); |
75 | } while (t != null); |
76 | } |
77 | else { |
78 | if (key == null) |
79 | throw new NullPointerException(); |
80 | @SuppressWarnings("unchecked") |
81 | Comparable<? super Int> k = (Comparable<? super Int>) key; |
82 | do { |
83 | parent = t; |
84 | cmp = k.compareTo(t.key); |
85 | if (cmp < 0) |
86 | t = t.left; |
87 | else if (cmp > 0) |
88 | t = t.right; |
89 | else |
90 | return t.setValue(value); |
91 | } while (t != null); |
92 | } |
93 | Entry<A> e = new Entry<>(key, value, parent); |
94 | if (cmp < 0) |
95 | parent.left = e; |
96 | else |
97 | parent.right = e; |
98 | fixAfterInsertion(e); |
99 | //size++; |
100 | modCount++; |
101 | return null; |
102 | }*/ |
103 | |
104 | /*public A remove(int idx) { |
105 | Entry<A> p = getEntry(key); |
106 | if (p == null) |
107 | return null; |
108 | |
109 | A oldValue = p.value; |
110 | deleteEntry(p); |
111 | return oldValue; |
112 | }*/ |
113 | |
114 | public void clear() { |
115 | modCount++; |
116 | //size = 0; |
117 | root = null; |
118 | } |
119 | |
120 | // Red-black mechanics |
121 | |
122 | private static final boolean RED = false; |
123 | private static final boolean BLACK = true; |
124 | |
125 | /** |
126 | * Node in the Tree. Doubles as a means to pass key-value pairs back to |
127 | * user (see Map.Entry). |
128 | */ |
129 | |
130 | static final class Entry<A> { |
131 | int size = 1; // entry count of subtree |
132 | //Int key; |
133 | A value; |
134 | Entry<A> left; |
135 | Entry<A> right; |
136 | Entry<A> parent; |
137 | boolean color = BLACK; |
138 | |
139 | /** |
140 | * Make a new cell with given value and parent, and with |
141 | * {@code null} child links, size 1, and BLACK color. |
142 | */ |
143 | Entry(A value, Entry<A> parent) { |
144 | this.value = value; |
145 | this.parent = parent; |
146 | } |
147 | } |
148 | |
149 | final Entry<A> getFirstEntry() { |
150 | Entry<A> p = root; |
151 | if (p != null) |
152 | while (p.left != null) |
153 | p = p.left; |
154 | return p; |
155 | } |
156 | |
157 | final Entry<A> getLastEntry() { |
158 | Entry<A> p = root; |
159 | if (p != null) |
160 | while (p.right != null) |
161 | p = p.right; |
162 | return p; |
163 | } |
164 | |
165 | static <Int,A> LogNArray.Entry<A> successor(Entry<A> t) { |
166 | if (t == null) |
167 | return null; |
168 | else if (t.right != null) { |
169 | Entry<A> p = t.right; |
170 | while (p.left != null) |
171 | p = p.left; |
172 | return p; |
173 | } else { |
174 | Entry<A> p = t.parent; |
175 | Entry<A> ch = t; |
176 | while (p != null && ch == p.right) { |
177 | ch = p; |
178 | p = p.parent; |
179 | } |
180 | return p; |
181 | } |
182 | } |
183 | |
184 | static <Int,A> Entry<A> predecessor(Entry<A> t) { |
185 | if (t == null) |
186 | return null; |
187 | else if (t.left != null) { |
188 | Entry<A> p = t.left; |
189 | while (p.right != null) |
190 | p = p.right; |
191 | return p; |
192 | } else { |
193 | Entry<A> p = t.parent; |
194 | Entry<A> ch = t; |
195 | while (p != null && ch == p.left) { |
196 | ch = p; |
197 | p = p.parent; |
198 | } |
199 | return p; |
200 | } |
201 | } |
202 | |
203 | /** |
204 | * Balancing operations. |
205 | * |
206 | * Implementations of rebalancings during insertion and deletion are |
207 | * slightly different than the CLR version. Rather than using dummy |
208 | * nilnodes, we use a set of accessors that deal properly with null. They |
209 | * are used to avoid messiness surrounding nullness checks in the main |
210 | * algorithms. |
211 | */ |
212 | |
213 | private static <Int,A> boolean colorOf(Entry<A> p) { |
214 | return (p == null ? BLACK : p.color); |
215 | } |
216 | |
217 | private static <Int,A> Entry<A> parentOf(Entry<A> p) { |
218 | return (p == null ? null: p.parent); |
219 | } |
220 | |
221 | private static <Int,A> void setColor(Entry<A> p, boolean c) { |
222 | if (p != null) |
223 | p.color = c; |
224 | } |
225 | |
226 | private static <Int,A> Entry<A> leftOf(Entry<A> p) { |
227 | return (p == null) ? null: p.left; |
228 | } |
229 | |
230 | private static <Int,A> Entry<A> rightOf(Entry<A> p) { |
231 | return (p == null) ? null: p.right; |
232 | } |
233 | |
234 | /** From CLR */ |
235 | private void rotateLeft(Entry<A> p) { |
236 | if (p != null) { |
237 | Entry<A> r = p.right; |
238 | p.right = r.left; |
239 | if (r.left != null) |
240 | r.left.parent = p; |
241 | r.parent = p.parent; |
242 | if (p.parent == null) |
243 | root = r; |
244 | else if (p.parent.left == p) |
245 | p.parent.left = r; |
246 | else |
247 | p.parent.right = r; |
248 | r.left = p; |
249 | p.parent = r; |
250 | } |
251 | } |
252 | |
253 | /** From CLR */ |
254 | private void rotateRight(Entry<A> p) { |
255 | if (p != null) { |
256 | Entry<A> l = p.left; |
257 | p.left = l.right; |
258 | if (l.right != null) l.right.parent = p; |
259 | l.parent = p.parent; |
260 | if (p.parent == null) |
261 | root = l; |
262 | else if (p.parent.right == p) |
263 | p.parent.right = l; |
264 | else p.parent.left = l; |
265 | l.right = p; |
266 | p.parent = l; |
267 | } |
268 | } |
269 | |
270 | /** From CLR */ |
271 | private void fixAfterInsertion(Entry<A> x) { |
272 | x.color = RED; |
273 | |
274 | while (x != null && x != root && x.parent.color == RED) { |
275 | if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { |
276 | Entry<A> y = rightOf(parentOf(parentOf(x))); |
277 | if (colorOf(y) == RED) { |
278 | setColor(parentOf(x), BLACK); |
279 | setColor(y, BLACK); |
280 | setColor(parentOf(parentOf(x)), RED); |
281 | x = parentOf(parentOf(x)); |
282 | } else { |
283 | if (x == rightOf(parentOf(x))) { |
284 | x = parentOf(x); |
285 | rotateLeft(x); |
286 | } |
287 | setColor(parentOf(x), BLACK); |
288 | setColor(parentOf(parentOf(x)), RED); |
289 | rotateRight(parentOf(parentOf(x))); |
290 | } |
291 | } else { |
292 | Entry<A> y = leftOf(parentOf(parentOf(x))); |
293 | if (colorOf(y) == RED) { |
294 | setColor(parentOf(x), BLACK); |
295 | setColor(y, BLACK); |
296 | setColor(parentOf(parentOf(x)), RED); |
297 | x = parentOf(parentOf(x)); |
298 | } else { |
299 | if (x == leftOf(parentOf(x))) { |
300 | x = parentOf(x); |
301 | rotateRight(x); |
302 | } |
303 | setColor(parentOf(x), BLACK); |
304 | setColor(parentOf(parentOf(x)), RED); |
305 | rotateLeft(parentOf(parentOf(x))); |
306 | } |
307 | } |
308 | } |
309 | root.color = BLACK; |
310 | } |
311 | |
312 | /** |
313 | * Delete node p, and then rebalance the tree. |
314 | */ |
315 | private void deleteEntry(Entry<A> p) { |
316 | modCount++; |
317 | //size--; |
318 | |
319 | // If strictly internal, copy successor's element to p and then make p |
320 | // point to successor. |
321 | if (p.left != null && p.right != null) { |
322 | Entry<A> s = successor(p); |
323 | p.value = s.value; |
324 | p = s; |
325 | } // p has 2 children |
326 | |
327 | // Start fixup at replacement node, if it exists. |
328 | Entry<A> replacement = (p.left != null ? p.left : p.right); |
329 | |
330 | if (replacement != null) { |
331 | // Link replacement to parent |
332 | replacement.parent = p.parent; |
333 | if (p.parent == null) |
334 | root = replacement; |
335 | else if (p == p.parent.left) |
336 | p.parent.left = replacement; |
337 | else |
338 | p.parent.right = replacement; |
339 | |
340 | // Null out links so they are OK to use by fixAfterDeletion. |
341 | p.left = p.right = p.parent = null; |
342 | |
343 | // Fix replacement |
344 | if (p.color == BLACK) |
345 | fixAfterDeletion(replacement); |
346 | } else if (p.parent == null) { // return if we are the only node. |
347 | root = null; |
348 | } else { // No children. Use self as phantom replacement and unlink. |
349 | if (p.color == BLACK) |
350 | fixAfterDeletion(p); |
351 | |
352 | if (p.parent != null) { |
353 | if (p == p.parent.left) |
354 | p.parent.left = null; |
355 | else if (p == p.parent.right) |
356 | p.parent.right = null; |
357 | p.parent = null; |
358 | } |
359 | } |
360 | } |
361 | |
362 | /** From CLR */ |
363 | private void fixAfterDeletion(Entry<A> x) { |
364 | while (x != root && colorOf(x) == BLACK) { |
365 | if (x == leftOf(parentOf(x))) { |
366 | Entry<A> sib = rightOf(parentOf(x)); |
367 | |
368 | if (colorOf(sib) == RED) { |
369 | setColor(sib, BLACK); |
370 | setColor(parentOf(x), RED); |
371 | rotateLeft(parentOf(x)); |
372 | sib = rightOf(parentOf(x)); |
373 | } |
374 | |
375 | if (colorOf(leftOf(sib)) == BLACK && |
376 | colorOf(rightOf(sib)) == BLACK) { |
377 | setColor(sib, RED); |
378 | x = parentOf(x); |
379 | } else { |
380 | if (colorOf(rightOf(sib)) == BLACK) { |
381 | setColor(leftOf(sib), BLACK); |
382 | setColor(sib, RED); |
383 | rotateRight(sib); |
384 | sib = rightOf(parentOf(x)); |
385 | } |
386 | setColor(sib, colorOf(parentOf(x))); |
387 | setColor(parentOf(x), BLACK); |
388 | setColor(rightOf(sib), BLACK); |
389 | rotateLeft(parentOf(x)); |
390 | x = root; |
391 | } |
392 | } else { // symmetric |
393 | Entry<A> sib = leftOf(parentOf(x)); |
394 | |
395 | if (colorOf(sib) == RED) { |
396 | setColor(sib, BLACK); |
397 | setColor(parentOf(x), RED); |
398 | rotateRight(parentOf(x)); |
399 | sib = leftOf(parentOf(x)); |
400 | } |
401 | |
402 | if (colorOf(rightOf(sib)) == BLACK && |
403 | colorOf(leftOf(sib)) == BLACK) { |
404 | setColor(sib, RED); |
405 | x = parentOf(x); |
406 | } else { |
407 | if (colorOf(leftOf(sib)) == BLACK) { |
408 | setColor(rightOf(sib), BLACK); |
409 | setColor(sib, RED); |
410 | rotateLeft(sib); |
411 | sib = leftOf(parentOf(x)); |
412 | } |
413 | setColor(sib, colorOf(parentOf(x))); |
414 | setColor(parentOf(x), BLACK); |
415 | setColor(leftOf(sib), BLACK); |
416 | rotateRight(parentOf(x)); |
417 | x = root; |
418 | } |
419 | } |
420 | } |
421 | |
422 | setColor(x, BLACK); |
423 | } |
424 | |
425 | /** |
426 | * Finds the level down to which to assign all nodes BLACK. This is the |
427 | * last `full' level of the complete binary tree produced by buildTree. |
428 | * The remaining nodes are colored RED. (This makes a `nice' set of |
429 | * color assignments wrt future insertions.) This level number is |
430 | * computed by finding the number of splits needed to reach the zeroeth |
431 | * node. |
432 | * |
433 | * @param size the (non-negative) number of keys in the tree to be built |
434 | */ |
435 | private static int computeRedLevel(int size) { |
436 | return 31 - Integer.numberOfLeadingZeros(size + 1); |
437 | } |
438 | } |
Began life as a copy of #1024411
download show line numbers debug dex old transpilations
Travelled to 6 computer(s): bhatertpkbcr, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1024412 |
Snippet name: | LogNArray v2 [based on TreeMap, dev.] |
Eternal ID of this version: | #1024412/12 |
Text MD5: | e218fba96a516a2cc9c23442a664817e |
Transpilation MD5: | ba1ee5307ba902cc8076a7b714b727b9 |
Author: | stefan |
Category: | javax |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2019-08-11 11:38:26 |
Source code size: | 12931 bytes / 438 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 280 / 391 |
Version history: | 11 change(s) |
Referenced in: | [show references] |