Skip to content

Commit 4df4112

Browse files
authored
Merge pull request #536 from Lakhan-Nad/AVLTree
AVL Tree Added in JS Fixes:#535
2 parents f647ca5 + c117885 commit 4df4112

File tree

1 file changed

+272
-0
lines changed

1 file changed

+272
-0
lines changed

Data-Structures/Tree/AVLTree.js

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
/**
2+
* Adelson-Velsky and Landis Tree
3+
* [Wikipedia](https://en.wikipedia.org/wiki/AVL_tree)
4+
* [A video lecture](http://www.youtube.com/watch?v=TbvhGcf6UJU)
5+
*/
6+
'use strict'
7+
8+
/**
9+
* A utility class for comparator
10+
* A comparator is expected to have following structure
11+
*
12+
* comp(a, b) RETURN < 0 if a < b
13+
* RETURN > 0 if a > b
14+
* MUST RETURN 0 if a == b
15+
*/
16+
let utils;
17+
(function (_utils) {
18+
function comparator () {
19+
return function (v1, v2) {
20+
if (v1 < v2) {
21+
return -1
22+
} else if (v2 < v1) {
23+
return 1
24+
} else {
25+
return 0
26+
}
27+
}
28+
}
29+
_utils.comparator = comparator
30+
})(utils || (utils = {}))
31+
32+
/**
33+
* @constructor
34+
* A class for AVL Tree
35+
* @argument comp - A function used by AVL Tree For Comparison
36+
* If no argument is sent it uses utils.comparator
37+
*/
38+
const AVLTree = (function () {
39+
function _avl (comp) {
40+
/** @public compartor function */
41+
this._comp = undefined
42+
if (comp !== undefined) {
43+
this._comp = comp
44+
} else {
45+
this._comp = utils.comparator()
46+
}
47+
/** @public root of the AVL Tree */
48+
this.root = null
49+
/** @public number of elements in AVL Tree */
50+
this.size = 0
51+
}
52+
// creates new Node Object
53+
const Node = function (val) {
54+
this._val = val
55+
this._left = null
56+
this._right = null
57+
this._height = 1
58+
}
59+
// get height of a node
60+
const getH = function (node) {
61+
if (node == null) { return 0 }
62+
return node._height
63+
}
64+
// height difference or balance factor of a node
65+
const getHDiff = function (node) {
66+
if (node == null) { return 0 } else { return getH(node._left) - getH(node._right) }
67+
}
68+
// update height of a node based on children's heights
69+
const updateH = function (node) {
70+
if (node == null) {
71+
return
72+
}
73+
node._height = Math.max(getH(node._left), getH(node._right)) + 1
74+
}
75+
// rotations of AVL Tree
76+
const leftRotate = function (node) {
77+
const temp = node._right
78+
node._right = temp._left
79+
temp._left = node
80+
updateH(node)
81+
updateH(temp)
82+
return temp
83+
}
84+
const rightRotate = function (node) {
85+
const temp = node._left
86+
node._left = temp._right
87+
temp._right = node
88+
updateH(node)
89+
updateH(temp)
90+
return temp
91+
}
92+
// check if tree is balanced else balance it for insertion
93+
const insertBalance = function (node, _val, balanceFactor) {
94+
if (balanceFactor > 1 && _val < node._left._val) {
95+
return rightRotate(node) // Left Left Case
96+
} else if (balanceFactor < 1 && _val > node._right._val) {
97+
return leftRotate(node) // Right Right Case
98+
} else if (balanceFactor > 1 && _val > node._left._val) {
99+
node._left = leftRotate(node._left) // Left Right Case
100+
return rightRotate(node)
101+
}
102+
node._right = rightRotate(node._right)
103+
return leftRotate(node)
104+
}
105+
// check if tree is balanced after deletion
106+
const delBalance = function (node) {
107+
const balanceFactor1 = getHDiff(node)
108+
if (balanceFactor1 === 0 || balanceFactor1 === 1 || balanceFactor1 === -1) {
109+
return node
110+
}
111+
if (balanceFactor1 > 1) {
112+
if (getHDiff(node._left) >= 0) {
113+
return rightRotate(node) // Left Left
114+
}
115+
node._left = leftRotate(node._left)
116+
return rightRotate(node) // Left Right
117+
}
118+
if (getHDiff(node._right) > 0) {
119+
node._right = rightRotate(node._right)
120+
return leftRotate(node) // Right Left
121+
}
122+
return leftRotate(node) // Rigth Right
123+
}
124+
// implement avl tree insertion
125+
const insert = function (root, val, tree) {
126+
if (root == null) {
127+
tree.size++
128+
return new Node(val)
129+
} else if (tree._comp(root._val, val) < 0) {
130+
root._right = insert(root._right, val, tree)
131+
} else if (tree._comp(root._val, val) > 0) {
132+
root._left = insert(root._left, val, tree)
133+
} else {
134+
return root
135+
}
136+
updateH(root)
137+
const balanceFactor = getHDiff(root)
138+
if (balanceFactor === 0 || balanceFactor === 1 || balanceFactor === -1) {
139+
return root
140+
}
141+
return insertBalance(root, val, balanceFactor)
142+
}
143+
// delete a element
144+
const del = function (root, _val, tree) {
145+
if (root == null) {
146+
return root
147+
} else if (tree._comp(root._val, _val) === 0) { // key found case
148+
if (root._left === null && root._right === null) {
149+
root = null
150+
tree.size--
151+
} else if (root._left === null) {
152+
root = root._right
153+
tree.size--
154+
} else if (root._right === null) {
155+
root = root._left
156+
tree.size--
157+
} else {
158+
let temp = root._right
159+
while (temp._left != null) {
160+
temp = temp._left
161+
}
162+
root._val = temp._val
163+
root._right = del(root._right, temp._val, tree)
164+
}
165+
} else {
166+
if (tree._comp(root._val, _val) < 0) {
167+
root._right = del(root._right, _val, tree)
168+
} else {
169+
root._left = del(root._left, _val, tree)
170+
}
171+
}
172+
updateH(root)
173+
root = delBalance(root)
174+
return root
175+
}
176+
// search tree for a element
177+
const search = function (root, val, tree) {
178+
if (root == null) {
179+
return null
180+
} else if (tree._comp(root._val, val) === 0) {
181+
return root
182+
} else if (tree._comp(root._val, val) < 0) {
183+
return search(root._right, val, tree)
184+
}
185+
return search(root._left, val, tree)
186+
}
187+
188+
/* Public Functions */
189+
/**
190+
* For Adding Elements to AVL Tree
191+
* @param {any} _val
192+
* Since in AVL Tree an element can only occur once so
193+
* if a element exists it return false
194+
* @returns {Boolean} element added or not
195+
*/
196+
_avl.prototype.add = function (_val) {
197+
const prevSize = this.size
198+
this.root = insert(this.root, _val, this)
199+
if (this.size === prevSize) {
200+
return false
201+
}
202+
return true
203+
}
204+
/**
205+
* TO check is a particluar element exists or not
206+
* @param {any} _val
207+
* @returns {Boolean} exists or not
208+
*/
209+
_avl.prototype.find = function (_val) {
210+
const temp = search(this.root, _val, this)
211+
if (temp != null) {
212+
return true
213+
}
214+
return false
215+
}
216+
/**
217+
*
218+
* @param {any} _val
219+
* It is possible that element doesn't exists in tree
220+
* in that case it return false
221+
* @returns {Boolean} if element was found and deleted
222+
*/
223+
_avl.prototype.remove = function (_val) {
224+
const prevSize = this.size
225+
this.root = del(this.root, _val, this)
226+
if (prevSize === this.size) {
227+
return false
228+
}
229+
return true
230+
}
231+
return _avl
232+
}());
233+
234+
/**
235+
* A Code for Testing the AVLTree
236+
*/
237+
(function test () {
238+
const newAVL = new AVLTree()
239+
const size = Math.floor(Math.random() * 1000000)
240+
let uniques = 0
241+
let i, temp, j
242+
const array = []
243+
for (i = 0; i < size; i++) {
244+
temp = Math.floor(Math.random() * Number.MAX_VALUE)
245+
if (newAVL.add(temp)) {
246+
uniques++
247+
array.push(temp)
248+
}
249+
}
250+
if (newAVL.size !== uniques) {
251+
throw new Error('elements not inserted properly')
252+
}
253+
const findTestSize = Math.floor(Math.random() * uniques)
254+
for (i = 0; i < findTestSize; i++) {
255+
j = Math.floor(Math.random() * uniques)
256+
if (!newAVL.find(array[j])) {
257+
throw new Error('inserted elements not found')
258+
}
259+
}
260+
const deleteTestSize = Math.floor(uniques * Math.random())
261+
for (i = 0; i < deleteTestSize; i++) {
262+
j = Math.floor(Math.random() * uniques)
263+
temp = array[j]
264+
if (newAVL.find(temp)) {
265+
if (!newAVL.remove(temp)) {
266+
throw new Error('delete not working properly')
267+
}
268+
}
269+
}
270+
})()
271+
272+
module.exports = AVLTree

0 commit comments

Comments
 (0)