diff --git a/mangle.json b/mangle.json
index 2b50a8f..fc409ca 100644
--- a/mangle.json
+++ b/mangle.json
@@ -2,14 +2,13 @@
"minify": {
"mangle": {
"properties": {
- "regex": "^(_color|isLeaf)$"
+ "regex": "^_color$"
}
}
},
"props": {
"props": {
- "$_color": "c",
- "$isLeaf": "L"
+ "$_color": "c"
}
}
}
\ No newline at end of file
diff --git a/package.json b/package.json
index fd879b4..d88c7fb 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
"build-gh-pages": "npm run build-docs",
"ci:test": "npm run lint-config && npm run lint && npm run cover",
"commit-msg": "commitlint --edit",
- "cover": "c8 --all --src src --reporter=lcov npm test",
+ "cover": "NODE_ENV=cover c8 --all --src src --reporter=lcov npm test",
"debug": "NODE_ENV=debug npm run test -- -st --fail-fast",
"dev": "npm run lint-config-and-fix && npm run lint-and-fix && npm run cover -- -- -st --fail-fast",
"install-hooks": "husky install",
@@ -104,9 +104,18 @@
[
"@babel/preset-env",
{
- "targets": [
- "defaults",
- "maintained node versions"
+ "targets": "current node"
+ }
+ ]
+ ],
+ "plugins": [
+ [
+ "transform-remove-console",
+ {
+ "exclude": [
+ "log",
+ "error",
+ "warn"
]
}
]
@@ -114,23 +123,6 @@
"env": {
"debug": {
"presets": [
- [
- "@babel/preset-env",
- {
- "targets": "current node"
- }
- ],
- "babel-preset-power-assert"
- ]
- },
- "test": {
- "presets": [
- [
- "@babel/preset-env",
- {
- "targets": "current node"
- }
- ],
"babel-preset-power-assert"
],
"plugins": [
@@ -138,6 +130,7 @@
"transform-remove-console",
{
"exclude": [
+ "debug",
"log",
"error",
"warn"
@@ -146,36 +139,45 @@
]
]
},
- "development": {
+ "test": {
"presets": [
"babel-preset-power-assert"
- ],
- "plugins": [
+ ]
+ },
+ "cover": {
+ "sourceMaps": "inline",
+ "presets": [
+ "babel-preset-power-assert"
+ ]
+ },
+ "development": {
+ "presets": [
[
- "transform-remove-console",
+ "@babel/preset-env",
{
- "exclude": [
- "log",
- "error",
- "warn"
+ "targets": [
+ "defaults",
+ "maintained node versions"
]
}
- ]
+ ],
+ "babel-preset-power-assert"
]
},
"production": {
- "plugins": [
- "babel-plugin-unassert",
+ "presets": [
[
- "transform-remove-console",
+ "@babel/preset-env",
{
- "exclude": [
- "log",
- "error",
- "warn"
+ "targets": [
+ "defaults",
+ "maintained node versions"
]
}
]
+ ],
+ "plugins": [
+ "babel-plugin-unassert"
]
}
}
diff --git a/src/debug/_debug.js b/src/debug/_debug.js
index 5dcbecf..75873a4 100644
--- a/src/debug/_debug.js
+++ b/src/debug/_debug.js
@@ -1,6 +1,5 @@
import assert from 'assert';
import Node from '../types/Node.js';
-import Leaf from '../types/Leaf.js';
import BLACK from '../color/BLACK.js';
/**
@@ -14,15 +13,11 @@ const _debug = ({red, black}) => {
* Recursively constructs a prettyprint string for the red-black tree rooted at
* root
.
*
- * @param {Node|Leaf} root - The root of the tree.
+ * @param {Node} root - The root of the tree.
* @returns {string}
*/
const debug = (root) => {
- assert(root instanceof Node || root instanceof Leaf);
- if (root.isLeaf()) {
- assert(root instanceof Leaf);
- return black('L');
- }
+ if (root === null) return black('L');
assert(root instanceof Node);
diff --git a/src/deletion/delete_case0.js b/src/deletion/delete_case0.js
new file mode 100644
index 0000000..22e1ddb
--- /dev/null
+++ b/src/deletion/delete_case0.js
@@ -0,0 +1,23 @@
+import assert from 'assert';
+import BLACK from '../color/BLACK.js';
+import Node from '../types/Node.js';
+import delete_case1 from './delete_case1.js';
+
+/**
+ * Preconditions:
+ * - n is black
+ * - all root-leaf paths going through n have a black height of b - 1
+ * - all other root-leaf paths have a black height of b
+ *
+ * @param {Node} n - The input node.
+ */
+const delete_case0 = (n) => {
+ assert(n instanceof Node);
+ assert(n._color === BLACK);
+ // If n is the root, there is nothing to do:
+ // - all paths go through n, and
+ // - n is black.
+ if (n.parent !== null) delete_case1(n);
+};
+
+export default delete_case0;
diff --git a/src/deletion/delete_case1.js b/src/deletion/delete_case1.js
index f1fbffa..3b08cb4 100644
--- a/src/deletion/delete_case1.js
+++ b/src/deletion/delete_case1.js
@@ -1,24 +1,52 @@
import assert from 'assert';
import BLACK from '../color/BLACK.js';
+import RED from '../color/RED.js';
import Node from '../types/Node.js';
-import Leaf from '../types/Leaf.js';
+import rotate_left from '../rotate/rotate_left.js';
+import rotate_right from '../rotate/rotate_right.js';
+import sibling from '../family/sibling.js';
+
import delete_case2 from './delete_case2.js';
+import delete_case3 from './delete_case3.js';
/**
* Preconditions:
* - n is black
* - all root-leaf paths going through n have a black height of b - 1
* - all other root-leaf paths have a black height of b
+ * - n is not the root
*
- * @param {Node|Leaf} n - The input node.
+ * @param {Node} n - The input node.
*/
const delete_case1 = (n) => {
- assert(n instanceof Node || n instanceof Leaf);
+ assert(n instanceof Node);
assert(n._color === BLACK);
- // If n is the root, there is nothing to do:
- // - all paths go through n, and
- // - n is black.
- if (n.parent !== null) delete_case2(n);
+ assert(n.parent !== null);
+
+ const s = sibling(n);
+ assert(s instanceof Node);
+
+ /**
+ * If n's sibling is red, prepare for and go to case 4.
+ *
+ * B B
+ * / \ / \
+ * >B R R B
+ * / \ / \ --> / \ / \
+ * - - B B >B B = =
+ * / \ / \ / \ / \
+ * = = = = - - = =
+ */
+ if (s._color === RED) {
+ n.parent._color = RED;
+ s._color = BLACK;
+ if (n === n.parent.left) rotate_left(n.parent);
+ else rotate_right(n.parent);
+ delete_case3(n);
+ }
+
+ // Otherwise, go to case 3.
+ else delete_case2(n);
};
export default delete_case1;
diff --git a/src/deletion/delete_case2.js b/src/deletion/delete_case2.js
index f77604a..3e85560 100644
--- a/src/deletion/delete_case2.js
+++ b/src/deletion/delete_case2.js
@@ -2,13 +2,10 @@ import assert from 'assert';
import BLACK from '../color/BLACK.js';
import RED from '../color/RED.js';
import Node from '../types/Node.js';
-import Leaf from '../types/Leaf.js';
-import rotate_left from '../rotate/rotate_left.js';
-import rotate_right from '../rotate/rotate_right.js';
import sibling from '../family/sibling.js';
+import delete_case0 from './delete_case0.js';
import delete_case3 from './delete_case3.js';
-import delete_case4 from './delete_case4.js';
/**
* Preconditions:
@@ -16,36 +13,41 @@ import delete_case4 from './delete_case4.js';
* - all root-leaf paths going through n have a black height of b - 1
* - all other root-leaf paths have a black height of b
* - n is not the root
+ * - n's sibling is black
*
- * @param {Node|Leaf} n - The input node.
+ * @param {Node} n - The input node.
*/
const delete_case2 = (n) => {
- assert(n instanceof Node || n instanceof Leaf);
+ assert(n instanceof Node);
assert(n._color === BLACK);
assert(n.parent !== null);
-
const s = sibling(n);
+ assert(s instanceof Node);
+ assert(s._color === BLACK);
/**
- * If n's sibling is red, prepare for and go to case 4.
+ * If n's parent is black and n's sibling's children are black, then
+ * repaint n's sibling red. Now all root-leaf paths going through n's
+ * parent have a black height of b - 1. We recurse thus on n's parent.
*
- * B B
+ * B >B
* / \ / \
- * >B R R B
- * / \ / \ --> / \ / \
- * - - B B >B B = =
- * / \ / \ / \ / \
- * = = = = - - = =
+ * >B B B R
+ * / \ / \ --> / \ / \
+ * - - B B - - B B
+ * / \ / \ / \ / \
+ * - - - - - - - -
*/
- if (s._color === RED) {
- n.parent._color = RED;
- s._color = BLACK;
- if (n === n.parent.left) rotate_left(n.parent);
- else rotate_right(n.parent);
- delete_case4(n);
+ if (
+ n.parent._color === BLACK &&
+ (s.left === null || s.left._color === BLACK) &&
+ (s.right === null || s.right._color === BLACK)
+ ) {
+ s._color = RED;
+ delete_case0(n.parent);
}
- // Otherwise, go to case 3.
+ // Otherwise, go to case 4.
else delete_case3(n);
};
diff --git a/src/deletion/delete_case3.js b/src/deletion/delete_case3.js
index c8985ac..bbeb718 100644
--- a/src/deletion/delete_case3.js
+++ b/src/deletion/delete_case3.js
@@ -2,10 +2,8 @@ import assert from 'assert';
import BLACK from '../color/BLACK.js';
import RED from '../color/RED.js';
import Node from '../types/Node.js';
-import Leaf from '../types/Leaf.js';
import sibling from '../family/sibling.js';
-import delete_case1 from './delete_case1.js';
import delete_case4 from './delete_case4.js';
/**
@@ -15,40 +13,48 @@ import delete_case4 from './delete_case4.js';
* - all other root-leaf paths have a black height of b
* - n is not the root
* - n's sibling is black
+ * - n's parent and n's sibling's children cannot all be black
*
- * @param {Node|Leaf} n - The input node.
+ * @param {Node} n - The input node.
*/
const delete_case3 = (n) => {
- assert(n instanceof Node || n instanceof Leaf);
+ assert(n instanceof Node);
assert(n._color === BLACK);
assert(n.parent !== null);
const s = sibling(n);
assert(s instanceof Node);
assert(s._color === BLACK);
+ assert(
+ n.parent._color === RED ||
+ s.left?._color === RED ||
+ s.right?._color === RED,
+ );
/**
- * If n's parent is black and n's sibling's children are black, then
- * repaint n's sibling red. Now all root-leaf paths going through n's
- * parent have a black height of b - 1. We recurse thus on n's parent.
+ * If n's parent is red and n's sibling's children are black, then swap n's
+ * parent and n's sibling color. All root-leaf paths going through n have
+ * now a black height of b. All other root-leaf paths have their black
+ * height unchanged. Red-black properties are respected. We are done.
*
- * B >B
+ * R B
* / \ / \
- * >B B B R
- * / \ / \ --> / \ / \
- * - - B B - - B B
- * / \ / \ / \ / \
- * - - - - - - - -
+ * >B B >B R
+ * / \ / \ --> / \ / \
+ * - - B B - - B B
+ * / \ / \ / \ / \
+ * - - - - - - - -
*/
if (
- n.parent._color === BLACK &&
- s.left._color === BLACK &&
- s.right._color === BLACK
+ // The parent color test is always true when coming from case 2
+ n.parent._color === RED &&
+ (s.left === null || s.left._color === BLACK) &&
+ (s.right === null || s.right._color === BLACK)
) {
s._color = RED;
- delete_case1(n.parent);
+ n.parent._color = BLACK;
}
- // Otherwise, go to case 4.
+ // Otherwise, go to case 5.
else delete_case4(n);
};
diff --git a/src/deletion/delete_case4.js b/src/deletion/delete_case4.js
index 713f57a..e57911d 100644
--- a/src/deletion/delete_case4.js
+++ b/src/deletion/delete_case4.js
@@ -2,7 +2,8 @@ import assert from 'assert';
import BLACK from '../color/BLACK.js';
import RED from '../color/RED.js';
import Node from '../types/Node.js';
-import Leaf from '../types/Leaf.js';
+import rotate_left from '../rotate/rotate_left.js';
+import rotate_right from '../rotate/rotate_right.js';
import sibling from '../family/sibling.js';
import delete_case5 from './delete_case5.js';
@@ -14,47 +15,59 @@ import delete_case5 from './delete_case5.js';
* - all other root-leaf paths have a black height of b
* - n is not the root
* - n's sibling is black
- * - n's parent and n's sibling's children cannot all be black
+ * - at least one of n's sibling's children is red
*
- * @param {Node|Leaf} n - The input node.
+ * @param {Node} n - The input node.
*/
const delete_case4 = (n) => {
- assert(n instanceof Node || n instanceof Leaf);
+ assert(n instanceof Node);
assert(n._color === BLACK);
assert(n.parent !== null);
const s = sibling(n);
assert(s instanceof Node);
assert(s._color === BLACK);
- assert(
- n.parent._color === RED || s.left._color === RED || s.right._color === RED,
- );
+ assert(s.left?._color === RED || s.right?._color === RED);
+
+ // The following statements just force the red n's sibling child to be on
+ // the left of the left of the parent, or right of the right, so case 6
+ // will rotate correctly.
/**
- * If n's parent is red and n's sibling's children are black, then swap n's
- * parent and n's sibling color. All root-leaf paths going through n have
- * now a black height of b. All other root-leaf paths have their black
- * height unchanged. Red-black properties are respected. We are done.
- *
- * R B
+ * ? ?
* / \ / \
- * >B B >B R
+ * >B B >B B
* / \ / \ --> / \ / \
- * - - B B - - B B
- * / \ / \ / \ / \
- * - - - - - - - -
+ * - - R B - - = R
+ * / \ / \ / \
+ * = = - - = B
+ * / \
+ * - -
*/
- if (
- // The parent color test is always true when coming from case 2
- n.parent._color === RED &&
- s.left._color === BLACK &&
- s.right._color === BLACK
+ if (n === n.parent.left && (s.right === null || s.right._color === BLACK)) {
+ s._color = RED;
+ s.left._color = BLACK;
+ rotate_right(s);
+ } else if (
+ n === n.parent.right &&
+ (s.left === null || s.left._color === BLACK)
) {
+ /**
+ * ? ?
+ * / \ / \
+ * B >B B >B
+ * / \ / \ --> / \ / \
+ * B R - - R = - -
+ * / \ / \ / \
+ * - - = = B =
+ * / \
+ * - -
+ */
s._color = RED;
- n.parent._color = BLACK;
+ s.right._color = BLACK;
+ rotate_left(s);
}
- // Otherwise, go to case 5.
- else delete_case5(n);
+ delete_case5(n);
};
export default delete_case4;
diff --git a/src/deletion/delete_case5.js b/src/deletion/delete_case5.js
index 1969d86..93e5eb2 100644
--- a/src/deletion/delete_case5.js
+++ b/src/deletion/delete_case5.js
@@ -2,13 +2,10 @@ import assert from 'assert';
import BLACK from '../color/BLACK.js';
import RED from '../color/RED.js';
import Node from '../types/Node.js';
-import Leaf from '../types/Leaf.js';
import rotate_left from '../rotate/rotate_left.js';
import rotate_right from '../rotate/rotate_right.js';
import sibling from '../family/sibling.js';
-import delete_case6 from './delete_case6.js';
-
/**
* Preconditions:
* - n is black
@@ -16,56 +13,52 @@ import delete_case6 from './delete_case6.js';
* - all other root-leaf paths have a black height of b
* - n is not the root
* - n's sibling is black
- * - at least one of n's sibling's children is red
+ * - if n is a left child, the right child of n's sibling is red
+ * - if n is a right child, the left child of n's sibling is red
*
- * @param {Node|Leaf} n - The input node.
+ * @param {Node} n - The input node.
*/
const delete_case5 = (n) => {
- assert(n instanceof Node || n instanceof Leaf);
+ assert(n instanceof Node);
assert(n._color === BLACK);
assert(n.parent !== null);
const s = sibling(n);
assert(s instanceof Node);
assert(s._color === BLACK);
- assert(s.left._color === RED || s.right._color === RED);
-
- // The following statements just force the red n's sibling child to be on
- // the left of the left of the parent, or right of the right, so case 6
- // will rotate correctly.
/**
- * ? ?
- * / \ / \
- * >B B >B B
- * / \ / \ --> / \ / \
- * - - R B - - = R
- * / \ / \ / \
- * = = - - = B
- * / \
- * - -
+ * Increment the black height of all root-leaf paths going through n by
+ * rotating at n's parent. This decrements the black height of all
+ * root-leaft paths going through n's sibling's right child.
+ * We can repaint n's sibling's right child in black to fix this.
+ * We are done.
+ *
+ * ? ?
+ * / \ / \
+ * >B B B B
+ * / \ / \ / \ / \
+ * - - = R --> >B = = B
+ * / \ / \ / \
+ * = B - - - -
+ * / \
+ * - -
*/
- if (n === n.parent.left && s.right._color === BLACK) {
- s._color = RED;
- s.left._color = BLACK;
- rotate_right(s);
- } else if (n === n.parent.right && s.left._color === BLACK) {
- /**
- * ? ?
- * / \ / \
- * B >B B >B
- * / \ / \ --> / \ / \
- * B R - - R = - -
- * / \ / \ / \
- * - - = = B =
- * / \
- * - -
- */
- s._color = RED;
+
+ s._color = n.parent._color;
+ n.parent._color = BLACK;
+
+ if (n === n.parent.left) {
+ assert(s.right._color === RED);
s.right._color = BLACK;
- rotate_left(s);
+ rotate_left(n.parent);
}
- delete_case6(n);
+ // Symmetric case
+ else {
+ assert(s.left._color === RED);
+ s.left._color = BLACK;
+ rotate_right(n.parent);
+ }
};
export default delete_case5;
diff --git a/src/deletion/delete_case6.js b/src/deletion/delete_case6.js
deleted file mode 100644
index 9f5f2c4..0000000
--- a/src/deletion/delete_case6.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import assert from 'assert';
-import BLACK from '../color/BLACK.js';
-import RED from '../color/RED.js';
-import Node from '../types/Node.js';
-import Leaf from '../types/Leaf.js';
-import rotate_left from '../rotate/rotate_left.js';
-import rotate_right from '../rotate/rotate_right.js';
-import sibling from '../family/sibling.js';
-
-/**
- * Preconditions:
- * - n is black
- * - all root-leaf paths going through n have a black height of b - 1
- * - all other root-leaf paths have a black height of b
- * - n is not the root
- * - n's sibling is black
- * - if n is a left child, the right child of n's sibling is red
- * - if n is a right child, the left child of n's sibling is red
- *
- * @param {Node|Leaf} n - The input node.
- */
-const delete_case6 = (n) => {
- assert(n instanceof Node || n instanceof Leaf);
- assert(n._color === BLACK);
- assert(n.parent !== null);
- const s = sibling(n);
- assert(s instanceof Node);
- assert(s._color === BLACK);
-
- /**
- * Increment the black height of all root-leaf paths going through n by
- * rotating at n's parent. This decrements the black height of all
- * root-leaft paths going through n's sibling's right child.
- * We can repaint n's sibling's right child in black to fix this.
- * We are done.
- *
- * ? ?
- * / \ / \
- * >B B B B
- * / \ / \ / \ / \
- * - - = R --> >B = = B
- * / \ / \ / \
- * = B - - - -
- * / \
- * - -
- */
-
- s._color = n.parent._color;
- n.parent._color = BLACK;
-
- if (n === n.parent.left) {
- assert(s.right._color === RED);
- s.right._color = BLACK;
- rotate_left(n.parent);
- }
-
- // Symmetric case
- else {
- assert(s.left._color === RED);
- s.left._color = BLACK;
- rotate_right(n.parent);
- }
-};
-
-export default delete_case6;
diff --git a/src/deletion/delete_no_child.js b/src/deletion/delete_no_child.js
new file mode 100644
index 0000000..7b00891
--- /dev/null
+++ b/src/deletion/delete_no_child.js
@@ -0,0 +1,49 @@
+import assert from 'assert';
+import BLACK from '../color/BLACK.js';
+import RED from '../color/RED.js';
+import Node from '../types/Node.js';
+
+import replace_node from './replace_node.js';
+import delete_case1 from './delete_case1.js';
+
+import prune from './prune.js';
+
+/**
+ * Delete a node n
that has no non-leaf child.
+ *
+ * Precondition:
+ * - n has no non-leaf child.
+ * - n is not the root
+ *
+ * @param {Node} n - The node to delete.
+ */
+const delete_no_child = (n) => {
+ assert(n instanceof Node);
+ assert(n.parent !== null);
+ assert(n.left === null);
+ assert(n.right === null);
+
+ if (n._color === RED) {
+ prune(n);
+ return;
+ }
+
+ assert(n._color === BLACK);
+
+ // Mock leaf since there is no left child
+ // We use key = n.key to avoid mixing types, but this property is never
+ // accessed.
+ const leaf = new Node(BLACK, n.key);
+
+ // Replace n with the mocked leaf
+ replace_node(n, leaf);
+
+ // If n is black, deleting it reduces the black-height of every path going
+ // through it by 1. The leaf is black, so there are more things to fix.
+ delete_case1(leaf);
+
+ // Delete mocked leaf
+ prune(leaf);
+};
+
+export default delete_no_child;
diff --git a/src/deletion/delete_one_child.js b/src/deletion/delete_one_child.js
index 382504e..5ab00f6 100644
--- a/src/deletion/delete_one_child.js
+++ b/src/deletion/delete_one_child.js
@@ -2,52 +2,39 @@ import assert from 'assert';
import BLACK from '../color/BLACK.js';
import RED from '../color/RED.js';
import Node from '../types/Node.js';
-import Leaf from '../types/Leaf.js';
import replace_node from './replace_node.js';
-import delete_case2 from './delete_case2.js';
/**
- * Delete a node n
that has at most a single non-leaf child.
+ * Delete a node n
with one non-leaf left child and one leaf right
+ * child.
*
* Precondition:
- * - n has at most one non-leaf child.
+ * - n has exactly one non-leaf child.
* - n is not the root
- * - if n has a non-leaf child, then it is its left child.
+ * - n's only non-leaf child is n's left child.
* - hence, n's right child is a leaf
+ * - hence, n's left child is RED
+ * - hence, n is BLACK
*
* @param {Node} n - The node to delete.
*/
const delete_one_child = (n) => {
assert(n instanceof Node);
+ assert(n._color === BLACK);
assert(n.parent !== null);
- // Precondition: n's right child is a leaf.
- // The right child of n is always a LEAF because either n is a subtree
- // predecessor or it is the only child of its parent by the red-black tree
- // properties
- assert(n.right instanceof Leaf);
+ assert(n.left instanceof Node);
+ assert(n.left._color === RED);
+ assert(n.right === null);
const child = n.left;
-
- // Replace n with its left child
- replace_node(n, child);
-
// If n is black, deleting it reduces the black-height of every path going
// through it by 1.
- if (n._color === BLACK) {
- // We can easily fix this when its left child is an
- // internal red node: change the color of the left child to black and
- // replace n with it.
- if (child._color === RED) child._color = BLACK;
- // Otherwise, there are more things to fix.
- else {
- delete_case2(child);
- }
- } else {
- // If n is red then its child can only be black. Replacing n with its
- // child suffices. This is a NO-OP.
- assert(child._color === BLACK);
- }
+ // We can easily fix this when its only child is an
+ // internal RED node: change the color of the child to black and
+ // replace n with it.
+ replace_node(n, child);
+ child._color = BLACK;
};
export default delete_one_child;
diff --git a/src/deletion/prune.js b/src/deletion/prune.js
new file mode 100644
index 0000000..74853ad
--- /dev/null
+++ b/src/deletion/prune.js
@@ -0,0 +1,17 @@
+import assert from 'assert';
+import Node from '../types/Node.js';
+
+/**
+ * Prune subtree rooted at input node.
+ *
+ * @param {Node} root - The root of the subtree to prune.
+ */
+const prune = (root) => {
+ assert(root instanceof Node);
+ assert(root.parent !== null);
+
+ if (root === root.parent.left) root.parent.left = null;
+ else root.parent.right = null;
+};
+
+export default prune;
diff --git a/src/deletion/replace_node.js b/src/deletion/replace_node.js
index ae4c302..1f480f6 100644
--- a/src/deletion/replace_node.js
+++ b/src/deletion/replace_node.js
@@ -1,17 +1,16 @@
import assert from 'assert';
import Node from '../types/Node.js';
-import Leaf from '../types/Leaf.js';
/**
* Replaces node A
by node B
.
*
* @param {Node} A - The node to replace.
- * @param {Node|Leaf} B - The replacement node.
+ * @param {Node} B - The replacement node.
*/
const replace_node = (A, B) => {
assert(A instanceof Node);
- assert(B instanceof Node || B instanceof Leaf);
- // We never apply delete_one_child on the root
+ assert(B instanceof Node);
+ // We never apply delete_one_child or delete_no_child on the root
assert(A.parent !== null);
if (A === A.parent.left) A.parent.left = B;
diff --git a/src/family/predecessor.js b/src/family/predecessor.js
index 27f8159..b62a3fe 100644
--- a/src/family/predecessor.js
+++ b/src/family/predecessor.js
@@ -12,7 +12,7 @@ const predecessor = (node) => {
assert(node.left instanceof Node);
let pred = node.left;
- while (!pred.right.isLeaf()) {
+ while (pred.right !== null) {
assert(pred.right instanceof Node);
pred = pred.right;
}
diff --git a/src/family/sibling.js b/src/family/sibling.js
index 4616602..7d40268 100644
--- a/src/family/sibling.js
+++ b/src/family/sibling.js
@@ -1,16 +1,15 @@
import assert from 'assert';
import Node from '../types/Node.js';
-import Leaf from '../types/Leaf.js';
/**
* Computes the sibling of the input node.
*
- * @param {Node|Leaf} node - The input node.
- * @returns {Node|Leaf}
+ * @param {Node} node - The input node.
+ * @returns {Node}
*/
const sibling = (node) => {
- assert(node instanceof Node || node instanceof Leaf);
- // We only use this function when node HAS a sibling.
+ assert(node instanceof Node);
+ // We only use this function when node HAS a non-leaf sibling.
assert(node.parent !== null);
return node === node.parent.left ? node.parent.right : node.parent.left;
diff --git a/src/family/uncle.js b/src/family/uncle.js
index 4bb1454..9698487 100644
--- a/src/family/uncle.js
+++ b/src/family/uncle.js
@@ -1,6 +1,5 @@
import assert from 'assert';
import Node from '../types/Node.js';
-import Leaf from '../types/Leaf.js';
import grandparent from './grandparent.js';
/**
@@ -8,15 +7,13 @@ import grandparent from './grandparent.js';
* exist.
*
* @param {Node} node - The input node.
- * @returns {Node|Leaf}
+ * @returns {Node}
*/
const uncle = (node) => {
assert(node instanceof Node);
const g = grandparent(node);
assert(g !== null);
- const u = node.parent === g.left ? g.right : g.left;
- assert(u instanceof Node || u instanceof Leaf);
- return u;
+ return node.parent === g.left ? g.right : g.left;
};
export default uncle;
diff --git a/src/index.js b/src/index.js
index 0c764a6..e3d1f23 100644
--- a/src/index.js
+++ b/src/index.js
@@ -3,29 +3,30 @@ export {default as from} from './api/from.js';
export {default as BLACK} from './color/BLACK.js';
export {default as RED} from './color/RED.js';
export {default as _debug} from './debug/_debug.js';
+export {default as delete_case0} from './deletion/delete_case0.js';
export {default as delete_case1} from './deletion/delete_case1.js';
export {default as delete_case2} from './deletion/delete_case2.js';
export {default as delete_case3} from './deletion/delete_case3.js';
export {default as delete_case4} from './deletion/delete_case4.js';
export {default as delete_case5} from './deletion/delete_case5.js';
-export {default as delete_case6} from './deletion/delete_case6.js';
+export {default as delete_no_child} from './deletion/delete_no_child.js';
export {default as delete_one_child} from './deletion/delete_one_child.js';
+export {default as prune} from './deletion/prune.js';
export {default as replace_node} from './deletion/replace_node.js';
export {default as grandparent} from './family/grandparent.js';
export {default as predecessor} from './family/predecessor.js';
export {default as sibling} from './family/sibling.js';
export {default as uncle} from './family/uncle.js';
export {default as insert} from './insertion/insert.js';
+export {default as insert_case0} from './insertion/insert_case0.js';
export {default as insert_case1} from './insertion/insert_case1.js';
export {default as insert_case2} from './insertion/insert_case2.js';
export {default as insert_case3} from './insertion/insert_case3.js';
export {default as insert_case4} from './insertion/insert_case4.js';
-export {default as insert_case5} from './insertion/insert_case5.js';
export {default as rotate_left} from './rotate/rotate_left.js';
export {default as rotate_right} from './rotate/rotate_right.js';
export {default as search} from './search/search.js';
export {default as inordertraversal} from './traversal/inordertraversal.js';
export {default as rangetraversal} from './traversal/rangetraversal.js';
-export {default as Leaf} from './types/Leaf.js';
export {default as Node} from './types/Node.js';
export {default as RedBlackTree} from './types/RedBlackTree.js';
diff --git a/src/insertion/insert.js b/src/insertion/insert.js
index 854f58d..f41d3c6 100644
--- a/src/insertion/insert.js
+++ b/src/insertion/insert.js
@@ -10,7 +10,7 @@ import Node from '../types/Node.js';
* search tree.
* For our red-black tree, all that is left to do is fix the red-black tree
* properties in case they have been violated by this insertion. This is fixed
- * by {@link insert_case1}.
+ * by {@link insert_case0}.
*
* @param {Function} compare - The comparison function to use.
* @param {Node} A - The root of the tree.
@@ -22,7 +22,7 @@ const insert = (compare, A, B) => {
if (compare(B.key, A.key) < 0) {
const node = A.left;
- if (node.isLeaf()) {
+ if (node === null) {
A.left = B;
break;
}
@@ -32,7 +32,7 @@ const insert = (compare, A, B) => {
} else {
const node = A.right;
- if (node.isLeaf()) {
+ if (node === null) {
A.right = B;
break;
}
diff --git a/src/insertion/insert_case0.js b/src/insertion/insert_case0.js
new file mode 100644
index 0000000..923093e
--- /dev/null
+++ b/src/insertion/insert_case0.js
@@ -0,0 +1,30 @@
+import assert from 'assert';
+import Node from '../types/Node.js';
+import BLACK from '../color/BLACK.js';
+import RED from '../color/RED.js';
+import insert_case1 from './insert_case1.js';
+
+/**
+ * Preconditions:
+ * - n is red.
+ * - n's children are BLACK
+ *
+ * @param {Node} n - The input node.
+ */
+const insert_case0 = (n) => {
+ assert(n instanceof Node);
+ assert(n._color === RED);
+ assert(n.left._color === BLACK);
+ assert(n.right._color === BLACK);
+ /**
+ * If n is the root of the tree, paint it black and we are done.
+ *
+ * >R
+ * / \
+ * - -
+ */
+ if (n.parent === null) n._color = BLACK;
+ else insert_case1(n);
+};
+
+export default insert_case0;
diff --git a/src/insertion/insert_case1.js b/src/insertion/insert_case1.js
index 1b0f2bf..56180a1 100644
--- a/src/insertion/insert_case1.js
+++ b/src/insertion/insert_case1.js
@@ -8,23 +8,29 @@ import insert_case2 from './insert_case2.js';
* Preconditions:
* - n is red.
* - n's children are BLACK
+ * - n is not the root of the tree.
*
* @param {Node} n - The input node.
*/
const insert_case1 = (n) => {
assert(n instanceof Node);
assert(n._color === RED);
- assert(n.left._color === BLACK);
- assert(n.right._color === BLACK);
+ assert(n.left === null || n.left._color === BLACK);
+ assert(n.right === null || n.right._color === BLACK);
+ assert(n.parent !== null);
+
/**
- * If n is the root of the tree, paint it black and we are done.
+ * If the parent of n is black then we have nothing to do.
*
- * >R
+ * B
+ * / \
+ * >R -
* / \
* - -
*/
- if (n.parent === null) n._color = BLACK;
- else insert_case2(n);
+ if (n.parent._color === BLACK) return;
+
+ insert_case2(n);
};
export default insert_case1;
diff --git a/src/insertion/insert_case2.js b/src/insertion/insert_case2.js
index 0f45433..22b522c 100644
--- a/src/insertion/insert_case2.js
+++ b/src/insertion/insert_case2.js
@@ -2,6 +2,9 @@ import assert from 'assert';
import Node from '../types/Node.js';
import BLACK from '../color/BLACK.js';
import RED from '../color/RED.js';
+import uncle from '../family/uncle.js';
+import grandparent from '../family/grandparent.js';
+import insert_case0 from './insert_case0.js';
import insert_case3 from './insert_case3.js';
/**
@@ -9,28 +12,40 @@ import insert_case3 from './insert_case3.js';
* - n is red.
* - n's children are BLACK
* - n is not the root of the tree.
+ * - n's parent is red.
*
* @param {Node} n - The input node.
*/
const insert_case2 = (n) => {
assert(n instanceof Node);
assert(n._color === RED);
- assert(n.left._color === BLACK);
- assert(n.right._color === BLACK);
+ assert(n.left === null || n.left._color === BLACK);
+ assert(n.right === null || n.right._color === BLACK);
assert(n.parent !== null);
+ assert(n.parent._color === RED);
+ const u = uncle(n);
/**
- * If the parent of n is black then we have nothing to do.
+ * If n has a non-leaf uncle and this uncle is red then we simply
+ * repaint the parent and the uncle of n in black, the grandparent of
+ * n in red, then call insert_case0 on n's grandparent.
*
- * B
- * / \
- * >R -
- * / \
- * - -
+ * B >R
+ * / \ / \
+ * R R B B
+ * / \ / \ --> / \ / \
+ * >R - - - R - - -
+ * / \ / \
+ * - - - -
*/
- if (n.parent._color === BLACK) return;
- insert_case3(n);
+ if (u !== null && u._color === RED) {
+ n.parent._color = BLACK;
+ u._color = BLACK;
+ const g = grandparent(n);
+ g._color = RED;
+ insert_case0(g);
+ } else insert_case3(n);
};
export default insert_case2;
diff --git a/src/insertion/insert_case3.js b/src/insertion/insert_case3.js
index 5d9b8bc..fbde812 100644
--- a/src/insertion/insert_case3.js
+++ b/src/insertion/insert_case3.js
@@ -2,9 +2,9 @@ import assert from 'assert';
import Node from '../types/Node.js';
import BLACK from '../color/BLACK.js';
import RED from '../color/RED.js';
-import uncle from '../family/uncle.js';
+import rotate_left from '../rotate/rotate_left.js';
+import rotate_right from '../rotate/rotate_right.js';
import grandparent from '../family/grandparent.js';
-import insert_case1 from './insert_case1.js';
import insert_case4 from './insert_case4.js';
/**
@@ -13,39 +13,80 @@ import insert_case4 from './insert_case4.js';
* - n's children are BLACK
* - n is not the root of the tree.
* - n's parent is red.
+ * - n's uncle is black.
+ *
+ * Here we fix the input subtree to pass the preconditions of {@link insert_case4}.
*
* @param {Node} n - The input node.
*/
const insert_case3 = (n) => {
assert(n instanceof Node);
assert(n._color === RED);
- assert(n.left._color === BLACK);
- assert(n.right._color === BLACK);
+ assert(n.left === null || n.left._color === BLACK);
+ assert(n.right === null || n.right._color === BLACK);
assert(n.parent !== null);
assert(n.parent._color === RED);
- const u = uncle(n);
+ const g = grandparent(n);
/**
- * If n has a non-leaf uncle and this uncle is red then we simply
- * repaint the parent and the uncle of n in black, the grandparent of
- * n in red, then call insert_case1 on n's grandparent.
+ * If the path from g to n makes a left-right, change it to a left-left
+ * with {@link rotate_left}. Then call {@link insert_case4} on the old
+ * parent of n.
*
- * B >R
+ * B B
* / \ / \
- * R R B B
+ * R B R B
* / \ / \ --> / \ / \
- * >R - - - R - - -
- * / \ / \
- * - - - -
+ * = >R - - >R = - -
+ * / \ / \
+ * = = = =
*/
- if (u._color === RED) {
- n.parent._color = BLACK;
- u._color = BLACK;
- const g = grandparent(n);
- g._color = RED;
- insert_case1(g);
- } else insert_case4(n);
+ if (n === n.parent.right && n.parent === g.left) {
+ rotate_left(n.parent);
+
+ /**
+ * Rotate_left can be the below because of already having *g = grandparent(n)
+ *
+ * saved_p=g.left, *saved_left_n=n.left;
+ * g.left=n;
+ * n.left=saved_p;
+ * saved_p.right=saved_left_n;
+ *
+ * and modify the parent's nodes properly
+ */
+
+ // n = n.left; /!\ need to fix rotate, so that we can safely reference a node
+ } else if (n === n.parent.left && n.parent === g.right) {
+ /**
+ * If the path from g to n makes a right-left, change it to a right-right
+ * with {@link rotate_right}. Then call {@link insert_case4} on the old
+ * parent of n.
+ *
+ * B B
+ * / \ / \
+ * B R B R
+ * / \ / \ --> / \ / \
+ * - - >R = - - = >R
+ * / \ / \
+ * = = = =
+ */
+ rotate_right(n.parent);
+
+ /**
+ * Rotate_right can be the below to take advantage of already having *g = grandparent(n)
+ *
+ * saved_p=g.right, *saved_right_n=n.right;
+ * g.right=n;
+ * n.right=saved_p;
+ * saved_p.left=saved_right_n;
+ *
+ */
+
+ // n = n.right ;
+ }
+
+ insert_case4(n);
};
export default insert_case3;
diff --git a/src/insertion/insert_case4.js b/src/insertion/insert_case4.js
index 97505f0..14fff50 100644
--- a/src/insertion/insert_case4.js
+++ b/src/insertion/insert_case4.js
@@ -5,7 +5,6 @@ import RED from '../color/RED.js';
import rotate_left from '../rotate/rotate_left.js';
import rotate_right from '../rotate/rotate_right.js';
import grandparent from '../family/grandparent.js';
-import insert_case5 from './insert_case5.js';
/**
* Preconditions:
@@ -14,79 +13,58 @@ import insert_case5 from './insert_case5.js';
* - n is not the root of the tree.
* - n's parent is red.
* - n's uncle is black.
- *
- * Here we fix the input subtree to pass the preconditions of {@link insert_case5}.
+ * - the path from n to its grandparent makes a left-left or right-right.
*
* @param {Node} n - The input node.
*/
const insert_case4 = (n) => {
assert(n instanceof Node);
assert(n._color === RED);
- assert(n.left._color === BLACK);
- assert(n.right._color === BLACK);
+ assert(n.left === null || n.left._color === BLACK);
+ assert(n.right === null || n.right._color === BLACK);
assert(n.parent !== null);
assert(n.parent._color === RED);
const g = grandparent(n);
- /**
- * If the path from g to n makes a left-right, change it to a left-left
- * with {@link rotate_left}. Then call {@link insert_case5} on the old
- * parent of n.
- *
- * B B
- * / \ / \
- * R B R B
- * / \ / \ --> / \ / \
- * = >R - - >R = - -
- * / \ / \
- * = = = =
- */
-
- if (n === n.parent.right && n.parent === g.left) {
- rotate_left(n.parent);
-
- /**
- * Rotate_left can be the below because of already having *g = grandparent(n)
- *
- * saved_p=g.left, *saved_left_n=n.left;
- * g.left=n;
- * n.left=saved_p;
- * saved_p.right=saved_left_n;
- *
- * and modify the parent's nodes properly
- */
+ // Repaint n's parent black, n's grandparent red
+ n.parent._color = BLACK;
+ g._color = RED;
- // n = n.left; /!\ need to fix rotate, so that we can safely reference a node
- } else if (n === n.parent.left && n.parent === g.right) {
+ if (n === n.parent.left) {
/**
- * If the path from g to n makes a right-left, change it to a right-right
- * with {@link rotate_right}. Then call {@link insert_case5} on the old
- * parent of n.
+ * If the path from g to n makes a left-left, {@link rotate_right} at g.
+ * We are done.
*
- * B B
+ * R B
* / \ / \
- * B R B R
+ * B B >R R
* / \ / \ --> / \ / \
- * - - >R = - - = >R
- * / \ / \
- * = = = =
+ * >R = - - = = = B
+ * / \ / \
+ * = = - -
*/
- rotate_right(n.parent);
-
+ assert(g.left instanceof Node);
+ assert(n === g.left.left);
+ assert(g.right === null || g.right._color === BLACK);
+ rotate_right(g);
+ } else {
/**
- * Rotate_right can be the below to take advantage of already having *g = grandparent(n)
- *
- * saved_p=g.right, *saved_right_n=n.right;
- * g.right=n;
- * n.right=saved_p;
- * saved_p.left=saved_right_n;
+ * If the path from g to n makes a right-right, {@link rotate_left} at g.
+ * We are done.
*
+ * R B
+ * / \ / \
+ * B B R >R
+ * / \ / \ --> / \ / \
+ * - - = >R B = = =
+ * / \ / \
+ * = = - -
*/
-
- // n = n.right ;
+ assert(g.right instanceof Node);
+ assert(n === g.right.right);
+ assert(g.left === null || g.left._color === BLACK);
+ rotate_left(g);
}
-
- insert_case5(n);
};
export default insert_case4;
diff --git a/src/insertion/insert_case5.js b/src/insertion/insert_case5.js
deleted file mode 100644
index 1aa439d..0000000
--- a/src/insertion/insert_case5.js
+++ /dev/null
@@ -1,70 +0,0 @@
-import assert from 'assert';
-import Node from '../types/Node.js';
-import BLACK from '../color/BLACK.js';
-import RED from '../color/RED.js';
-import rotate_left from '../rotate/rotate_left.js';
-import rotate_right from '../rotate/rotate_right.js';
-import grandparent from '../family/grandparent.js';
-
-/**
- * Preconditions:
- * - n is red.
- * - n's children are BLACK
- * - n is not the root of the tree.
- * - n's parent is red.
- * - n's uncle is black.
- * - the path from n to its grandparent makes a left-left or right-right.
- *
- * @param {Node} n - The input node.
- */
-const insert_case5 = (n) => {
- assert(n instanceof Node);
- assert(n._color === RED);
- assert(n.left._color === BLACK);
- assert(n.right._color === BLACK);
- assert(n.parent !== null);
- assert(n.parent._color === RED);
- const g = grandparent(n);
-
- // Repaint n's parent black, n's grandparent red
- n.parent._color = BLACK;
- g._color = RED;
-
- if (n === n.parent.left) {
- /**
- * If the path from g to n makes a left-left, {@link rotate_right} at g.
- * We are done.
- *
- * R B
- * / \ / \
- * B B >R R
- * / \ / \ --> / \ / \
- * >R = - - = = = B
- * / \ / \
- * = = - -
- */
- assert(g.left instanceof Node);
- assert(n === g.left.left);
- assert(g.right._color === BLACK);
- rotate_right(g);
- } else {
- /**
- * If the path from g to n makes a right-right, {@link rotate_left} at g.
- * We are done.
- *
- * R B
- * / \ / \
- * B B R >R
- * / \ / \ --> / \ / \
- * - - = >R B = = =
- * / \ / \
- * = = - -
- */
- assert(g.right instanceof Node);
- assert(n === g.right.right);
- assert(g.left._color === BLACK);
- rotate_left(g);
- }
-};
-
-export default insert_case5;
diff --git a/src/rotate/rotate_left.js b/src/rotate/rotate_left.js
index 51bf7ea..c645022 100644
--- a/src/rotate/rotate_left.js
+++ b/src/rotate/rotate_left.js
@@ -33,9 +33,9 @@ const rotate_left = (A) => {
B.left = a;
B.right = b;
- a.parent = B;
- b.parent = B;
- c.parent = A;
+ if (a !== null) a.parent = B;
+ if (b !== null) b.parent = B;
+ if (c !== null) c.parent = A;
};
export default rotate_left;
diff --git a/src/rotate/rotate_right.js b/src/rotate/rotate_right.js
index 9ad8cdb..ff0ef26 100644
--- a/src/rotate/rotate_right.js
+++ b/src/rotate/rotate_right.js
@@ -33,9 +33,9 @@ const rotate_right = (B) => {
A.left = b;
A.right = c;
- a.parent = B;
- b.parent = A;
- c.parent = A;
+ if (a !== null) a.parent = B;
+ if (b !== null) b.parent = A;
+ if (c !== null) c.parent = A;
};
export default rotate_right;
diff --git a/src/search/search.js b/src/search/search.js
index a400739..37de5b2 100644
--- a/src/search/search.js
+++ b/src/search/search.js
@@ -18,14 +18,11 @@ const search = (compare, root, key) => {
return root;
}
- const child = d < 0 ? root.left : root.right;
+ root = d < 0 ? root.left : root.right;
- if (child.isLeaf()) {
+ if (root === null) {
return null;
}
-
- assert(child instanceof Node);
- root = child;
}
};
diff --git a/src/traversal/inordertraversal.js b/src/traversal/inordertraversal.js
index 398ab56..a0c3451 100644
--- a/src/traversal/inordertraversal.js
+++ b/src/traversal/inordertraversal.js
@@ -9,7 +9,7 @@ import Node from '../types/Node.js';
*/
export default function* inordertraversal(node) {
assert(node instanceof Node);
- if (!node.left.isLeaf()) {
+ if (node.left !== null) {
// Yield the nodes on the left recursively. Those nodes are all smaller
// than (or equal to) the current node by the binary search tree
// properties.
@@ -20,7 +20,7 @@ export default function* inordertraversal(node) {
// Yield the current node.
yield node.key;
- if (!node.right.isLeaf()) {
+ if (node.right !== null) {
// Yield the nodes on the right recursively. Those nodes are all larger
// than (or equal to) the current node by the binary search tree
// properties.
diff --git a/src/traversal/rangetraversal.js b/src/traversal/rangetraversal.js
index d26307b..c4db2c7 100644
--- a/src/traversal/rangetraversal.js
+++ b/src/traversal/rangetraversal.js
@@ -15,27 +15,27 @@ export default function* rangetraversal(compare, root, left, right) {
if (compare(root.key, left) < 0) {
// If the root lies to the left of the interval, we can discard the
// entire left subtree.
- if (!root.right.isLeaf()) {
+ if (root.right !== null) {
assert(root.right instanceof Node);
yield* rangetraversal(compare, root.right, left, right);
}
} else if (compare(root.key, right) >= 0) {
// If the root lies to the right of the interval, we can discard the
// entire right subtree.
- if (!root.left.isLeaf()) {
+ if (root.left !== null) {
assert(root.left instanceof Node);
yield* rangetraversal(compare, root.left, left, right);
}
} else {
// Otherwise just recurse on both subtrees and yield the root in
// between.
- if (!root.left.isLeaf()) {
+ if (root.left !== null) {
assert(root.left instanceof Node);
yield* rangetraversal(compare, root.left, left, right);
}
yield root.key;
- if (!root.right.isLeaf()) {
+ if (root.right !== null) {
assert(root.right instanceof Node);
yield* rangetraversal(compare, root.right, left, right);
}
diff --git a/src/types/Leaf.js b/src/types/Leaf.js
deleted file mode 100644
index 3e8a0a1..0000000
--- a/src/types/Leaf.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import assert from 'assert';
-import BLACK from '../color/BLACK.js';
-import Node from './Node.js';
-
-/**
- * A black leaf node.
- *
- * @class
- * @param {Node} parent - The parent node in the tree.
- */
-export default function Leaf(parent) {
- assert(parent instanceof Node);
- /** @constant {number} The color of the node. */
- this._color = BLACK;
- /** @member {Node} The parent node. */
- this.parent = parent;
-}
-
-/**
- * Returns true
if the Leaf
object is a leaf. This
- * always returns true
.
- *
- * @returns {boolean}
- */
-Leaf.prototype.isLeaf = function () {
- return true;
-};
diff --git a/src/types/Node.js b/src/types/Node.js
index e4bc1aa..c74cd66 100644
--- a/src/types/Node.js
+++ b/src/types/Node.js
@@ -1,5 +1,3 @@
-import Leaf from './Leaf.js';
-
/**
* An internal node. This node can be red or black.
*
@@ -10,22 +8,12 @@ import Leaf from './Leaf.js';
export default function Node(color, key) {
/** @member {number} The color of the node. */
this._color = color;
- /** @member {Node|Leaf} The left child */
- this.left = new Leaf(this);
- /** @member {Node|Leaf} The right child */
- this.right = new Leaf(this);
+ /** @member {Node} The left child */
+ this.left = null;
+ /** @member {Node} The right child */
+ this.right = null;
/** @member {Node} The parent node. */
this.parent = null;
/** @member {any} The key held by this node. */
this.key = key;
}
-
-/**
- * Returns true
if the Node
object is a leaf. This
- * always returns false
.
- *
- * @returns {boolean}
- */
-Node.prototype.isLeaf = function () {
- return false;
-};
diff --git a/src/types/RedBlackTree.js b/src/types/RedBlackTree.js
index e0ec5b0..e161abc 100644
--- a/src/types/RedBlackTree.js
+++ b/src/types/RedBlackTree.js
@@ -1,12 +1,12 @@
import assert from 'assert';
import Node from './Node.js';
-import Leaf from './Leaf.js';
import BLACK from '../color/BLACK.js';
import RED from '../color/RED.js';
import predecessor from '../family/predecessor.js';
import insert from '../insertion/insert.js';
-import insert_case2 from '../insertion/insert_case2.js';
+import insert_case1 from '../insertion/insert_case1.js';
import delete_one_child from '../deletion/delete_one_child.js';
+import delete_no_child from '../deletion/delete_no_child.js';
import search from '../search/search.js';
import inordertraversal from '../traversal/inordertraversal.js';
import rangetraversal from '../traversal/rangetraversal.js';
@@ -22,6 +22,7 @@ export default class RedBlackTree {
* @param {Function} compare - The comparison function for node keys.
*/
constructor(compare) {
+ assert(compare instanceof Function);
/** @member {Function} The comparison function for node keys. */
this.compare = compare;
/** @member {Node} The root of the tree. */
@@ -48,7 +49,7 @@ export default class RedBlackTree {
} else {
const node = new Node(RED, key);
insert(this.compare, this.root, node);
- insert_case2(node);
+ insert_case1(node);
}
}
@@ -95,31 +96,31 @@ export default class RedBlackTree {
* @param {Node} node - The input node to delete.
*/
_delete(node) {
- if (!node.left.isLeaf()) {
+ assert(node instanceof Node);
+ if (node.left !== null) {
// Replace node's key with predecessor's key
const pred = predecessor(node);
node.key = pred.key;
// Delete predecessor node
- // note: this node can only have one non-leaf child
- // because the tree is a red-black tree
- delete_one_child(pred);
- } else if (!node.right.isLeaf()) {
+ // NOTE: this node can only have one non-leaf (left) child because
+ // of red-black tree invariant.
+ if (pred.left === null) {
+ delete_no_child(pred);
+ } else {
+ delete_one_child(pred);
+ }
+ } else if (node.right !== null) {
// Replace node's key with successor's key
- // If there is no left child, then there can only be one right
- // child.
+ // NOTE: Since there is no left child, then there can only be one
+ // right child by the red-black tree invariant.
const succ = node.right;
- assert(succ instanceof Node);
- assert(succ.left instanceof Leaf);
- assert(succ.right instanceof Leaf);
node.key = succ.key;
// Delete successor node
- // note: this node can only have one non-leaf child
- // because the tree is a red-black tree
- delete_one_child(succ);
+ delete_no_child(succ);
} else if (node === this.root) {
this.root = null;
} else {
- delete_one_child(node);
+ delete_no_child(node);
}
}