Skip to content

Commit d10293c

Browse files
committed
Add AVL Tree.
1 parent 81253e8 commit d10293c

File tree

2 files changed

+287
-0
lines changed

2 files changed

+287
-0
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import BinarySearchTree from '../binary-search-tree/BinarySearchTree';
2+
3+
export default class AvlTree extends BinarySearchTree {
4+
insert(value) {
5+
// Do the normal BST insert.
6+
super.insert(value);
7+
8+
// Let's move up to the root and check balance factors along the way.
9+
let currentNode = this.root.find(value);
10+
while (currentNode) {
11+
this.balance(currentNode);
12+
currentNode = currentNode.parent;
13+
}
14+
}
15+
16+
balance(node) {
17+
// If balance factor is not OK then try to balance the node.
18+
if (node.balanceFactor > 1) {
19+
// Left rotation.
20+
if (node.left.balanceFactor > 0) {
21+
// Left-Left rotation
22+
this.rotateLeftLeft(node);
23+
} else if (node.left.balanceFactor < 0) {
24+
// Left-Right rotation.
25+
this.rotateLeftRight(node);
26+
}
27+
} else if (node.balanceFactor < -1) {
28+
// Right rotation.
29+
if (node.right.balanceFactor < 0) {
30+
// Right-Right rotation
31+
this.rotateRightRight(node);
32+
} else if (node.right.balanceFactor > 0) {
33+
// Right-Left rotation.
34+
this.rotateRightLeft(node);
35+
}
36+
}
37+
}
38+
39+
rotateLeftLeft(rootNode) {
40+
// Detach left node from root node.
41+
const leftNode = rootNode.left;
42+
rootNode.setLeft(null);
43+
44+
// Make left node to be a child of rootNode's parent.
45+
if (rootNode.parent) {
46+
rootNode.parent.setLeft(leftNode);
47+
} else if (rootNode === this.root) {
48+
// If root node is root then make left node to be a new root.
49+
this.root = leftNode;
50+
}
51+
52+
// If left node has a right child then detach it and
53+
// attach it as a left child for rootNode.
54+
if (leftNode.right) {
55+
rootNode.setLeft(leftNode.right);
56+
}
57+
58+
// Attach rootNode to the right of leftNode.
59+
leftNode.setRight(rootNode);
60+
}
61+
62+
rotateLeftRight(rootNode) {
63+
// Detach left node from rootNode since it is going to be replaced.
64+
const leftNode = rootNode.left;
65+
rootNode.setLeft(null);
66+
67+
// Detach right node from leftNode.
68+
const leftRightNode = leftNode.right;
69+
leftNode.setRight(null);
70+
71+
// Attach leftRightNode to the rootNode.
72+
rootNode.setLeft(leftRightNode);
73+
74+
// Attach leftNode as left node for leftRight node.
75+
leftRightNode.setLeft(leftNode);
76+
77+
// Do left-left rotation.
78+
this.rotateLeftLeft(rootNode);
79+
}
80+
81+
rotateRightLeft(rootNode) {
82+
// Detach right node from rootNode since it is going to be replaced.
83+
const rightNode = rootNode.right;
84+
rootNode.setRight(null);
85+
86+
// Detach left node from rightNode.
87+
const rightLeftNode = rightNode.left;
88+
rightNode.setLeft(null);
89+
90+
// Attach rightLeftNode to the rootNode.
91+
rootNode.setRight(rightLeftNode);
92+
93+
// Attach rightNode as right node for rightLeft node.
94+
rightLeftNode.setRight(rightNode);
95+
96+
// Do right-right rotation.
97+
this.rotateRightRight(rootNode);
98+
}
99+
100+
rotateRightRight(rootNode) {
101+
// Detach right node from root node.
102+
const rightNode = rootNode.right;
103+
rootNode.setRight(null);
104+
105+
// Make right node to be a child of rootNode's parent.
106+
if (rootNode.parent) {
107+
rootNode.parent.setRight(rightNode);
108+
} else if (rootNode === this.root) {
109+
// If root node is root then make right node to be a new root.
110+
this.root = rightNode;
111+
}
112+
113+
// If right node has a left child then detach it and
114+
// attach it as a right child for rootNode.
115+
if (rightNode.left) {
116+
rootNode.setRight(rightNode.left);
117+
}
118+
119+
// Attach rootNode to the left of rightNode.
120+
rightNode.setLeft(rootNode);
121+
}
122+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import AvlTree from '../AvlTree';
2+
3+
describe('AvlTree', () => {
4+
it('should do simple left-left rotation', () => {
5+
const tree = new AvlTree();
6+
7+
tree.insert(4);
8+
tree.insert(3);
9+
tree.insert(2);
10+
11+
expect(tree.toString()).toBe('2,3,4');
12+
expect(tree.root.value).toBe(3);
13+
expect(tree.root.height).toBe(1);
14+
15+
tree.insert(1);
16+
17+
expect(tree.toString()).toBe('1,2,3,4');
18+
expect(tree.root.value).toBe(3);
19+
expect(tree.root.height).toBe(2);
20+
21+
tree.insert(0);
22+
23+
expect(tree.toString()).toBe('0,1,2,3,4');
24+
expect(tree.root.value).toBe(3);
25+
expect(tree.root.left.value).toBe(1);
26+
expect(tree.root.height).toBe(2);
27+
});
28+
29+
it('should do complex left-left rotation', () => {
30+
const tree = new AvlTree();
31+
32+
tree.insert(30);
33+
tree.insert(20);
34+
tree.insert(40);
35+
tree.insert(10);
36+
37+
expect(tree.root.value).toBe(30);
38+
expect(tree.root.height).toBe(2);
39+
expect(tree.toString()).toBe('10,20,30,40');
40+
41+
tree.insert(25);
42+
expect(tree.root.value).toBe(30);
43+
expect(tree.root.height).toBe(2);
44+
expect(tree.toString()).toBe('10,20,25,30,40');
45+
46+
tree.insert(5);
47+
expect(tree.root.value).toBe(20);
48+
expect(tree.root.height).toBe(2);
49+
expect(tree.toString()).toBe('5,10,20,25,30,40');
50+
});
51+
52+
it('should do simple right-right rotation', () => {
53+
const tree = new AvlTree();
54+
55+
tree.insert(2);
56+
tree.insert(3);
57+
tree.insert(4);
58+
59+
expect(tree.toString()).toBe('2,3,4');
60+
expect(tree.root.value).toBe(3);
61+
expect(tree.root.height).toBe(1);
62+
63+
tree.insert(5);
64+
65+
expect(tree.toString()).toBe('2,3,4,5');
66+
expect(tree.root.value).toBe(3);
67+
expect(tree.root.height).toBe(2);
68+
69+
tree.insert(6);
70+
71+
expect(tree.toString()).toBe('2,3,4,5,6');
72+
expect(tree.root.value).toBe(3);
73+
expect(tree.root.right.value).toBe(5);
74+
expect(tree.root.height).toBe(2);
75+
});
76+
77+
it('should do complex right-right rotation', () => {
78+
const tree = new AvlTree();
79+
80+
tree.insert(30);
81+
tree.insert(20);
82+
tree.insert(40);
83+
tree.insert(50);
84+
85+
expect(tree.root.value).toBe(30);
86+
expect(tree.root.height).toBe(2);
87+
expect(tree.toString()).toBe('20,30,40,50');
88+
89+
tree.insert(35);
90+
expect(tree.root.value).toBe(30);
91+
expect(tree.root.height).toBe(2);
92+
expect(tree.toString()).toBe('20,30,35,40,50');
93+
94+
tree.insert(55);
95+
expect(tree.root.value).toBe(40);
96+
expect(tree.root.height).toBe(2);
97+
expect(tree.toString()).toBe('20,30,35,40,50,55');
98+
});
99+
100+
it('should do left-right rotation', () => {
101+
const tree = new AvlTree();
102+
103+
tree.insert(30);
104+
tree.insert(20);
105+
tree.insert(25);
106+
107+
expect(tree.root.height).toBe(1);
108+
expect(tree.root.value).toBe(25);
109+
expect(tree.toString()).toBe('20,25,30');
110+
});
111+
112+
it('should do right-left rotation', () => {
113+
const tree = new AvlTree();
114+
115+
tree.insert(30);
116+
tree.insert(40);
117+
tree.insert(35);
118+
119+
expect(tree.root.height).toBe(1);
120+
expect(tree.root.value).toBe(35);
121+
expect(tree.toString()).toBe('30,35,40');
122+
});
123+
124+
it('should create balanced tree: case #1', () => {
125+
const tree = new AvlTree();
126+
127+
tree.insert(1);
128+
tree.insert(2);
129+
tree.insert(3);
130+
131+
expect(tree.root.value).toBe(2);
132+
expect(tree.root.height).toBe(1);
133+
expect(tree.toString()).toBe('1,2,3');
134+
135+
tree.insert(6);
136+
137+
expect(tree.root.value).toBe(2);
138+
expect(tree.root.height).toBe(2);
139+
expect(tree.toString()).toBe('1,2,3,6');
140+
141+
tree.insert(15);
142+
143+
expect(tree.root.value).toBe(2);
144+
expect(tree.root.height).toBe(2);
145+
expect(tree.toString()).toBe('1,2,3,6,15');
146+
147+
tree.insert(-2);
148+
149+
expect(tree.root.value).toBe(2);
150+
expect(tree.root.height).toBe(2);
151+
expect(tree.toString()).toBe('-2,1,2,3,6,15');
152+
153+
tree.insert(-5);
154+
155+
expect(tree.root.value).toBe(2);
156+
expect(tree.root.height).toBe(2);
157+
expect(tree.toString()).toBe('-5,-2,1,2,3,6,15');
158+
159+
tree.insert(-8);
160+
161+
expect(tree.root.value).toBe(2);
162+
expect(tree.root.height).toBe(3);
163+
expect(tree.toString()).toBe('-8,-5,-2,1,2,3,6,15');
164+
});
165+
});

0 commit comments

Comments
 (0)