Skip to content

Commit cab9942

Browse files
✨ feat: Make refs stable.
1 parent 23c5af4 commit cab9942

22 files changed

+396
-126
lines changed

src/deletion/delete_case0.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ import delete_case1 from './delete_case1.js';
1010
* - all other root-leaf paths have a black height of b
1111
*
1212
* @param {Node} n - The input node.
13+
* @return {Node} The root of the modified subtree.
1314
*/
1415
const delete_case0 = (n) => {
1516
assert(n instanceof Node);
1617
assert(n._color === BLACK);
1718
// If n is the root, there is nothing to do:
1819
// - all paths go through n, and
1920
// - n is black.
20-
if (n.parent !== null) delete_case1(n);
21+
return n.parent === null ? n : delete_case1(n);
2122
};
2223

2324
export default delete_case0;

src/deletion/delete_case1.js

+19-18
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import delete_case3 from './delete_case3.js';
1717
* - n is not the root
1818
*
1919
* @param {Node} n - The input node.
20+
* @return {Node} The root of the modified subtree.
2021
*/
2122
const delete_case1 = (n) => {
2223
assert(n instanceof Node);
@@ -28,25 +29,25 @@ const delete_case1 = (n) => {
2829

2930
if (s._color === BLACK) {
3031
// If n's sibling is BLACK, go to case 2.
31-
delete_case2(n);
32-
} else {
33-
/**
34-
* Otherwise, prepare for and go to case 3.
35-
*
36-
* B B
37-
* / \ / \
38-
* >B R R B
39-
* / \ / \ --> / \ / \
40-
* - - B B >B B = =
41-
* / \ / \ / \ / \
42-
* = = = = - - = =
43-
*/
44-
n.parent._color = RED;
45-
s._color = BLACK;
46-
if (n === n.parent.left) rotate_left(n.parent);
47-
else rotate_right(n.parent);
48-
delete_case3(n);
32+
return delete_case2(n);
4933
}
34+
35+
/**
36+
* Otherwise, prepare for and go to case 3.
37+
*
38+
* B *B
39+
* / \ / \
40+
* >B R R B
41+
* / \ / \ --> / \ / \
42+
* - - B B >B B = =
43+
* / \ / \ / \ / \
44+
* = = = = - - = =
45+
*/
46+
n.parent._color = RED;
47+
s._color = BLACK;
48+
if (n === n.parent.left) rotate_left(n.parent);
49+
else rotate_right(n.parent);
50+
return delete_case3(n).parent;
5051
};
5152

5253
export default delete_case1;

src/deletion/delete_case2.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import delete_case3 from './delete_case3.js';
1616
* - n's sibling is black
1717
*
1818
* @param {Node} n - The input node.
19+
* @return {Node} The root of the modified subtree.
1920
*/
2021
const delete_case2 = (n) => {
2122
assert(n instanceof Node);
@@ -44,11 +45,11 @@ const delete_case2 = (n) => {
4445
(s.right === null || s.right._color === BLACK)
4546
) {
4647
s._color = RED;
47-
delete_case0(n.parent);
48+
return delete_case0(n.parent);
4849
}
4950

5051
// Otherwise, go to case 3.
51-
else delete_case3(n);
52+
return delete_case3(n);
5253
};
5354

5455
export default delete_case2;

src/deletion/delete_case3.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import delete_case4 from './delete_case4.js';
1616
* - n's parent and n's sibling's children cannot all be black
1717
*
1818
* @param {Node} n - The input node.
19+
* @return {Node} The root of the modified subtree.
1920
*/
2021
const delete_case3 = (n) => {
2122
assert(n instanceof Node);
@@ -52,10 +53,11 @@ const delete_case3 = (n) => {
5253
) {
5354
s._color = RED;
5455
n.parent._color = BLACK;
56+
return n.parent;
5557
}
5658

5759
// Otherwise, go to case 4.
58-
else delete_case4(n);
60+
return delete_case4(n);
5961
};
6062

6163
export default delete_case3;

src/deletion/delete_case4.js

+15-12
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import delete_case5 from './delete_case5.js';
1818
* - at least one of n's sibling's children is red
1919
*
2020
* @param {Node} n - The input node.
21+
* @return {Node} The root of the modified subtree.
2122
*/
2223
const delete_case4 = (n) => {
2324
assert(n instanceof Node);
@@ -32,18 +33,18 @@ const delete_case4 = (n) => {
3233
// the left of the left of the parent, or right of the right, so case 5
3334
// will rotate correctly.
3435

35-
/**
36-
* ? ?
37-
* / \ / \
38-
* >B B >B B
39-
* / \ / \ --> / \ / \
40-
* - - R B - - = R
41-
* / \ / \ / \
42-
* = = - - = B
43-
* / \
44-
* - -
45-
*/
4636
if (n === n.parent.left && (s.right === null || s.right._color === BLACK)) {
37+
/**
38+
* ? ?
39+
* / \ / \
40+
* >B B >B B
41+
* / \ / \ --> / \ / \
42+
* - - R B - - = R
43+
* / \ / \ / \
44+
* = = - - = B
45+
* / \
46+
* - -
47+
*/
4748
s._color = RED;
4849
s.left._color = BLACK;
4950
rotate_right(s);
@@ -67,7 +68,9 @@ const delete_case4 = (n) => {
6768
rotate_left(s);
6869
}
6970

70-
delete_case5(n);
71+
// TODO we could merge case 4 and 5 without too much trouble.
72+
// It would allow to avoid checking n's direction twice.
73+
return delete_case5(n);
7174
};
7275

7376
export default delete_case4;

src/deletion/delete_case5.js

+10-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import sibling from '../family/sibling.js';
1717
* - if n is a right child, the left child of n's sibling is red
1818
*
1919
* @param {Node} n - The input node.
20+
* @return {Node} The root of the modified subtree.
2021
*/
2122
const delete_case5 = (n) => {
2223
assert(n instanceof Node);
@@ -28,8 +29,9 @@ const delete_case5 = (n) => {
2829

2930
/**
3031
* Increment the black height of all root-leaf paths going through n by
32+
* swapping the colors of n's parent and n's sibling and
3133
* rotating at n's parent. This decrements the black height of all
32-
* root-leaft paths going through n's sibling's right child.
34+
* root-leaf paths going through n's sibling's right child.
3335
* We can repaint n's sibling's right child in black to fix this.
3436
* We are done.
3537
*
@@ -44,21 +46,23 @@ const delete_case5 = (n) => {
4446
* - -
4547
*/
4648

49+
// Swap the color of the parent and the sibling.
4750
s._color = n.parent._color;
4851
n.parent._color = BLACK;
4952

5053
if (n === n.parent.left) {
5154
assert(s.right._color === RED);
5255
s.right._color = BLACK;
5356
rotate_left(n.parent);
57+
return s;
5458
}
5559

5660
// Symmetric case
57-
else {
58-
assert(s.left._color === RED);
59-
s.left._color = BLACK;
60-
rotate_right(n.parent);
61-
}
61+
62+
assert(s.left._color === RED);
63+
s.left._color = BLACK;
64+
rotate_right(n.parent);
65+
return s;
6266
};
6367

6468
export default delete_case5;

src/deletion/delete_no_child.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import prune from './prune.js';
1616
* - n is not the root
1717
*
1818
* @param {Node} n - The node to delete.
19+
* @return {Node} The root of the modified subtree.
1920
*/
2021
const delete_no_child = (n) => {
2122
assert(n instanceof Node);
@@ -26,7 +27,7 @@ const delete_no_child = (n) => {
2627
if (n._color !== BLACK) {
2728
assert(n._color === RED);
2829
prune(n);
29-
return;
30+
return n.parent;
3031
}
3132

3233
// Mock leaf since there is no left child
@@ -39,10 +40,11 @@ const delete_no_child = (n) => {
3940

4041
// If n is black, deleting it reduces the black-height of every path going
4142
// through it by 1. The leaf is black, so there are more things to fix.
42-
delete_case1(leaf);
43+
const subtree = delete_case1(leaf);
4344

4445
// Delete mocked leaf
4546
prune(leaf);
47+
return subtree;
4648
};
4749

4850
export default delete_no_child;

src/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ export {default as insert_case4} from './insertion/insert_case4.js';
2626
export {default as rotate_left} from './rotate/rotate_left.js';
2727
export {default as rotate_right} from './rotate/rotate_right.js';
2828
export {default as search} from './search/search.js';
29+
export {default as swap_color} from './swap/swap_color.js';
30+
export {default as swap_left} from './swap/swap_left.js';
31+
export {default as swap_non_adjacent} from './swap/swap_non_adjacent.js';
2932
export {default as inordertraversal} from './traversal/inordertraversal.js';
3033
export {default as rangetraversal} from './traversal/rangetraversal.js';
3134
export {default as Node} from './types/Node.js';

src/insertion/insert_case0.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import insert_case1 from './insert_case1.js';
1010
* - n's children are BLACK
1111
*
1212
* @param {Node} n - The input node.
13+
* @return {Node} The root of the modified subtree.
1314
*/
1415
const insert_case0 = (n) => {
1516
assert(n instanceof Node);
@@ -23,8 +24,12 @@ const insert_case0 = (n) => {
2324
* / \
2425
* - -
2526
*/
26-
if (n.parent === null) n._color = BLACK;
27-
else insert_case1(n);
27+
if (n.parent === null) {
28+
n._color = BLACK;
29+
return n;
30+
}
31+
32+
return insert_case1(n);
2833
};
2934

3035
export default insert_case0;

src/insertion/insert_case1.js

+2-3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import insert_case2 from './insert_case2.js';
1111
* - n is not the root of the tree.
1212
*
1313
* @param {Node} n - The input node.
14+
* @return {Node} The root of the modified subtree.
1415
*/
1516
const insert_case1 = (n) => {
1617
assert(n instanceof Node);
@@ -28,9 +29,7 @@ const insert_case1 = (n) => {
2829
* / \
2930
* - -
3031
*/
31-
if (n.parent._color === BLACK) return;
32-
33-
insert_case2(n);
32+
return n.parent._color === BLACK ? n : insert_case2(n);
3433
};
3534

3635
export default insert_case1;

src/insertion/insert_case2.js

+5-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import insert_case3 from './insert_case3.js';
1515
* - n's parent is red.
1616
*
1717
* @param {Node} n - The input node.
18+
* @return {Node} The root of the modified subtree.
1819
*/
1920
const insert_case2 = (n) => {
2021
assert(n instanceof Node);
@@ -45,8 +46,10 @@ const insert_case2 = (n) => {
4546
u._color = BLACK;
4647
const g = grandparent(n);
4748
g._color = RED;
48-
insert_case0(g);
49-
} else insert_case3(n);
49+
return insert_case0(g);
50+
}
51+
52+
return insert_case3(n);
5053
};
5154

5255
export default insert_case2;

0 commit comments

Comments
 (0)