Libraryless. Click here for Pure Java version (320L/3K).
1 | do not include function get. |
2 | do not include function add. |
3 | do not include function remove. |
4 | do not include function set. |
5 | do not include function println. |
6 | |
7 | // see: https://en.wikipedia.org/wiki/Order_statistic_tree |
8 | // based on: https://algs4.cs.princeton.edu/33balanced/RedBlackBST.java.html |
9 | final sclass LogNArray<Value> extends RandomAccessAbstractList<Value> { |
10 | |
11 | // A symbol table implemented using a left-leaning red-black BST. |
12 | // This is the 2-3 version. |
13 | |
14 | private static final boolean RED = true; |
15 | private static final boolean BLACK = false; |
16 | |
17 | private Node<Value> root; // root of the BST |
18 | |
19 | // BST helper node data type |
20 | private final sclass Node<Value> { |
21 | private Value val; // associated data |
22 | private Node<Value> left, right; // links to left and right subtrees |
23 | private int sizeAndColor; // subtree count + color (highest bit) |
24 | |
25 | *() {} |
26 | |
27 | *(Value val, boolean color, int size) { |
28 | this.val = val; |
29 | sizeAndColor = size | (color ? 0x80000000 : 0); |
30 | } |
31 | |
32 | int size() { ret sizeAndColor & 0x7FFFFFFF; } |
33 | bool color() { ret sizeAndColor < 0; } |
34 | |
35 | void setSize(int size) { sizeAndColor = sizeAndColor & 0x80000000 | size; } |
36 | void setColor(bool color) { sizeAndColor = size() | (color ? 0x80000000 : 0); } |
37 | } |
38 | |
39 | // is node x red; false if x is null ? |
40 | private boolean isRed(Node<Value> x) { |
41 | if (x == null) return false; |
42 | return x.color() == RED; |
43 | } |
44 | |
45 | // number of node in subtree rooted at x; 0 if x is null |
46 | private int size(Node<Value> x) { |
47 | if (x == null) return 0; |
48 | return x.size(); |
49 | } |
50 | |
51 | |
52 | public int size() { |
53 | return size(root); |
54 | } |
55 | |
56 | public boolean isEmpty() { |
57 | return root == null; |
58 | } |
59 | |
60 | |
61 | public void add(int idx, Value val) { |
62 | if (idx < 0 || idx > size()) throw new IndexOutOfBoundsException(idx + " / " + size()); |
63 | |
64 | root = add(root, idx, val); |
65 | root.setColor(BLACK); |
66 | ifdef LogNArray_debug assertTrue(check()); endifdef |
67 | } |
68 | |
69 | // insert the value pair in index k of subtree rooted at h |
70 | private Node<Value> add(Node<Value> h, int k, Value val) { |
71 | if (h == null) ret new Node(val, RED, 1); |
72 | |
73 | int t = size(h.left); |
74 | if (k < t) |
75 | // replace / fit in left child |
76 | h.left = add(h.left, k, val); |
77 | else if (k == t) { |
78 | // move value to right child, replace value |
79 | h.right = add(h.right, 0, h.val); |
80 | h.val = val; |
81 | } else |
82 | // replace / fit in right child |
83 | h.right = add(h.right, k-t-1, val); |
84 | |
85 | // fix-up any right-leaning links |
86 | if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h); |
87 | if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h); |
88 | if (isRed(h.left) && isRed(h.right)) flipColors(h); |
89 | h.setSize(size(h.left) + size(h.right) + 1); |
90 | |
91 | ifdef LogNArray_debug |
92 | print("LogNArray.add: Node " + h.val + " new size: " + h.size()); |
93 | endifdef |
94 | |
95 | ret h; |
96 | } |
97 | |
98 | public Value remove(int idx) { |
99 | Value oldValue = get(idx); |
100 | |
101 | // if both children of root are black, set root to red |
102 | if (!isRed(root.left) && !isRed(root.right)) |
103 | root.setColor(RED); |
104 | |
105 | root = remove(root, idx); |
106 | if (root != null) root.setColor(BLACK); |
107 | |
108 | ifdef LogNArray_debug assertTrue(check()); endifdef |
109 | |
110 | ret oldValue; |
111 | } |
112 | |
113 | private Node<Value> remove(Node<Value> h, int k) { |
114 | int t = size(h.left); |
115 | if (k < t) { // remove from left child |
116 | if (!isRed(h.left) && !isRed(h.left.left)) |
117 | h = moveRedLeft(h); |
118 | h.left = remove(h.left, k); |
119 | } else { // remove from center or right child |
120 | if (isRed(h.left)) { |
121 | h = rotateRight(h); |
122 | t = size(h.left); |
123 | } |
124 | if (h.right == null) null; // drop whole node |
125 | if (!isRed(h.right) && !isRed(h.right.left)) { |
126 | h = moveRedRight(h); |
127 | t = size(h.left); |
128 | } |
129 | if (k == t) { // replace center value with min of right child |
130 | h.val = nodeAtIndex(h.right, 0).val; |
131 | h.right = remove(h.right, 0); |
132 | } |
133 | else h.right = remove(h.right, k-t-1); |
134 | } |
135 | ret balance(h); |
136 | } |
137 | |
138 | // make a left-leaning link lean to the right |
139 | private Node<Value> rotateRight(Node<Value> h) { |
140 | // assert (h != null) && isRed(h.left); |
141 | Node<Value> x = h.left; |
142 | h.left = x.right; |
143 | x.right = h; |
144 | x.setColor(x.right.color()); |
145 | x.right.setColor(RED); |
146 | x.setSize(h.size()); |
147 | h.setSize(size(h.left) + size(h.right) + 1); |
148 | return x; |
149 | } |
150 | |
151 | // make a right-leaning link lean to the left |
152 | private Node<Value> rotateLeft(Node<Value> h) { |
153 | // assert (h != null) && isRed(h.right); |
154 | Node<Value> x = h.right; |
155 | h.right = x.left; |
156 | x.left = h; |
157 | x.setColor(x.left.color()); |
158 | x.left.setColor(RED); |
159 | x.setSize(h.size()); |
160 | h.setSize(size(h.left) + size(h.right) + 1); |
161 | return x; |
162 | } |
163 | |
164 | // flip the colors of a node and its two children |
165 | private void flipColors(Node<Value> h) { |
166 | // h must have opposite color of its two children |
167 | // assert (h != null) && (h.left != null) && (h.right != null); |
168 | // assert (!isRed(h) && isRed(h.left) && isRed(h.right)) |
169 | // || (isRed(h) && !isRed(h.left) && !isRed(h.right)); |
170 | h.setColor(!h.color()); |
171 | h.left.setColor(!h.left.color()); |
172 | h.right.setColor(!h.right.color()); |
173 | } |
174 | |
175 | // Assuming that h is red and both h.left and h.left.left |
176 | // are black, make h.left or one of its children red. |
177 | private Node<Value> moveRedLeft(Node<Value> h) { |
178 | // assert (h != null); |
179 | // assert isRed(h) && !isRed(h.left) && !isRed(h.left.left); |
180 | |
181 | flipColors(h); |
182 | if (isRed(h.right.left)) { |
183 | h.right = rotateRight(h.right); |
184 | h = rotateLeft(h); |
185 | flipColors(h); |
186 | } |
187 | return h; |
188 | } |
189 | |
190 | // Assuming that h is red and both h.right and h.right.left |
191 | // are black, make h.right or one of its children red. |
192 | private Node<Value> moveRedRight(Node<Value> h) { |
193 | // assert (h != null); |
194 | // assert isRed(h) && !isRed(h.right) && !isRed(h.right.left); |
195 | flipColors(h); |
196 | if (isRed(h.left.left)) { |
197 | h = rotateRight(h); |
198 | flipColors(h); |
199 | } |
200 | return h; |
201 | } |
202 | |
203 | // restore red-black tree invariant |
204 | private Node<Value> balance(Node<Value> h) { |
205 | // assert (h != null); |
206 | |
207 | if (isRed(h.right)) h = rotateLeft(h); |
208 | if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h); |
209 | if (isRed(h.left) && isRed(h.right)) flipColors(h); |
210 | |
211 | h.setSize(size(h.left) + size(h.right) + 1); |
212 | return h; |
213 | } |
214 | |
215 | |
216 | /** |
217 | * Returns the height of the BST (for debugging). |
218 | * @return the height of the BST (a 1-node tree has height 0) |
219 | */ |
220 | public int height() { |
221 | return height(root); |
222 | } |
223 | |
224 | private int height(Node<Value> x) { |
225 | if (x == null) return -1; |
226 | return 1 + Math.max(height(x.left), height(x.right)); |
227 | } |
228 | |
229 | public Value get(int k) { |
230 | ret nodeAtIndex(k).val; |
231 | } |
232 | |
233 | public Value set(int k, Value val) { |
234 | Node<Value> n = nodeAtIndex(k); |
235 | Value oldValue = n.val; |
236 | n.val = val; |
237 | ret oldValue; |
238 | } |
239 | |
240 | public Node<Value> nodeAtIndex(int k) { |
241 | if (k < 0 || k >= size()) { |
242 | throw new IndexOutOfBoundsException(k + " / " + size()); |
243 | } |
244 | ret nodeAtIndex(root, k); |
245 | } |
246 | |
247 | // the key of rank k in the subtree rooted at x |
248 | private Node<Value> nodeAtIndex(Node<Value> x, int k) { |
249 | // assert x != null; |
250 | // assert k >= 0 && k < size(x); |
251 | int t = size(x.left); |
252 | if (t > k) return nodeAtIndex(x.left, k); |
253 | else if (t < k) return nodeAtIndex(x.right, k-t-1); |
254 | else return x; |
255 | } |
256 | |
257 | private boolean check() { |
258 | if (!isSizeConsistent()) System.out.println("Subtree counts not consistent"); |
259 | if (!is23()) System.out.println("Not a 2-3 tree"); |
260 | if (!isBalanced()) System.out.println("Not balanced"); |
261 | return isSizeConsistent() && is23() && isBalanced(); |
262 | } |
263 | |
264 | // are the size fields correct? |
265 | private boolean isSizeConsistent() { return isSizeConsistent(root); } |
266 | private boolean isSizeConsistent(Node<Value> x) { |
267 | if (x == null) return true; |
268 | if (x.size() != size(x.left) + size(x.right) + 1) return false; |
269 | return isSizeConsistent(x.left) && isSizeConsistent(x.right); |
270 | } |
271 | |
272 | // Does the tree have no red right links, and at most one (left) |
273 | // red links in a row on any path? |
274 | private boolean is23() { return is23(root); } |
275 | private boolean is23(Node<Value> x) { |
276 | if (x == null) return true; |
277 | if (isRed(x.right)) return false; |
278 | if (x != root && isRed(x) && isRed(x.left)) |
279 | return false; |
280 | return is23(x.left) && is23(x.right); |
281 | } |
282 | |
283 | // do all paths from root to leaf have same number of black edges? |
284 | private boolean isBalanced() { |
285 | int black = 0; // number of black links on path from root to min |
286 | Node<Value> x = root; |
287 | while (x != null) { |
288 | if (!isRed(x)) black++; |
289 | x = x.left; |
290 | } |
291 | return isBalanced(root, black); |
292 | } |
293 | |
294 | // does every path from the root to a leaf have the given number of black links? |
295 | private boolean isBalanced(Node<Value> x, int black) { |
296 | if (x == null) return black == 0; |
297 | if (!isRed(x)) black--; |
298 | return isBalanced(x.left, black) && isBalanced(x.right, black); |
299 | } |
300 | |
301 | public void clear() { root = null; } |
302 | } |
Began life as a copy of #1024427
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: | #1024436 |
Snippet name: | LogNArray v4 for export |
Eternal ID of this version: | #1024436/5 |
Text MD5: | a566b7f8a05b5a499d9d57a25ed7b07d |
Transpilation MD5: | 1c7828d71966901460eb0c4d0cae74ca |
Author: | stefan |
Category: | javax |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2019-08-12 17:16:28 |
Source code size: | 9568 bytes / 302 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 200 / 289 |
Version history: | 4 change(s) |
Referenced in: | [show references] |