From c117885246c0158d86bc52ea65bc933e0124bf9d Mon Sep 17 00:00:00 2001 From: lakhan_nad Date: Fri, 30 Oct 2020 22:52:16 +0530 Subject: [PATCH] AVL Tree Added in JS Fixes:#535 --- Data-Structures/Tree/AVLTree.js | 272 ++++++++++++++++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 Data-Structures/Tree/AVLTree.js diff --git a/Data-Structures/Tree/AVLTree.js b/Data-Structures/Tree/AVLTree.js new file mode 100644 index 000000000..0c72ce365 --- /dev/null +++ b/Data-Structures/Tree/AVLTree.js @@ -0,0 +1,272 @@ +/** + * Adelson-Velsky and Landis Tree + * [Wikipedia](https://en.wikipedia.org/wiki/AVL_tree) + * [A video lecture](http://www.youtube.com/watch?v=TbvhGcf6UJU) + */ +'use strict' + +/** + * A utility class for comparator + * A comparator is expected to have following structure + * + * comp(a, b) RETURN < 0 if a < b + * RETURN > 0 if a > b + * MUST RETURN 0 if a == b + */ +let utils; +(function (_utils) { + function comparator () { + return function (v1, v2) { + if (v1 < v2) { + return -1 + } else if (v2 < v1) { + return 1 + } else { + return 0 + } + } + } + _utils.comparator = comparator +})(utils || (utils = {})) + +/** + * @constructor + * A class for AVL Tree + * @argument comp - A function used by AVL Tree For Comparison + * If no argument is sent it uses utils.comparator + */ +const AVLTree = (function () { + function _avl (comp) { + /** @public compartor function */ + this._comp = undefined + if (comp !== undefined) { + this._comp = comp + } else { + this._comp = utils.comparator() + } + /** @public root of the AVL Tree */ + this.root = null + /** @public number of elements in AVL Tree */ + this.size = 0 + } + // creates new Node Object + const Node = function (val) { + this._val = val + this._left = null + this._right = null + this._height = 1 + } + // get height of a node + const getH = function (node) { + if (node == null) { return 0 } + return node._height + } + // height difference or balance factor of a node + const getHDiff = function (node) { + if (node == null) { return 0 } else { return getH(node._left) - getH(node._right) } + } + // update height of a node based on children's heights + const updateH = function (node) { + if (node == null) { + return + } + node._height = Math.max(getH(node._left), getH(node._right)) + 1 + } + // rotations of AVL Tree + const leftRotate = function (node) { + const temp = node._right + node._right = temp._left + temp._left = node + updateH(node) + updateH(temp) + return temp + } + const rightRotate = function (node) { + const temp = node._left + node._left = temp._right + temp._right = node + updateH(node) + updateH(temp) + return temp + } + // check if tree is balanced else balance it for insertion + const insertBalance = function (node, _val, balanceFactor) { + if (balanceFactor > 1 && _val < node._left._val) { + return rightRotate(node) // Left Left Case + } else if (balanceFactor < 1 && _val > node._right._val) { + return leftRotate(node) // Right Right Case + } else if (balanceFactor > 1 && _val > node._left._val) { + node._left = leftRotate(node._left) // Left Right Case + return rightRotate(node) + } + node._right = rightRotate(node._right) + return leftRotate(node) + } + // check if tree is balanced after deletion + const delBalance = function (node) { + const balanceFactor1 = getHDiff(node) + if (balanceFactor1 === 0 || balanceFactor1 === 1 || balanceFactor1 === -1) { + return node + } + if (balanceFactor1 > 1) { + if (getHDiff(node._left) >= 0) { + return rightRotate(node) // Left Left + } + node._left = leftRotate(node._left) + return rightRotate(node) // Left Right + } + if (getHDiff(node._right) > 0) { + node._right = rightRotate(node._right) + return leftRotate(node) // Right Left + } + return leftRotate(node) // Rigth Right + } + // implement avl tree insertion + const insert = function (root, val, tree) { + if (root == null) { + tree.size++ + return new Node(val) + } else if (tree._comp(root._val, val) < 0) { + root._right = insert(root._right, val, tree) + } else if (tree._comp(root._val, val) > 0) { + root._left = insert(root._left, val, tree) + } else { + return root + } + updateH(root) + const balanceFactor = getHDiff(root) + if (balanceFactor === 0 || balanceFactor === 1 || balanceFactor === -1) { + return root + } + return insertBalance(root, val, balanceFactor) + } + // delete a element + const del = function (root, _val, tree) { + if (root == null) { + return root + } else if (tree._comp(root._val, _val) === 0) { // key found case + if (root._left === null && root._right === null) { + root = null + tree.size-- + } else if (root._left === null) { + root = root._right + tree.size-- + } else if (root._right === null) { + root = root._left + tree.size-- + } else { + let temp = root._right + while (temp._left != null) { + temp = temp._left + } + root._val = temp._val + root._right = del(root._right, temp._val, tree) + } + } else { + if (tree._comp(root._val, _val) < 0) { + root._right = del(root._right, _val, tree) + } else { + root._left = del(root._left, _val, tree) + } + } + updateH(root) + root = delBalance(root) + return root + } + // search tree for a element + const search = function (root, val, tree) { + if (root == null) { + return null + } else if (tree._comp(root._val, val) === 0) { + return root + } else if (tree._comp(root._val, val) < 0) { + return search(root._right, val, tree) + } + return search(root._left, val, tree) + } + + /* Public Functions */ + /** + * For Adding Elements to AVL Tree + * @param {any} _val + * Since in AVL Tree an element can only occur once so + * if a element exists it return false + * @returns {Boolean} element added or not + */ + _avl.prototype.add = function (_val) { + const prevSize = this.size + this.root = insert(this.root, _val, this) + if (this.size === prevSize) { + return false + } + return true + } + /** + * TO check is a particluar element exists or not + * @param {any} _val + * @returns {Boolean} exists or not + */ + _avl.prototype.find = function (_val) { + const temp = search(this.root, _val, this) + if (temp != null) { + return true + } + return false + } + /** + * + * @param {any} _val + * It is possible that element doesn't exists in tree + * in that case it return false + * @returns {Boolean} if element was found and deleted + */ + _avl.prototype.remove = function (_val) { + const prevSize = this.size + this.root = del(this.root, _val, this) + if (prevSize === this.size) { + return false + } + return true + } + return _avl +}()); + +/** + * A Code for Testing the AVLTree + */ +(function test () { + const newAVL = new AVLTree() + const size = Math.floor(Math.random() * 1000000) + let uniques = 0 + let i, temp, j + const array = [] + for (i = 0; i < size; i++) { + temp = Math.floor(Math.random() * Number.MAX_VALUE) + if (newAVL.add(temp)) { + uniques++ + array.push(temp) + } + } + if (newAVL.size !== uniques) { + throw new Error('elements not inserted properly') + } + const findTestSize = Math.floor(Math.random() * uniques) + for (i = 0; i < findTestSize; i++) { + j = Math.floor(Math.random() * uniques) + if (!newAVL.find(array[j])) { + throw new Error('inserted elements not found') + } + } + const deleteTestSize = Math.floor(uniques * Math.random()) + for (i = 0; i < deleteTestSize; i++) { + j = Math.floor(Math.random() * uniques) + temp = array[j] + if (newAVL.find(temp)) { + if (!newAVL.remove(temp)) { + throw new Error('delete not working properly') + } + } + } +})() + +module.exports = AVLTree