UNPKG

12.2 kBJavaScriptView Raw
1/**
2 * Red-black-tree implementation.
3 *
4 * @module tree
5 */
6// @ts-nocheck TODO: remove or refactor this file
7
8const rotate = (tree, parent, newParent, n) => {
9 if (parent === null) {
10 tree.root = newParent
11 newParent._parent = null
12 } else if (parent.left === n) {
13 parent.left = newParent
14 } else if (parent.right === n) {
15 parent.right = newParent
16 } else {
17 throw new Error('The elements are wrongly connected!')
18 }
19}
20
21/**
22 * @template V
23 */
24class N {
25 /**
26 * A created node is always red!
27 *
28 * @param {V} val
29 */
30 constructor (val) {
31 this.val = val
32 this.color = true
33 this._left = null
34 this._right = null
35 this._parent = null
36 }
37
38 isRed () { return this.color }
39 isBlack () { return !this.color }
40 redden () { this.color = true; return this }
41 blacken () { this.color = false; return this }
42 get grandparent () {
43 return this.parent.parent
44 }
45
46 get parent () {
47 return this._parent
48 }
49
50 get sibling () {
51 return (this === this.parent.left)
52 ? this.parent.right
53 : this.parent.left
54 }
55
56 get left () {
57 return this._left
58 }
59
60 get right () {
61 return this._right
62 }
63
64 set left (n) {
65 if (n !== null) {
66 n._parent = this
67 }
68 this._left = n
69 }
70
71 set right (n) {
72 if (n !== null) {
73 n._parent = this
74 }
75 this._right = n
76 }
77
78 rotateLeft (tree) {
79 const parent = this.parent
80 const newParent = this.right
81 const newRight = this.right.left
82 newParent.left = this
83 this.right = newRight
84 rotate(tree, parent, newParent, this)
85 }
86
87 next () {
88 if (this.right !== null) {
89 // search the most left node in the right tree
90 let o = this.right
91 while (o.left !== null) {
92 o = o.left
93 }
94 return o
95 } else {
96 let p = this
97 while (p.parent !== null && p !== p.parent.left) {
98 p = p.parent
99 }
100 return p.parent
101 }
102 }
103
104 prev () {
105 if (this.left !== null) {
106 // search the most right node in the left tree
107 let o = this.left
108 while (o.right !== null) {
109 o = o.right
110 }
111 return o
112 } else {
113 let p = this
114 while (p.parent !== null && p !== p.parent.right) {
115 p = p.parent
116 }
117 return p.parent
118 }
119 }
120
121 rotateRight (tree) {
122 const parent = this.parent
123 const newParent = this.left
124 const newLeft = this.left.right
125 newParent.right = this
126 this.left = newLeft
127 rotate(tree, parent, newParent, this)
128 }
129
130 getUncle () {
131 // we can assume that grandparent exists when this is called!
132 if (this.parent === this.parent.parent.left) {
133 return this.parent.parent.right
134 } else {
135 return this.parent.parent.left
136 }
137 }
138}
139
140const isBlack = node =>
141 node !== null ? node.isBlack() : true
142
143const isRed = (node) =>
144 node !== null ? node.isRed() : false
145
146/**
147 * This is a Red Black Tree implementation
148 *
149 * @template K,V
150 */
151export class Tree {
152 constructor () {
153 this.root = null
154 this.length = 0
155 }
156
157 /**
158 * @param {K} id
159 */
160 findNext (id) {
161 const nextID = id.clone()
162 nextID.clock += 1
163 return this.findWithLowerBound(nextID)
164 }
165
166 /**
167 * @param {K} id
168 */
169 findPrev (id) {
170 const prevID = id.clone()
171 prevID.clock -= 1
172 return this.findWithUpperBound(prevID)
173 }
174
175 /**
176 * @param {K} from
177 */
178 findNodeWithLowerBound (from) {
179 let o = this.root
180 if (o === null) {
181 return null
182 } else {
183 while (true) {
184 if (from === null || (from.lessThan(o.val._id) && o.left !== null)) {
185 // o is included in the bound
186 // try to find an element that is closer to the bound
187 o = o.left
188 } else if (from !== null && o.val._id.lessThan(from)) {
189 // o is not within the bound, maybe one of the right elements is..
190 if (o.right !== null) {
191 o = o.right
192 } else {
193 // there is no right element. Search for the next bigger element,
194 // this should be within the bounds
195 return o.next()
196 }
197 } else {
198 return o
199 }
200 }
201 }
202 }
203
204 /**
205 * @param {K} to
206 */
207 findNodeWithUpperBound (to) {
208 if (to === undefined) {
209 throw new Error('You must define from!')
210 }
211 let o = this.root
212 if (o === null) {
213 return null
214 } else {
215 while (true) {
216 if ((to === null || o.val._id.lessThan(to)) && o.right !== null) {
217 // o is included in the bound
218 // try to find an element that is closer to the bound
219 o = o.right
220 } else if (to !== null && to.lessThan(o.val._id)) {
221 // o is not within the bound, maybe one of the left elements is..
222 if (o.left !== null) {
223 o = o.left
224 } else {
225 // there is no left element. Search for the prev smaller element,
226 // this should be within the bounds
227 return o.prev()
228 }
229 } else {
230 return o
231 }
232 }
233 }
234 }
235
236 /**
237 * @return {V}
238 */
239 findSmallestNode () {
240 let o = this.root
241 while (o != null && o.left != null) {
242 o = o.left
243 }
244 return o
245 }
246
247 /**
248 * @param {K} from
249 * @return {V}
250 */
251 findWithLowerBound (from) {
252 const n = this.findNodeWithLowerBound(from)
253 return n == null ? null : n.val
254 }
255
256 /**
257 * @param {K} to
258 * @return {V}
259 */
260 findWithUpperBound (to) {
261 const n = this.findNodeWithUpperBound(to)
262 return n == null ? null : n.val
263 }
264
265 /**
266 * @param {K} from
267 * @param {V} from
268 * @param {function(V):void} f
269 */
270 iterate (from, to, f) {
271 let o
272 if (from === null) {
273 o = this.findSmallestNode()
274 } else {
275 o = this.findNodeWithLowerBound(from)
276 }
277 while (
278 o !== null &&
279 (
280 to === null || // eslint-disable-line no-unmodified-loop-condition
281 o.val._id.lessThan(to) ||
282 o.val._id.equals(to)
283 )
284 ) {
285 f(o.val)
286 o = o.next()
287 }
288 }
289
290 /**
291 * @param {K} id
292 * @return {V|null}
293 */
294 find (id) {
295 const n = this.findNode(id)
296 if (n !== null) {
297 return n.val
298 } else {
299 return null
300 }
301 }
302
303 /**
304 * @param {K} id
305 * @return {N<V>|null}
306 */
307 findNode (id) {
308 let o = this.root
309 if (o === null) {
310 return null
311 } else {
312 while (true) {
313 if (o === null) {
314 return null
315 }
316 if (id.lessThan(o.val._id)) {
317 o = o.left
318 } else if (o.val._id.lessThan(id)) {
319 o = o.right
320 } else {
321 return o
322 }
323 }
324 }
325 }
326
327 /**
328 * @param {K} id
329 */
330 delete (id) {
331 let d = this.findNode(id)
332 if (d == null) {
333 // throw new Error('Element does not exist!')
334 return
335 }
336 this.length--
337 if (d.left !== null && d.right !== null) {
338 // switch d with the greates element in the left subtree.
339 // o should have at most one child.
340 let o = d.left
341 // find
342 while (o.right !== null) {
343 o = o.right
344 }
345 // switch
346 d.val = o.val
347 d = o
348 }
349 // d has at most one child
350 // let n be the node that replaces d
351 let isFakeChild
352 let child = d.left || d.right
353 if (child === null) {
354 isFakeChild = true
355 child = new N(null)
356 child.blacken()
357 d.right = child
358 } else {
359 isFakeChild = false
360 }
361
362 if (d.parent === null) {
363 if (!isFakeChild) {
364 this.root = child
365 child.blacken()
366 child._parent = null
367 } else {
368 this.root = null
369 }
370 return
371 } else if (d.parent.left === d) {
372 d.parent.left = child
373 } else if (d.parent.right === d) {
374 d.parent.right = child
375 } else {
376 throw new Error('Impossible!')
377 }
378 if (d.isBlack()) {
379 if (child.isRed()) {
380 child.blacken()
381 } else {
382 this._fixDelete(child)
383 }
384 }
385 this.root.blacken()
386 if (isFakeChild) {
387 if (child.parent.left === child) {
388 child.parent.left = null
389 } else if (child.parent.right === child) {
390 child.parent.right = null
391 } else {
392 throw new Error('Impossible #3')
393 }
394 }
395 }
396
397 _fixDelete (n) {
398 if (n.parent === null) {
399 // this can only be called after the first iteration of fixDelete.
400 return
401 }
402 // d was already replaced by the child
403 // d is not the root
404 // d and child are black
405 let sibling = n.sibling
406 if (isRed(sibling)) {
407 // make sibling the grandfather
408 n.parent.redden()
409 sibling.blacken()
410 if (n === n.parent.left) {
411 n.parent.rotateLeft(this)
412 } else if (n === n.parent.right) {
413 n.parent.rotateRight(this)
414 } else {
415 throw new Error('Impossible #2')
416 }
417 sibling = n.sibling
418 }
419 // parent, sibling, and children of n are black
420 if (n.parent.isBlack() &&
421 sibling.isBlack() &&
422 isBlack(sibling.left) &&
423 isBlack(sibling.right)
424 ) {
425 sibling.redden()
426 this._fixDelete(n.parent)
427 } else if (n.parent.isRed() &&
428 sibling.isBlack() &&
429 isBlack(sibling.left) &&
430 isBlack(sibling.right)
431 ) {
432 sibling.redden()
433 n.parent.blacken()
434 } else {
435 if (n === n.parent.left &&
436 sibling.isBlack() &&
437 isRed(sibling.left) &&
438 isBlack(sibling.right)
439 ) {
440 sibling.redden()
441 sibling.left.blacken()
442 sibling.rotateRight(this)
443 sibling = n.sibling
444 } else if (n === n.parent.right &&
445 sibling.isBlack() &&
446 isRed(sibling.right) &&
447 isBlack(sibling.left)
448 ) {
449 sibling.redden()
450 sibling.right.blacken()
451 sibling.rotateLeft(this)
452 sibling = n.sibling
453 }
454 sibling.color = n.parent.color
455 n.parent.blacken()
456 if (n === n.parent.left) {
457 sibling.right.blacken()
458 n.parent.rotateLeft(this)
459 } else {
460 sibling.left.blacken()
461 n.parent.rotateRight(this)
462 }
463 }
464 }
465
466 put (v) {
467 const node = new N(v)
468 if (this.root !== null) {
469 let p = this.root // p abbrev. parent
470 while (true) {
471 if (node.val._id.lessThan(p.val._id)) {
472 if (p.left === null) {
473 p.left = node
474 break
475 } else {
476 p = p.left
477 }
478 } else if (p.val._id.lessThan(node.val._id)) {
479 if (p.right === null) {
480 p.right = node
481 break
482 } else {
483 p = p.right
484 }
485 } else {
486 p.val = node.val
487 return p
488 }
489 }
490 this._fixInsert(node)
491 } else {
492 this.root = node
493 }
494 this.length++
495 this.root.blacken()
496 return node
497 }
498
499 _fixInsert (n) {
500 if (n.parent === null) {
501 n.blacken()
502 return
503 } else if (n.parent.isBlack()) {
504 return
505 }
506 const uncle = n.getUncle()
507 if (uncle !== null && uncle.isRed()) {
508 // Note: parent: red, uncle: red
509 n.parent.blacken()
510 uncle.blacken()
511 n.grandparent.redden()
512 this._fixInsert(n.grandparent)
513 } else {
514 // Note: parent: red, uncle: black or null
515 // Now we transform the tree in such a way that
516 // either of these holds:
517 // 1) grandparent.left.isRed
518 // and grandparent.left.left.isRed
519 // 2) grandparent.right.isRed
520 // and grandparent.right.right.isRed
521 if (n === n.parent.right && n.parent === n.grandparent.left) {
522 n.parent.rotateLeft(this)
523 // Since we rotated and want to use the previous
524 // cases, we need to set n in such a way that
525 // n.parent.isRed again
526 n = n.left
527 } else if (n === n.parent.left && n.parent === n.grandparent.right) {
528 n.parent.rotateRight(this)
529 // see above
530 n = n.right
531 }
532 // Case 1) or 2) hold from here on.
533 // Now traverse grandparent, make parent a black node
534 // on the highest level which holds two red nodes.
535 n.parent.blacken()
536 n.grandparent.redden()
537 if (n === n.parent.left) {
538 // Case 1
539 n.grandparent.rotateRight(this)
540 } else {
541 // Case 2
542 n.grandparent.rotateLeft(this)
543 }
544 }
545 }
546}