diff --git a/solutions/javascript/binary-search-tree.js b/solutions/javascript/binary-search-tree.js index 6f5e74d..645b39f 100644 --- a/solutions/javascript/binary-search-tree.js +++ b/solutions/javascript/binary-search-tree.js @@ -1,37 +1,107 @@ var root, createNode, - add; + add, + search, + addSubNode, + findRightMost, + replaceNodeInParent, + binaryTreeDelete; // not overwrite keyword. createNode = function createNode(num) { return { add: add, + search: search, + delete: binaryTreeDelete, left: undefined, right: undefined, value: num }; }; -add = function add(num) { +addSubNode = function(node, direct, num) { + if (node[direct] === undefined) { + node[direct] = createNode(num); + } else { + node[direct].add(num); + } +}; + +add = function(num) { + // Add the value to the correct side of the binary search tree. + // If the value is the same as the current node, we'll just ignore it. if (this.value === undefined) { this.value = num; } else { if (num < this.value) { - if (this.left === undefined) { - this.left = createNode(num); - } else { - this.left.add(num); - } + addSubNode(this, 'left', num); } else if (num > this.value) { - if (this.right === undefined) { - this.right = createNode(num); - } else { - this.right.add(num); - } + addSubNode(this, 'right', num); + } + } + return root; +}; + +search = function(num) { + if (num === this.value) { + return this; + } else if (num < this.value && this.left) { + return this.left.search(num); + } else if (num > this.value && this.right) { + return this.right.search(num); + } else { + return false; + } +}; + +findRightMost = function(node) { + if (node.right === undefined) { + return node; + } + return findRightMost(node.right); +}; + +replaceNodeInParent = function(node, parent, newNode) { + // root's parent is undefined. + if (parent === undefined) { + if (newNode) { + root.value = newNode.value, + root.left = newNode.left, + root.right = newNode.right; + }else{ + root.value=undefined; + } + return; + } + + if (parent.left === node) { + parent.left = newNode; + } else { + parent.right = newNode; + } +}; + + +binaryTreeDelete = function(num, parent) { + var successor; + if (num < this.value) { + return this.left ? this.left.delete(num, this) : root; + } else if (num > this.value) { + return this.right ? this.right.delete(num, this) : root; + } else { + // delete key here + if (this.left !== undefined && this.right !== undefined) { + successor = findRightMost(this.left); + this.value = successor.value; + this.left.delete(successor.value, this); + } else if (this.left) { + replaceNodeInParent(this, parent, this.left); + } else if (this.right) { + replaceNodeInParent(this, parent, this.right); } else { - return false; + replaceNodeInParent(this, parent); // replace with undefined } } return root; }; -module.exports = (root = createNode(undefined)); +module.exports = (root = createNode(undefined)); \ No newline at end of file diff --git a/tests/javascript/binary-search-tree.js b/tests/javascript/binary-search-tree.js new file mode 100644 index 0000000..2599baf --- /dev/null +++ b/tests/javascript/binary-search-tree.js @@ -0,0 +1,113 @@ +var assert = require('assert'); +var isBST = require('../../solutions/javascript/binary-search-tree-check'); +var bst = require('../../solutions/javascript/binary-search-tree'); + +var root = bst.add(50).add(9).add(100) + .add(7).add(10).add(8).add(6) + .add(75).add(150).add(175).add(125); + +var node = function(value, left, right) { + var result = { + value: value + }; + if (left) { + result.left = left; + } + if (right) { + result.right = right; + } + return result; +}; + +var jsonify = function(object) { + return JSON.parse(JSON.stringify(object)); +}; + +var expect; + +describe('binary search tree', function() { + it('root should pass a valid binary search tree', function() { + expect = node(50, + node(9, + node(7, node(6), node(8)), + node(10) + ), + node(100, + node(75), + node(150, node(125), node(175)) + ) + ); + + assert.ok(isBST(root)); + assert.deepEqual(jsonify(root), jsonify(expect)); + }); + + it('deleting nodes should pass a valid binary search tree', function() { + root.delete(125).delete(9).delete(75); + + expect = node(50, + node(8, + node(7, node(6)), + node(10) + ), + node(100, + undefined, + node(150, undefined, node(175)) + ) + ); + assert.ok(isBST(root)); + assert.deepEqual(jsonify(root), jsonify(expect)); + }); + + it('deleting unexisted nodes should pass a valid binary search tree', + function() { + root.delete(1252222).delete(20392); //delete unexisted node + assert.ok(isBST(root)); + assert.deepEqual(jsonify(root), jsonify(expect)); + }); + + it('search nodes', function() { + var searchExpect; + var searchResult = root.search(999999); // if not existed + assert.ok(!searchResult); + + searchResult = root.search(125); + assert.ok(!searchResult); + + searchExpect = node(7, node(6)); + searchResult = root.search(7); // if existed + assert.deepEqual(jsonify(searchResult), jsonify(searchExpect)); + + searchExpect = node(100, undefined, node(150, undefined, node(175))); + searchResult = root.search(100); // if existed + assert.deepEqual(jsonify(searchResult), jsonify(searchExpect)); + + searchExpect = node(50, node(8, node(7, node(6)), node(10)), + node(100, undefined, node(150, undefined, node(175)))); + searchResult = root.search(50); // if existed + assert.deepEqual(jsonify(searchResult), jsonify(searchExpect)); + }); + + it('delete root', function() { + var rootExpect; + + root.delete(50); + rootExpect = node(10, node(8, node(7, node(6))), + node(100, undefined, node(150, undefined, node(175)))); + assert.deepEqual(jsonify(root), jsonify(rootExpect)); + + //remove left sub tree + root.delete(10).delete(8).delete(7).delete(6); + rootExpect = node(100, undefined, node(150, undefined, node(175))); + assert.deepEqual(jsonify(root), jsonify(rootExpect)); + + //remove right sub tree + root.delete(175).delete(150).add(6).add(5).add(8).add(7).delete(100); + rootExpect = node(6, node(5), node(8, node(7))); + assert.deepEqual(jsonify(root), jsonify(rootExpect)); + + //remove every node. + root.delete(6).delete(5).delete(8).delete(7); + assert.equal(undefined, root.value); + }); +}); \ No newline at end of file