freeCodeCamp/curriculum/challenges/english/08-coding-interview-prep/data-structures/delete-a-node-with-two-chil...

216 lines
10 KiB
Markdown

---
id: 587d8258367417b2b2512c82
title: Delete a Node with Two Children in a Binary Search Tree
challengeType: 1
---
## Description
<section id='description'>
Removing nodes that have two children is the hardest case to implement. Removing a node like this produces two subtrees that are no longer connected to the original tree structure. How can we reconnect them? One method is to find the smallest value in the right subtree of the target node and replace the target node with this value. Selecting the replacement in this way ensures that it is greater than every node in the left subtree it becomes the new parent of but also less than every node in the right subtree it becomes the new parent of.
Once this replacement is made the replacement node must be removed from the right subtree. Even this operation is tricky because the replacement may be a leaf or it may itself be the parent of a right subtree. If it is a leaf we must remove its parent's reference to it. Otherwise, it must be the right child of the target. In this case, we must replace the target value with the replacement value and make the target reference the replacement's right child.
Instructions: Let's finish our <code>remove</code> method by handling the third case. We've provided some code again for the first two cases. Add some code now to handle target nodes with two children. Any edge cases to be aware of? What if the tree has only three nodes? Once you are finished this will complete our deletion operation for binary search trees. Nice job, this is a pretty hard problem!
</section>
## Instructions
<section id='instructions'>
</section>
## Tests
<section id='tests'>
```yml
tests:
- text: The <code>BinarySearchTree</code> data structure exists.
testString: assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() }; return (typeof test == 'object')})(), 'The <code>BinarySearchTree</code> data structure exists.');
- text: The binary search tree has a method called <code>remove</code>.
testString: assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; return (typeof test.remove == 'function')})(), 'The binary search tree has a method called <code>remove</code>.');
- text: Trying to remove an element that does not exist returns <code>null</code>.
testString: 'assert((function() { var test = false; if (typeof BinarySearchTree !== ''undefined'') { test = new BinarySearchTree() } else { return false; }; return (typeof test.remove == ''function'') ? (test.remove(100) == null) : false})(), ''Trying to remove an element that does not exist returns <code>null</code>.'');'
- text: If the root node has no children, deleting it sets the root to <code>null</code>.
testString: 'assert((function() { var test = false; if (typeof BinarySearchTree !== ''undefined'') { test = new BinarySearchTree() } else { return false; }; test.add(500); test.remove(500); return (typeof test.remove == ''function'') ? (test.inorder() == null) : false})(), ''If the root node has no children, deleting it sets the root to <code>null</code>.'');'
- text: The <code>remove</code> method removes leaf nodes from the tree
testString: 'assert((function() { var test = false; if (typeof BinarySearchTree !== ''undefined'') { test = new BinarySearchTree() } else { return false; }; test.add(5); test.add(3); test.add(7); test.add(6); test.add(10); test.add(12); test.remove(3); test.remove(12); test.remove(10); return (typeof test.remove == ''function'') ? (test.inorder().join('''') == ''567'') : false})(), ''The <code>remove</code> method removes leaf nodes from the tree'');'
- text: The <code>remove</code> method removes nodes with one child.
testString: assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; test.add(-1); test.add(3); test.add(7); test.add(16); test.remove(16); test.remove(7); test.remove(3); return (test.inorder().join('') == '-1'); })(), 'The <code>remove</code> method removes nodes with one child.');
- text: Removing the root in a tree with two nodes sets the second to be the root.
testString: assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; test.add(15); test.add(27); test.remove(15); return (test.inorder().join('') == '27'); })(), 'Removing the root in a tree with two nodes sets the second to be the root.');
- text: The <code>remove</code> method removes nodes with two children while maintaining the binary search tree structure.
testString: assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; test.add(1); test.add(4); test.add(3); test.add(7); test.add(9); test.add(11); test.add(14); test.add(15); test.add(19); test.add(50); test.remove(9); if (!test.isBinarySearchTree()) { return false; }; test.remove(11); if (!test.isBinarySearchTree()) { return false; }; test.remove(14); if (!test.isBinarySearchTree()) { return false; }; test.remove(19); if (!test.isBinarySearchTree()) { return false; }; test.remove(3); if (!test.isBinarySearchTree()) { return false; }; test.remove(50); if (!test.isBinarySearchTree()) { return false; }; test.remove(15); if (!test.isBinarySearchTree()) { return false; }; return (test.inorder().join('') == '147'); })(), 'The <code>remove</code> method removes nodes with two children while maintaining the binary search tree structure.');
- text: The root can be removed on a tree of three nodes.
testString: assert((function() { var test = false; if (typeof BinarySearchTree !== 'undefined') { test = new BinarySearchTree() } else { return false; }; if (typeof test.remove !== 'function') { return false; }; test.add(100); test.add(50); test.add(300); test.remove(100); return (test.inorder().join('') == 50300); })(), 'The root can be removed on a tree of three nodes.');
```
</section>
## Challenge Seed
<section id='challengeSeed'>
<div id='js-seed'>
```js
var displayTree = (tree) => console.log(JSON.stringify(tree, null, 2));
function Node(value) {
this.value = value;
this.left = null;
this.right = null;
}
function BinarySearchTree() {
this.root = null;
this.remove = function(value) {
if (this.root === null) {
return null;
}
var target;
var parent = null;
// find the target value and its parent
(function findValue(node = this.root) {
if (value == node.value) {
target = node;
} else if (value < node.value && node.left !== null) {
parent = node;
return findValue(node.left);
} else if (value < node.value && node.left === null) {
return null;
} else if (value > node.value && node.right !== null) {
parent = node;
return findValue(node.right);
} else {
return null;
}
}).bind(this)();
if (target === null) {
return null;
}
// count the children of the target to delete
var children = (target.left !== null ? 1 : 0) + (target.right !== null ? 1 : 0);
// case 1: target has no children
if (children === 0) {
if (target == this.root) {
this.root = null;
}
else {
if (parent.left == target) {
parent.left = null;
} else {
parent.right = null;
}
}
}
// case 2: target has one child
else if (children == 1) {
var newChild = (target.left !== null) ? target.left : target.right;
if (parent === null) {
target.value = newChild.value;
target.left = null;
target.right = null;
} else if (newChild.value < parent.value) {
parent.left = newChild;
} else {
parent.right = newChild;
}
target = null;
}
// case 3: target has two children, change code below this line
};
}
```
</div>
### After Test
<div id='js-teardown'>
```js
BinarySearchTree.prototype = {
add: function(value) {
var node = this.root;
if (node == null) {
this.root = new Node(value);
return;
} else {
function searchTree(node) {
if (value < node.value) {
if (node.left == null) {
node.left = new Node(value);
return;
} else if (node.left != null) {
return searchTree(node.left)
};
} else if (value > node.value) {
if (node.right == null) {
node.right = new Node(value);
return;
} else if (node.right != null) {
return searchTree(node.right);
};
} else {
return null;
};
};
return searchTree(node);
};
},
inorder: function() {
if (this.root == null) {
return null;
} else {
var result = new Array();
function traverseInOrder(node) {
if (node.left != null) {
traverseInOrder(node.left);
};
result.push(node.value);
if (node.right != null) {
traverseInOrder(node.right);
};
}
traverseInOrder(this.root);
return result;
};
},
isBinarySearchTree() {
if (this.root == null) {
return null;
} else {
var check = true;
function checkTree(node) {
if (node.left != null) {
var left = node.left;
if (left.value > node.value) {
check = false;
} else {
checkTree(left);
}
}
if (node.right != null) {
var right = node.right;
if (right.value < node.value) {
check = false;
} else {
checkTree(right);
};
};
};
checkTree(this.root);
return check;
}
}
};
```
</div>
</section>
## Solution
<section id='solution'>
```js
// solution required
```
</section>