Skip to content

AVL Tree Added in JS Fixes:#535 #536

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 17, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
272 changes: 272 additions & 0 deletions Data-Structures/Tree/AVLTree.js
Original file line number Diff line number Diff line change
@@ -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