-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathAVL.js
More file actions
245 lines (226 loc) · 7.9 KB
/
AVL.js
File metadata and controls
245 lines (226 loc) · 7.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
//HEADER SECTION
class Node {
constructor(game) {
this.game = game;
this.left = null;
this.right = null;
this.height = 1;
}
}
//Comparing global sales (helper function)
function globalSalesComparison(a, b) {
//Handles cells with missing/non-numerical data
if(a.Global_Sales == "N/A" || a.Global_Sales =="tbd"){
return -1;
}
if(b.Global_Sales == "N/A" || b.Global_Sales =="tbd"){
return 1;
}
const salesA = Number(a.Global_Sales);
const salesB = Number(b.Global_Sales);
if (salesA < salesB) return -1;
if (salesA > salesB) return 1;
//Check names if first attributes are equal
const nameA = a.Name.toLowerCase();
const nameB = b.Name.toLowerCase();
if (nameA < nameB) return -1;
if (nameA > nameB) return 1;
//Return 0 if completely equal
return 0;
}
//Comparing critic scores (helper function)
function criticScoreComparison(a, b) {
//Handles cells with missing/non-numerical data
if(a.Critic_Score == "N/A" || a.Critic_Score =="tbd"){
return -1;
}
if(b.Critic_Score == "N/A" || b.Critic_Score =="tbd"){
return 1;
}
const criticScoreA = Number(a.Critic_Score);
const criticScoreB = Number(b.Critic_Score);
if (criticScoreA < criticScoreB) return -1;
if (criticScoreA > criticScoreB) return 1;
//Check names if first attributes are equal
const nameA = a.Name.toLowerCase();
const nameB = b.Name.toLowerCase();
if (nameA < nameB) return -1;
if (nameA > nameB) return 1;
//Return 0 if completely equal
return 0;
}
//Comparing user scores (helper function)
function userScoreComparison(a, b) {
//Handles cells with missing/non-numerical data
if(a.User_Score == "N/A" || a.User_Score =="tbd"){
return -1;
}
if(b.User_Score == "N/A" || b.User_Score =="tbd"){
return 1;
}
const userScoreA = Number(a.User_Score);
const userScoreB = Number(b.User_Score);
if (userScoreA < userScoreB) return -1;
if (userScoreA > userScoreB) return 1;
//Check names if first attributes are equal
const nameA = a.Name.toLowerCase();
const nameB = b.Name.toLowerCase();
if (nameA < nameB) return -1;
if (nameA > nameB) return 1;
//Return 0 if completely equal
return 0;
}
//SOURCE SECTION
class AVL {
//Constructor
constructor(comparisonFunction) {
this.root = null;
this.comparisonFunction = comparisonFunction;
}
//Inserting node to BST
insertNode(root, game) {
if (root === null) return new Node(game);
// Standard insertion with call to helper function
const cmp = this.comparisonFunction(game, root.game);
if (cmp < 0) {
root.left = this.insertNode(root.left, game);
} else {
root.right = this.insertNode(root.right, game);
}
// Update height
root.height = 1 + Math.max(this.getHeight(root.left), this.getHeight(root.right));
// Get balance factor
let balance = this.getBalanceFactor(root);
// All rotation cases
// LL Case
if (balance > 1 && this.comparisonFunction(game, root.left.game) < 0) {
return this.rotateRight(root);
}
// RR Case
if (balance < -1 && this.comparisonFunction(game, root.right.game) > 0) {
return this.rotateLeft(root);
}
// LR Case
if (balance > 1 && this.comparisonFunction(game, root.left.game) > 0) {
root.left = this.rotateLeft(root.left);
return this.rotateRight(root);
}
// RL Case
if (balance < -1 && this.comparisonFunction(game, root.right.game) < 0) {
root.right = this.rotateRight(root.right);
return this.rotateLeft(root);
}
return root;
}
//Returns height of tree
getHeight(node) {
return node ? node.height : 0;
}
//Returns balance factor of tree
getBalanceFactor(node) {
if (!node) return 0;
return this.getHeight(node.left) - this.getHeight(node.right);
}
//Rotate right function called when inserting node
rotateRight(node) {
let temp = node.left;
node.left = temp.right;
temp.right = node;
node.height = 1 + Math.max(this.getHeight(node.left), this.getHeight(node.right));
temp.height = 1 + Math.max(this.getHeight(temp.left), this.getHeight(temp.right));
return temp;
}
//Rotate left function called when inserting node
rotateLeft(node) {
let temp = node.right;
node.right = temp.left;
temp.left = node;
node.height = 1 + Math.max(this.getHeight(node.left), this.getHeight(node.right));
temp.height = 1 + Math.max(this.getHeight(temp.left), this.getHeight(temp.right));
return temp;
}
//Filters the games displayed based on user's selection
filterGame(game, filters) {
if (!filters || Object.keys(filters).length === 0) return true;
//Loops through every filter key
for (let key in filters) {
if (Array.isArray(filters[key])) {
//Checks if any filtered values match the game's values for given key
if (!filters[key].some(f => String(game[key]).toLowerCase().includes(String(f).toLowerCase()))) return false;
} else {
//Does same check but for non-array filter values
if (!String(game[key]).toLowerCase().includes(String(filters[key]).toLowerCase())) return false;
}
}
return true;
}
//Used for InorderSearch to traverse recursively
inorderHelper(node, outputV, filters, count) {
if (!node) return;
this.inorderHelper(node.right, outputV, filters, count);
count.nodesPassed++;
if (this.filterGame(node.game, filters)) {
//Creates copy of current node's game object w/ how many nodes visited to append object to array
outputV.push({...node.game, nodesPassed: count.nodesPassed});
}
this.inorderHelper(node.left, outputV, filters, count);
}
//Inorder search that primarily calls helper functions
InorderSearch(filters = {}) {
//Initializes array of game objects
let outputV = [];
//Initializes count object with nodesPassed property
let count = {nodesPassed: 0};
this.inorderHelper(this.root, outputV, filters, count);
return outputV;
}
//BFS search using queue to traverse
BFS(filters){
if (!this.root) {
return []
}
let queue = [];
queue.push(this.root);
let outputArr = [];
let nodesPassed = 0;
while(queue.length > 0){
let currentNode = queue.shift()
nodesPassed++;
if(this.filterGame(currentNode.game, filters)){ //check if the current game object matches the filters
//Pushes copy of current node's game object w/ nodesPassed count
outputArr.push({...currentNode.game, nodesPassed})
}
if(currentNode.left){
queue.push(currentNode.left)
}
if(currentNode.right){
queue.push(currentNode.right)
}
}
return outputArr //return an array of game objects that match the given filter
}
//Destructor helper function (optional in JS)
deleteTree(node) {
if (!node) return;
this.deleteTree(node.left);
this.deleteTree(node.right);
node.left = null;
node.right = null;
}
//Destructor (optional in JS)
destructor() {
this.deleteTree(this.root);
this.root = null;
}
}
window.AVL = AVL;
window.globalSalesComparison = globalSalesComparison;
window.criticScoreComparison = criticScoreComparison;
window.userScoreComparison = userScoreComparison;
/*
module.exports = {
AVL,
globalSalesComparison,
criticScoreComparison,
userScoreComparison
}*/