112 |
113 |
--------------------------------------------------------------------------------
/Binary-Search-Tree-DSA.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/BinarySearchTree.java:
--------------------------------------------------------------------------------
1 | /**
2 | * A class that represents a Binary Search Tree and its associated methods.
3 | * @author David Nguyen
4 | * @since 03/08/2023
5 | */
6 | public class BinarySearchTree {
7 |
8 | // A field that represents the root of the Binary Search Tree
9 | private Node root;
10 |
11 | // A field that represents the number of nodes visited when traversing the binary search tree (used for the delete method)
12 | private int numNodesVisited;
13 |
14 | // CONSTRUCTORS: //
15 |
16 | /**
17 | * A constructor that creates a Binary Search Tree that takes no input (A B.S.T. can be created in two main ways, either
18 | * with an input assigned as it root when it's created, or with no inputs and the tree will be created as an empty tree.)
19 | * A Binary Search Tree can be created in three ways in order to allow for the flexibility of creating the tree.
20 | *
21 | */
22 | public BinarySearchTree() {
23 | this.root = null;
24 | // The number of nodes visited field will always be reset upon the creation of the new tree
25 | this.numNodesVisited = 0;
26 | }
27 |
28 | /**
29 | * A constructor that creates a Binary Search Tree that takes one int input and assigns it as the tree's root by
30 | * creating a node with the parsed-in, given key and making it the root node of the B.S.T.
31 | * (A B.S.T. can be created in two main ways, either with an input assigned as it root when it's created,
32 | * or with no inputs and the tree will be created as an empty tree.)
33 | * A Binary Search Tree can be created in three ways in order to allow for the flexibility of creating the tree.
34 | *
35 | * @param root A root for the tree to be created, parsed in as an integer and a new node will be automatically created
36 | * for that root.
37 | */
38 | public BinarySearchTree(int root) {
39 | this.root = new Node(root);
40 | // The number of nodes visited field will always be reset upon the creation of the new tree
41 | this.numNodesVisited = 0;
42 | }
43 |
44 | /**
45 | * A constructor that creates a Binary Search Tree that takes one Node input and assigns it as the tree's root.
46 | * (A B.S.T. can be created in two main ways, either with an input assigned as it root when it's created,
47 | * or with no inputs and the tree will be created as an empty tree.)
48 | * A Binary Search Tree can be created in three ways in order to allow for the flexibility of creating the tree.
49 | *
50 | * @param root A root node for the tree to be created, parsed in as a created node
51 | */
52 | public BinarySearchTree(Node root) {
53 | this.root = root;
54 | // The number of nodes visited field will always be reset upon the creation of the new tree
55 | this.numNodesVisited = 0;
56 | }
57 |
58 | // PRIMARY METHODS: //
59 |
60 | /**
61 | * A method that inserts the specified key into the Binary Search Tree by calling another helper, overloaded method
62 | * with the same name.
63 | *
64 | * @param key A key that will be inserted into the tree.
65 | */
66 | public void insert(int key) {
67 | insert(this.root, key);
68 | }
69 |
70 | /**
71 | * A method that creates a tree from a given, parsed-in array of integers. Any existing tree will be overwritten.
72 | *
73 | * @param values any given array from which a tree will be created from
74 | */
75 | public void createTree(int[] values) {
76 | // Now creates a new root for the new tree with the first value of the given array and override the current root
77 | Node newRoot = new Node(values[0]);
78 | // Assign the new root to be the newly-created node
79 | this.root = newRoot;
80 | /*
81 | * A loop that passes every element of the given array into the tree using the insert() method above. It
82 | * will stop when the end of the array is reached.
83 | */
84 | for (int i = 1; i < values.length; i++) {
85 | insert(newRoot, values[i]);
86 | }
87 | /*
88 | * Now, print out a confirmation that a new tree is created and print out the new tree using three different types
89 | * of traversal. This is done to help with the testing and behavior verification part of this method.
90 | */
91 | System.out.println("A new tree has been successfully created from the given array:");
92 | System.out.println("Inorder traversal of the new tree: ");
93 | inorderRec(newRoot);
94 | System.out.println();
95 | System.out.println("Preorder traversal of the new tree: ");
96 | preorderRec(newRoot);
97 | System.out.println();
98 | System.out.println("Postorder traversal of the new tree: ");
99 | postorderRec(newRoot);
100 | /*
101 | * When these statements to print the tree are executed, it means that the tree is created successfully from the
102 | * given array, and the programmer can use the information printed on the screen to verify the elements of the tree.
103 | */
104 | }
105 |
106 | /**
107 | * A method that searches for a specific key in the Binary Search Tree by calling another helper, overloaded method
108 | * with the same name.
109 | * (I have implemented this function before the assignment was changed and therefore, my helper method still returns
110 | * a node with the specified key.) Please forgive me for this mistake.
111 | * @param key A key in the B.S.T. to search for.
112 | * @return A node that is found with the given key, or may return null if no node with such key is found
113 | */
114 | public boolean search(int key) {
115 | // A Node variable that will be assigned and store the result of the search() helper function
116 | Node searchResult = null;
117 | // A variable that stores the boolean variable true-false for returning
118 | boolean result = true;
119 | // Perform a search that returns a node with the specified key
120 | searchResult = search(this.root, key);
121 | // Checks if the returned node is null. If it is, it means that no nodes with such key is found, return false.
122 | if (searchResult == null) {
123 | result = false;
124 | }
125 | // Simply return this result variable, which can either be true or false
126 | return result;
127 | }
128 |
129 | /**
130 | * A method that deletes the node with the specified key from the Binary Search Tree by calling another helper,
131 | * overloaded method with the same name.
132 | *
133 | * @param key The key of the node that will be deleted from the tree.
134 | * @return The node that is the new root of the new tree after deletion.
135 | */
136 | public Node delete(int key) {
137 | // A Node variable that will be assigned and store the result of the helper function delete()
138 | Node newRoot = null;
139 | newRoot = delete(this.root, key);
140 | // Simply return this result variable
141 | return newRoot;
142 | }
143 |
144 | /**
145 | * A method that traverses the tree in inorder traversal using recursion and prints out all the nodes while traversing
146 | * by calling another helper, overloaded method with the same name.
147 | */
148 | public void inorderRec() {
149 | inorderRec(this.root);
150 | }
151 |
152 | /**
153 | * A method that traverses the tree in preorder traversal using recursion and prints out all the nodes while traversing
154 | * by calling another helper, overloaded method with the same name.
155 | */
156 | public void preorderRec() {
157 | preorderRec(this.root);
158 | }
159 |
160 | /**
161 | * A method that traverses the tree in postorder traversal using recursion and prints out all the nodes while traversing
162 | * by calling another helper, overloaded method with the same name.
163 | */
164 | public void postorderRec() {
165 | postorderRec(this.root);
166 | }
167 |
168 | /**
169 | * A method that finds the k-th-smallest node of the current tree with the given root by calling another helper,
170 | * overloaded method with the same name by implementing inorder traversal.
171 | *
172 | * @param k The value of how small the node to be found is in the tree (e.g. 2nd-smallest or 5th-smallest)
173 | * @return The node that is found given the k value input, or may return null if no such node is found.
174 | */
175 | public Node kthSmallest(int k) {
176 | // A Node variable that will be assigned and store the result of the helper function kthSmallestHelper2()
177 | Node kthSmallestNode = null;
178 | // A try-catch block to check if there is any exception thrown due to invalidity of input k
179 | try {
180 | kthSmallestNode = kthSmallestHelper2(this.root, k);
181 | // Simply return this variable as the result
182 | return kthSmallestNode;
183 | }
184 | // If an exception is caught, system should print a message saying that the entered k value is invalid
185 | catch (IllegalArgumentException e) {
186 | System.out.println("The entered k value is invalid! It is either smaller than 0 or greater than the number of nodes currently in the tree!");
187 | // Return null because the given k value is invalid and henceforth no kth-smallest node can be found with it
188 | return null;
189 | }
190 | }
191 |
192 | // HELPER METHODS: //
193 |
194 | /**
195 | * A method that traverses and inserts a Node to a given Binary Search Tree with its root specified using recursion
196 | *
197 | * @param root The root of a B.S.T. in which a node will be added
198 | * @param key The key (data) of the node to be inserted
199 | */
200 | private void insert(Node root, int key) {
201 | // A variable that stores the current node of the tree (i.e. current root of subtree) when traversing the tree
202 | Node traverseNode = root;
203 | // A variable that keeps track of the parent (previous) node of the current traversing node.
204 | Node parent = null;
205 | /*
206 | * The base case for recursion: the method should assign the current node of the tree that we are on the value
207 | * to be inserted, and then the method is stopped. This should only be done when the method has found the exact,
208 | * correct place in the tree to insert a node.
209 | */
210 | if (traverseNode == null) {
211 | // Assigns the key to be inserted as a new leaf node of the tree by creating a new node with the given key:
212 | traverseNode = new Node(key);
213 | this.root = traverseNode;
214 | // Stops the function by using the return keyword
215 | return;
216 | }
217 | /*
218 | * A while-loop that stops when the traverse node is null. It will traverse the B.S.T. to find the place where
219 | * the new node should be inserted by continuously assigning the traversing node to be its child node in either its
220 | * right or left subtree, as described below:
221 | */
222 | while (traverseNode != null) {
223 | // First, keeps track of the current parent of the traversing node:
224 | parent = traverseNode;
225 | // If the key to be inserted is smaller than the current traversing node's key, go to its left subtree
226 | if (key < traverseNode.key) {
227 | traverseNode = traverseNode.left;
228 | }
229 | // If the key to be inserted is greater than the current traversing node's key, go to its right subtree
230 | else if (key >= traverseNode.key) {
231 | traverseNode = traverseNode.right;
232 | }
233 | /* The loop will stop when the traversing node is null - i.e. it has reached the leaf of the tree where
234 | * the new node will be inserted. */
235 | }
236 | /*
237 | * Now that we have found where to insert our new node, we will compare the given key with the key of our current
238 | * parent. If that key is greater than the current parent's key, create a new node and assign it as the left child node
239 | * of the current parent.
240 | */
241 | if (key < parent.key) {
242 | parent.left = new Node(key);
243 | }
244 | /*
245 | * Otherwise, if the given key is greater than or equal to the current parent's key, assign the new node with
246 | * the given key to be the right child node of the current parent.
247 | */
248 | else if (key >= parent.key) {
249 | parent.right = new Node(key);
250 | }
251 | }
252 |
253 | /**
254 | * A method that searches for a node with the specific key in the given B.S.T. with the given root using recursion
255 | *
256 | * @param root The root of the binary search tree that will be searched
257 | * @param key Any given key to be searched
258 | * @return The node is found to have the same key as the given input key. In the case that no node with such key is
259 | * found in the tree, the system will return the given root only, and prints out a confirmation that no such node with
260 | * given key is found in the tree.
261 | */
262 | private Node search(Node root, int key) {
263 | // A variable that keeps track of the current node (i.e. current root of subtree) used when traversing the tree
264 | Node traverseNode = root;
265 | /*
266 | * Now, if the traverse node is null, which means that we have traversed the entire tree and found no matching
267 | * node. Thus, only the given root will be returned and a message will be printed to the screen to confirm that
268 | * no such key is found in the tree.
269 | */
270 | if (traverseNode == null) {
271 | System.out.println("No Such Key or Element Exists in the Tree!");
272 | return null;
273 | }
274 | /*
275 | * The base case for the recursion method: It should stop and return the node found if the node that has the same
276 | * key as the given key is found
277 | */
278 | if (traverseNode.key == key) {
279 | return traverseNode;
280 | }
281 | /*
282 | * If the current traverse node's key is greater than the given key, traverse and search its left subtree as the
283 | * node to be found should now be in the traverse node's left subtree
284 | */
285 | if (key < traverseNode.key) {
286 | traverseNode = search(traverseNode.left, key);
287 | }
288 | /*
289 | * Otherwise, if the current traverse node's key is smaller than or equal to the given key, traverse and search
290 | * its right subtree as the node to be found should now be in the traverse node's right subtree
291 | */
292 | else if (key >= traverseNode.key) {
293 | traverseNode = search(traverseNode.right, key);
294 | }
295 | // At the end of the function, return the traversing node in order to stop the function and add a return statement
296 | return traverseNode;
297 | }
298 |
299 | /**
300 | * A method that will delete the node with the specific key in a B.S.T. and return te node that is deleted.
301 | *
302 | * @param root the root of the tree whose node is to be deleted
303 | * @param key the key of the node to be found and deleted
304 | * @return the node that is the new root of the new tree after deletion
305 | */
306 | private Node delete(Node root, int key) {
307 | // A variable that stores and keeps track of the current traversing node (i.e. current root of subtree) while traversing the tree
308 | Node traverseNode = root;
309 | /*
310 | * In the base case of this method: return the current traversing node, as we have traversed the entire tree,
311 | * and we may or may not have found and deleted a node from the tree. This base case also works when the tree is empty,
312 | * as it will simply return the parsed-in root.
313 | */
314 | if (traverseNode == null) {
315 | return null;
316 | }
317 | /*
318 | * If the current traversing node's key is greater than the given key, traverse its left subtree as the node to
319 | * be found and deleted is likely in this subtree, using recursion.
320 | */
321 | if (traverseNode.key > key) {
322 | traverseNode.left = delete(traverseNode.left, key);
323 | }
324 | /*
325 | * If the current traversing node's key is smaller than the given key, traverse its right subtree as the node to
326 | * be found and deleted is likely in this subtree, using recursion.
327 | */
328 | else if (traverseNode.key < key) {
329 | traverseNode.right = delete(traverseNode.right, key);
330 | }
331 | /*
332 | * If the current traversing node's key is the same as the given key, which means that we have found the node to
333 | * be deleted. We will now look at three different cases, as described below:
334 | */
335 | else if (key == traverseNode.key) {
336 | /*
337 | * If the right child node of the current traversing node is null (which means that it has only 1 left child),
338 | * assign it to be its left child in order to delete it from the tree.
339 | */
340 | if (traverseNode.right == null) {
341 | // A temporary Node variable to store the current node to be deleted, just in case it gets mixed up
342 | Node temporaryNode = traverseNode;
343 | // Assigns the current root (i.e. the current traversal node) to be its left child
344 | traverseNode = traverseNode.left;
345 | }
346 | /*
347 | * If the left child node of the current traversing node is null (which means that it has only 1 right child),
348 | * assign it to be its right child in order to delete it from the tree.
349 | */
350 | else if (traverseNode.left == null) {
351 | // A temporary Node variable to store the current node to be deleted, just in case it gets mixed up
352 | Node temporaryNode = traverseNode;
353 | // Assigns the current root (i.e. the current traversal node) to be its right child
354 | traverseNode = traverseNode.right;
355 | }
356 | // Otherwise, if the node to be deleted is a leaf node (that has no children), simply delete it from the tree
357 | else if (traverseNode.left == null && traverseNode.right == null) {
358 | traverseNode = null;
359 | }
360 | /*
361 | * If the node to be deleted has two children, find the minimum key in its right subtree and replace its key
362 | * with the minimum key. Then traverse the current traversing node's right subtree to delete the minimum key
363 | * from the tree.
364 | */
365 | else if (traverseNode.left != null && traverseNode.right != null) {
366 | // A temporary Node variable to store the current node to be deleted, just in case it gets mixed up with other variables
367 | Node temporaryNode = traverseNode;
368 | // A variable that stores the minimum key in the current traverse node's right subtree
369 | Node minimum = findMinimumInSubTree(temporaryNode.right);
370 | // Replace the current node's key to be the minimum node's key in order to delete it from the tree
371 | traverseNode.key = minimum.key;
372 | // Delete the minimum node's key from the tree in order to avoid any duplications in the tree
373 | traverseNode.right = delete(traverseNode.right, minimum.key);
374 | }
375 | }
376 | // Return the current traverse node in order to stop the function
377 | return traverseNode;
378 | }
379 |
380 | /**
381 | * A helper function that helps find the minimum node in the current tree or subtree given its root, using recursion
382 | *
383 | * @param root The node of the tree that will be searched
384 | * @return The minimum node in the current tree or subtree given its root
385 | */
386 | private Node findMinimumInSubTree(Node root) {
387 | // A variable that stores and keeps track of the current traversing node (i.e. current root of subtree) while traversing the tree
388 | Node traverseNode = root;
389 | /*
390 | * The base case for the recursion: if the left child node of the current traversing node is null, return the
391 | * traversing node, as we have now found the smallest node in the current tree or subtree.
392 | */
393 | if (traverseNode.left == null) {
394 | return traverseNode;
395 | }
396 | /*
397 | * Otherwise, traverse the current node's left subtree in order to find the smallest node in it. Then, return
398 | * the current traversing node in order to stop the function.
399 | */
400 | else if (traverseNode.left != null) {
401 | traverseNode = findMinimumInSubTree(traverseNode.left);
402 | return traverseNode;
403 | }
404 | // Return the current traversal node
405 | return traverseNode;
406 | }
407 |
408 | /**
409 | * A method that traverses the tree in inorder traversal using recursion and prints out all the nodes while traversing
410 | *
411 | * @param root The root of the tree to be traversed
412 | */
413 | private void inorderRec(Node root) {
414 | // A variable that stores and keeps track of the current traversing node (i.e. current root of subtree) while traversing the tree
415 | Node traverseNode = root;
416 | /*
417 | * The base case for the recursion: the function will stop when the traverse node is null, or when the tree
418 | * is empty or when the tree has been completely, fully traversed.
419 | */
420 | if (traverseNode == null) {
421 | return;
422 | }
423 | // First, recursively traverse the left subtree of the current traversing node
424 | inorderRec(traverseNode.left);
425 | // Then, visit the current root & prints out the nodes as we traverse through the B.S.T.'s subtree
426 | System.out.print(traverseNode.key + " ");
427 | // Then, recursively traverse the right subtree of the current traversing node
428 | inorderRec(traverseNode.right);
429 | }
430 |
431 | /**
432 | * A method that traverses the tree in preorder traversal using recursion and prints out all the nodes while traversing
433 | *
434 | * @param root The root of the tree to be traversed
435 | */
436 | public void preorderRec(Node root) {
437 | // A variable that stores and keeps track of the current traversing node (i.e. current root of subtree) while traversing the tree
438 | Node traverseNode = root;
439 | /*
440 | * The base case for the recursion: the function will stop when the traverse node is null, or when the tree
441 | * is empty or when the tree has been completely, fully traversed.
442 | */
443 | if (traverseNode == null) {
444 | return;
445 | }
446 | // First, visit the current root & prints out the nodes of the tree as we traverse it
447 | System.out.print(traverseNode.key + " ");
448 | // Then, recursively traverse the left subtree of the current traversing node
449 | preorderRec(traverseNode.left);
450 | // Then, recursively traverse the right subtree of the current traversing node
451 | preorderRec(traverseNode.right);
452 | }
453 |
454 | /**
455 | * A method that traverses the tree in preorder traversal using recursion and prints out all the nodes while traversing
456 | *
457 | * @param root The root of the tree to be traversed
458 | */
459 | private void postorderRec(Node root) {
460 | // A variable that stores and keeps track of the current traversing node (i.e. current root of subtree) while traversing the tree
461 | Node traverseNode = root;
462 | /*
463 | * The base case for the recursion: the function will stop when the traverse node is null, or when the tree
464 | * is empty or when the tree has been completely, fully traversed.
465 | */
466 | if (traverseNode == null) {
467 | return;
468 | }
469 | // First, recursively traverse the left subtree of the current traversing node
470 | preorderRec(traverseNode.left);
471 | // Then, recursively traverse the right subtree of the current traversing node
472 | preorderRec(traverseNode.right);
473 | // Then, visit the current root & prints out the nodes of the tree as we traverse it
474 | System.out.print(traverseNode.key + " ");
475 | }
476 |
477 | /**
478 | * A method that helps find the k-th-smallest element/node in a given tree or subtree using recursion and inorder
479 | * traversal.
480 | *
481 | * @param root The root of the tree in which the k-th-smallest element will be found
482 | * @param k the value of how small the node to be found is in the tree (e.g. 2nd-smallest or 5th-smallest)
483 | * @return the k-th-smallest element
484 | */
485 | private Node kthSmallestHelper1(Node root, int k) {
486 | // A variable that stores and keeps track of the current traversing node (i.e. current root of subtree) while traversing the tree
487 | Node traverseNode = root;
488 | // A node that keeps track of the k-th-smallest node
489 | Node kthSmallestNode = null;
490 | /*
491 | * Base case for the recursion: If the current traverse node is null, meaning that either we have traversed the
492 | * entire tree or when the tree is null
493 | */
494 | if (traverseNode == null) {
495 | return traverseNode;
496 | }
497 | // First, traverse the left subtree of the current node to perform inorder traversal
498 | kthSmallestNode = kthSmallestHelper1(traverseNode.left, k);
499 | /*
500 | * Whenever the k-th-smallest node variable is assigned a value (i.e. it is found), the method should stop and
501 | * return this variable.
502 | */
503 | if (kthSmallestNode != null) {
504 | return kthSmallestNode;
505 | }
506 | // Otherwise, increment the current number of nodes visited while traversing the tree by 1
507 | numNodesVisited = numNodesVisited + 1;
508 | /*
509 | * Check if the current number of nodes visited field is equal to the given k-value. If it is, return the current
510 | * traversing node. Otherwise, let the method continue as normal.
511 | */
512 | if (numNodesVisited == k) {
513 | // Assign the kth-smallest-node variable to be the current traversing node and return it
514 | kthSmallestNode = traverseNode;
515 | return kthSmallestNode;
516 | }
517 | /*
518 | * After completing traversing the current root's left subtree, we will now traverse its right subtree to complete
519 | * the inorder traversal of the tree.
520 | */
521 | kthSmallestNode = kthSmallestHelper1(traverseNode.right, k);
522 | // Now, at the end of the method, return the current kth-smallest-node in order to stop the function.
523 | return kthSmallestNode;
524 | }
525 |
526 | /**
527 | * A method that finds the k-th-smallest node of the current tree with the given root
528 | *
529 | * @param root the root of the tree to find the node from
530 | * @param k The value of how small the node to be found is in the tree (e.g. 2nd-smallest or 5th-smallest)
531 | * @return the k-th-smallest node of the current tree
532 | * @throws IllegalArgumentException Throw this exception if the entered k value is invalid (i.e. it is greater than
533 | * the current number of nodes in the tree or is below 0.)
534 | */
535 | private Node kthSmallestHelper2(Node root, int k) {
536 | /* Throw this exception if the entered k value is invalid (i.e. it is greater than the current number of nodes
537 | * in the tree or is below 0.) */
538 | if (k < 0 || k > numNodes(this.root)) {
539 | throw new IllegalArgumentException();
540 | }
541 | // Resets the field that keeps track of the number of nodes visited when traversing the tree to 0
542 | numNodesVisited = 0;
543 | /*
544 | * Calls the helper function that will traverse the tree and find the k-th-smallest node.
545 | * Two separate functions are written to achieve this task to avoid the reuse of the above-mentioned field when
546 | * using this method multiple times in a row, as the reuse of this field can lead to inaccurate results.
547 | */
548 | return kthSmallestHelper1(root, k);
549 | }
550 |
551 | /**
552 | * A function that traverses and calculates the total number of nodes currently in the tree using recursion.
553 | *
554 | * @param root The root of the tree whose nodes are being counted
555 | * @return Total number of nodes currently in the B.S.T.
556 | */
557 | private int numNodes(Node root){
558 | // Base Case: If a null node is reached or a given tree is empty
559 | if (root == null) {
560 | return 0;
561 | }
562 | // Recursively traverse left & right subtree of current root node to visit all nodes currently in tree
563 | return 1 + numNodes(root.left) + numNodes(root.right);
564 | }
565 |
566 | }
--------------------------------------------------------------------------------
/Main.java:
--------------------------------------------------------------------------------
1 | /**
2 | * A class that tests the behavior of the methods in the Binary Search Tree class
3 | * @author David Nguyen
4 | * @since 03/08/2023
5 | */
6 | public class Main {
7 |
8 | /**
9 | * A method that tests and demonstrates the behavior of the methods in the Binary Search Tree class.
10 | *
11 | * @param args any specified arguments to run the main method.
12 | */
13 | public static void main(String[] args) {
14 | // Creating a new tree and inserting nodes into the tree
15 | BinarySearchTree tree = new BinarySearchTree();
16 | tree.insert(40);
17 | tree.insert(20);
18 | tree.insert(10);
19 | tree.insert(30);
20 | tree.insert(60);
21 | tree.insert(50);
22 | tree.insert(70);
23 | tree.insert(5);
24 | tree.insert(15);
25 | tree.insert(55);
26 | tree.insert(75);
27 | tree.insert(95);
28 | // Now print out the tree to show its current state in three different types of tree traversals
29 | System.out.println("1) Create & Print Out The Following Binary Search Tree: ");
30 | System.out.println("Inorder Traversal: ");
31 | tree.inorderRec();
32 | System.out.println();
33 | System.out.println("Preorder Traversal: ");
34 | tree.preorderRec();
35 | System.out.println();
36 | System.out.println("Postorder Traversal: ");
37 | tree.postorderRec();
38 | System.out.println();
39 | System.out.println("==> The tree constructor and the insert method both worked as expected!");
40 | System.out.println("==> The three traversal methods (Inorder, Postorder, and Preorder) also worked as expected!");
41 | // Now test the behavior of delete() method, with the first case of deleting a node with two children
42 | System.out.println("2) Deleting node 40 which have two children and is also the root node: ");
43 | Node newRoot = tree.delete(40);
44 | System.out.println("Inorder Traversal After Deletion: ");
45 | tree.inorderRec();
46 | System.out.println();
47 | System.out.println("Preorder Traversal After Deletion: ");
48 | tree.preorderRec();
49 | System.out.println();
50 | System.out.println("Postorder Traversal After Deletion: ");
51 | tree.postorderRec();
52 | System.out.println();
53 | System.out.println("The value of the new root node is: ");
54 | System.out.println(newRoot.key);
55 | // Now test the behavior of delete() method, with the second case of deleting a node with one child
56 | System.out.println("3) Deleting node 70 which have one child: ");
57 | Node newRoot1 = tree.delete(70);
58 | // Print out the entire tree to show its current state after deletion:
59 | System.out.println("Inorder Traversal After Deletion: ");
60 | tree.inorderRec();
61 | System.out.println();
62 | System.out.println("Preorder Traversal After Deletion: ");
63 | tree.preorderRec();
64 | System.out.println();
65 | System.out.println("Postorder Traversal After Deletion: ");
66 | tree.postorderRec();
67 | System.out.println();
68 | System.out.println("The value of the new root node is: ");
69 | System.out.println(newRoot1.key);
70 | // Now test the behavior of delete() method, with the third case of deleting a node with no children
71 | System.out.println("4) Deleting node 55 which have no children - is a leaf node: ");
72 | Node newRoot2 = tree.delete(55);
73 | // Print out the entire tree to show its current state after deletion:
74 | System.out.println("Inorder Traversal After Deletion: ");
75 | tree.inorderRec();
76 | System.out.println();
77 | System.out.println("Preorder Traversal After Deletion: ");
78 | tree.preorderRec();
79 | System.out.println();
80 | System.out.println("Postorder Traversal After Deletion: ");
81 | tree.postorderRec();
82 | System.out.println();
83 | System.out.println("The value of the new root node is: ");
84 | System.out.println(newRoot2.key);
85 | // Now find the node with the key of 20 in the tree and print the result out
86 | System.out.println("5) Now Find The Node That Has The Key Of 5: ");
87 | boolean found = tree.search(5);
88 | System.out.println(found);
89 | // Now find the node with the key of 30 in the tree and print the result out
90 | System.out.println("6) Now Find The Node That Has The Key Of 30: ");
91 | boolean found1 = tree.search(30);
92 | System.out.println(found1);
93 | // Now find the node with the key of 60 in the tree and print the result out
94 | System.out.println("7) Now Find The Node That Has The Key Of 60: ");
95 | boolean found3 = tree.search(60);
96 | System.out.println(found3);
97 | // Now find the node with the key of 95 in the tree and print the result out
98 | System.out.println("7) Now Find The Node That Has The Key Of 95: ");
99 | boolean found4 = tree.search(95);
100 | System.out.println(found4);
101 | // Now find the node with the key of 100 in the tree and print the result out
102 | System.out.println("8) Now Find The Node That Has The Key Of 100 - Which Is Not In The Tree: ");
103 | boolean found2 = tree.search(100);
104 | System.out.println(found2);
105 | System.out.println("=> The system should say that no such node with the given key in the tree and print false.");
106 | // Now find the 2nd smallest element in the current tree (after deletions), which should be 10
107 | System.out.println("9) Now Find The 2nd Smallest Element In The Tree: ");
108 | Node smallest = tree.kthSmallest(2);
109 | System.out.println(smallest.key);
110 | System.out.println("==> System Should Print 10 Because It's The 2nd Smallest!");
111 | // Now find the 5th smallest element in the current tree (after deletions), which should be 30
112 | System.out.println("10) Now Find The 5th Smallest Element In The Tree: ");
113 | Node smallest1 = tree.kthSmallest(5);
114 | System.out.println(smallest1);
115 | System.out.println(smallest1.key);
116 | System.out.println("==> System Should Print Out The Node And The Value 30 Because It's The 5th Smallest!");
117 | // Now find the 7th smallest element in the current tree (after deletions), which should be 60
118 | System.out.println("11) Now Find The 7th Smallest Element In The Tree: ");
119 | Node smallest2 = tree.kthSmallest(7);
120 | System.out.println(smallest2);
121 | System.out.println(smallest2.key);
122 | System.out.println("==> System Should Print Out The Node And The Value 60 Because It's The 7th Smallest!");
123 | // Now find the smallest element in the current tree (after deletions), which should be 60
124 | System.out.println("12) Now Find The SMALLEST Element In The Tree: ");
125 | Node smallest3 = tree.kthSmallest(1);
126 | System.out.println(smallest3);
127 | System.out.println(smallest3.key);
128 | System.out.println("==> System Should Print Out The Node And The Value 5 Because It's The Smallest!");
129 | // Now find the largest element in the current tree (after deletions), which should be 95
130 | System.out.println("14) Now Find The LARGEST Element In The Tree: ");
131 | Node smallest4 = tree.kthSmallest(9);
132 | System.out.println(smallest4.key);
133 | System.out.println("==> System Should Print Out The Node And The Value 95 Because It's The Largest!");
134 | System.out.println("15) Test If The Entered K Value Is Invalid - Greater Than Number of Nodes in Tree:");
135 | System.out.println(tree.kthSmallest(100));
136 | System.out.println("16) Test If The Entered K Value Is Invalid - Smaller Than 0:");
137 | System.out.println(tree.kthSmallest(-1));
138 | System.out.println(tree.kthSmallest(-100));
139 | System.out.println("==> System Should Print A Message Noticing The User That Enter K Value Is Invalid & Return A Null Value.");
140 | // Now try converting an array of single digits (with duplicates) into a tree
141 | System.out.println("17) Try Converting An Array of Single Digits Into A Tree - The Existing Tree Should Be Overwritten:");
142 | System.out.println("The Array To Be Parsed In Is:");
143 | int[] values = {1,3,2,4,7,8,9,6,9,9};
144 | // Prints the values in the parsed-in array out to the screen
145 | for (int value : values) {
146 | System.out.print(value + " ");
147 | }
148 | // The method createTree() will also print out the tree in three different traversals to show the current state of the tree
149 | System.out.println();
150 | tree.createTree(values);
151 | System.out.println();
152 | // Now try converting an array of multiple digits (with duplicates) into a tree
153 | System.out.println("18) Try Converting An Array of Multiple Digits Into A Tree - The Existing Tree Should Be Overwritten:");
154 | System.out.println("The Array To Be Parsed In Is:");
155 | int[] values2 = {12,33,22,45,75,8,98,56,9,99,1230,9999,10450};
156 | // Prints the values in the parsed-in array out to the screen
157 | for (int value : values2) {
158 | System.out.print(value + " ");
159 | }
160 | // The method createTree() will also print out the tree in three different traversals to show the current state of the tree
161 | System.out.println();
162 | tree.createTree(values2);
163 | System.out.println();
164 | System.out.println("==> The createTree() method works perfectly!");
165 | System.out.println("===> Now that all other methods have been tested, test the other constructors of the Binary Search Tree class");
166 | // Now that all other methods have been tested, test the other constructors of the Binary Search Tree class:
167 | // Creating a new tree and inserting nodes into the tree using the second constructor, which takes in an int
168 | BinarySearchTree tree2 = new BinarySearchTree(40);
169 | tree2.insert(20);
170 | tree2.insert(10);
171 | tree2.insert(30);
172 | tree2.insert(60);
173 | tree2.insert(50);
174 | tree2.insert(70);
175 | tree2.insert(5);
176 | tree2.insert(15);
177 | tree2.insert(55);
178 | tree2.insert(75);
179 | tree2.insert(95);
180 | // Now print out the tree to show its current state in three different types of tree traversals
181 | System.out.println("19) Create & Print Out The Following Binary Search Tree Using The Second Constructor: ");
182 | System.out.println("Inorder Traversal: ");
183 | tree2.inorderRec();
184 | System.out.println();
185 | System.out.println("Preorder Traversal: ");
186 | tree2.preorderRec();
187 | System.out.println();
188 | System.out.println("Postorder Traversal: ");
189 | tree2.postorderRec();
190 | System.out.println();
191 | System.out.println("=> The second constructor and the insert method both worked as expected!");
192 | // Creating a new tree and inserting nodes into the tree using the third constructor, which takes in a node
193 | Node rootNode = new Node(40);
194 | BinarySearchTree tree1 = new BinarySearchTree(rootNode);
195 | tree1.insert(20);
196 | tree1.insert(10);
197 | tree1.insert(30);
198 | tree1.insert(60);
199 | tree1.insert(50);
200 | tree1.insert(70);
201 | tree1.insert(5);
202 | tree1.insert(15);
203 | tree1.insert(55);
204 | tree1.insert(75);
205 | tree1.insert(95);
206 | // Now print out the tree to show its current state in three different types of tree traversals
207 | System.out.println("20) Create & Print Out The Following Binary Search Tree Using The Third Constructor: ");
208 | System.out.println("Inorder Traversal: ");
209 | tree1.inorderRec();
210 | System.out.println();
211 | System.out.println("Preorder Traversal: ");
212 | tree1.preorderRec();
213 | System.out.println();
214 | System.out.println("Postorder Traversal: ");
215 | tree1.postorderRec();
216 | System.out.println();
217 | System.out.println("=> The third constructor and the insert method both worked as expected!");
218 | /* Now all methods of a 'normal' tree have been tested, we will now test the performance of the methods when the
219 | * tree is null (i.e. empty). Test especially for Null Pointer Exceptions */
220 | System.out.println("21) Testing Search Method If Tree Is Empty: ");
221 | BinarySearchTree tree4 = new BinarySearchTree();
222 | System.out.println(tree4.search(5));
223 | System.out.println("=> The program should print 'false' because the tree is currently empty.");
224 | System.out.println("22) Testing Delete Method If Tree Is Empty");
225 | System.out.println(tree4.delete(5));
226 | System.out.println("23) Testing Three Traversal Methods If Tree Is Empty:");
227 | System.out.println("Inorder Traversal:");
228 | tree4.inorderRec();
229 | System.out.println();
230 | System.out.println("Preorder Traversal:");
231 | tree4.preorderRec();
232 | System.out.println();
233 | System.out.println("Postorder Traversal: ");
234 | tree4.postorderRec();
235 | System.out.println();
236 | System.out.println("=> Nothing should be printed out because the tree is currently empty!");
237 | System.out.println("24) Testing The Find Kth-Smallest Method If Tree Is Empty. Case 1: Test if K is positive:");
238 | System.out.println(tree4.kthSmallest(2));
239 | System.out.println("25) Testing The Find Kth-Smallest Method If Tree Is Empty. Case 2: Test if K is negative:");
240 | System.out.println(tree4.kthSmallest(-99));
241 | int[] valueEmpty = new int[6];
242 | System.out.println("26) Now try creating a B.S.T. from an empty array");
243 | for (int value : valueEmpty) {
244 | System.out.println(value);
245 | }
246 | tree4.createTree(valueEmpty);
247 | System.out.println();
248 | System.out.println("=> A tree was successfully created from the given empty array of int!");
249 | // Now we will test the case where the B.S.T. has only one node in it. Still watch out for null pointer exceptions
250 | BinarySearchTree tree6 = new BinarySearchTree();
251 | tree6.insert(9999999);
252 | System.out.println("27) Testing Search Method If Tree Has Only One Node: ");
253 | System.out.println(tree6.search(5));
254 | System.out.println("28) Testing Delete Method If Tree Has Only One Node");
255 | Node delete6 = tree6.delete(5);
256 | System.out.println(delete6);
257 | System.out.println(delete6.key);
258 | System.out.println("29) Testing Three Traversal Methods Has Only One Node:");
259 | System.out.println("Inorder Traversal:");
260 | tree6.inorderRec();
261 | System.out.println();
262 | System.out.println("Preorder Traversal:");
263 | tree6.preorderRec();
264 | System.out.println();
265 | System.out.println("Postorder Traversal: ");
266 | tree6.postorderRec();
267 | System.out.println();
268 | System.out.println("=> Only one node should be printed out because the tree has only one node!");
269 | System.out.println("30) Testing The Find Kth-Smallest Method If Tree Has Only One Node. Case 1: Test if K is valid:");
270 | Node kthSmallest2 = tree6.kthSmallest(1);
271 | System.out.println(kthSmallest2);
272 | System.out.println(kthSmallest2.key);
273 | System.out.println("=> System Should Print Out The Node And The Value 9999999");
274 | System.out.println("31) Testing The Find Kth-Smallest Method If Tree Has Only One Node. Case 2: Test if K is invalid - NEGATIVE K:");
275 | System.out.println(tree6.kthSmallest(-999));
276 | System.out.println("32) Testing The Find Kth-Smallest Method If Tree Has Only One Node. Case 3: Test if K is invalid - TOO LARGE K:");
277 | System.out.println(tree6.kthSmallest(2));
278 | System.out.println();
279 | System.out.println("===> If this statement is reached, all methods have worked correctly and performed as expected!");
280 | }
281 |
282 | }
--------------------------------------------------------------------------------
/Node.java:
--------------------------------------------------------------------------------
1 | /**
2 | * A class that represents a Node in the Binary Search Tree, which has its key (data), its left and right child pointers.
3 | * It is declared to be available for any classes in the package to use only, so it does not have any access modifiers.
4 | * @author David Nguyen
5 | * @since 03/08/2023
6 | */
7 | class Node {
8 |
9 | // A field that represents the key (data) of the node
10 | protected int key;
11 |
12 | // A field that stores the left child pointer of the node (points to the left child of the node)
13 | protected Node left;
14 |
15 | // A field that stores the right child pointer of the node (points to the right child of the node)
16 | protected Node right;
17 |
18 | /**
19 | * A constructor that creates a node instance that takes in a key (data) as an integer.
20 | * When a node is created, its left and right pointer should initially be null. Additionally, the key of the node
21 | * should also be assigned the parsed-in key value
22 | * @param key The key (data) of the node to be created
23 | */
24 | protected Node(int key) {
25 | this.key = key;
26 | this.left = null;
27 | this.right = null;
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Binary Search Tree - DSA Project
2 |
3 | ## Overview
4 | This Java project implements a Binary Search Tree (BST) with fundamental operations such as insertion, deletion, and traversal. Designed with efficiency and simplicity in mind, this class provides an intuitive way to manage and manipulate a collection of integers in a hierarchical structure.
5 |
6 | ## Features
7 | - **Dynamic Tree Construction**: Create a BST either empty, with a single root node, or by parsing an array of integers.
8 | - **Insertion**: Add new elements to the tree while maintaining the BST properties.
9 | - **Deletion**: Remove elements from the tree, adjusting the structure as necessary to preserve BST properties.
10 | - **Search**: Check whether a specific value exists within the tree.
11 | - **Traversal**: Inorder, preorder, and postorder traversal methods to explore the tree's contents.
12 | - **K-th Smallest Element**: Retrieve the k-th smallest element from the tree, demonstrating an application of inorder traversal.
13 | - **Create Tree from Array**: Build a new BST from an array of integers, replacing any existing tree.
14 |
15 | ## Prerequisites
16 | - Java Development Kit (JDK) 11 or later.
17 | - A Java IDE (e.g. IntelliJ IDEA or Eclipse) is highly recommended.
18 |
19 | ## Setup and Compilation
20 | 1. Ensure Java is installed on your system. You can verify this by running `java -version` in your command line or terminal.
21 | 2. Clone or download this repository to your local machine.
22 | 3. Navigate to the directory containing the `BinarySearchTree.java` file.
23 | 4. Compile the class using the Java compiler:
24 | ```bash
25 | javac BinarySearchTree.java
26 | ```
27 |
28 | ## Usage
29 | ### Creating a BST
30 | Instantiate a BST object in your Java program. You can create a tree in three ways:
31 | - Empty tree: `BinarySearchTree bst = new BinarySearchTree();`
32 | - Tree with an integer root: `BinarySearchTree bst = new BinarySearchTree(10);`
33 | - Tree with a Node as root: `BinarySearchTree bst = new BinarySearchTree(new Node(10));`
34 |
35 | ### Inserting Elements
36 | Add elements to your BST:
37 | ```java
38 | bst.insert(5);
39 | bst.insert(15);
40 | ```
41 |
42 | ### Deleting Elements
43 | Remove elements by value:
44 | ```java
45 | bst.delete(5);
46 | ```
47 |
48 | ### Searching for Elements
49 | Check if an element exists in the tree:
50 | ```java
51 | boolean found = bst.search(15);
52 | ```
53 |
54 | ### Traversing the Tree
55 | Perform different tree traversals:
56 | ```java
57 | bst.inorderRec();
58 | bst.preorderRec();
59 | bst.postorderRec();
60 | ```
61 |
62 | ### Finding the K-th Smallest Element
63 | Retrieve the k-th smallest element from the BST:
64 | ```java
65 | Node kthSmallest = bst.kthSmallest(3);
66 | if (kthSmallest != null) {
67 | System.out.println("K-th Smallest: " + kthSmallest.key);
68 | }
69 | ```
70 |
71 | ### Creating a Tree from an Array
72 | Overwrite the current tree with a new tree constructed from an array of integers:
73 | ```java
74 | int[] values = {3, 1, 4, 2};
75 | bst.createTree(values);
76 | ```
77 |
78 | ### Main File
79 | There is also a Main.java file, which comprehensively tests all the functions and methods of the BST class. Feel free to run it as follows:
80 | ```bash
81 | java Main
82 | ```
83 |
84 | ## Contributing
85 | Contributions to improve the Binary Search Tree implementation or extend its functionality are welcome. Please fork the repository, make your changes, and submit a pull request with a clear description of your modifications or additions.
86 |
87 | ---
88 |
89 | Created with ❤️ by [Son Nguyen](https://github.com/hoangsonww) in 2023.
90 |
--------------------------------------------------------------------------------
/out/production/Binary-Search-Tree-DSA/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Editor-based HTTP Client requests
5 | /httpRequests/
6 | # Datasource local storage ignored files
7 | /dataSources/
8 | /dataSources.local.xml
9 |
--------------------------------------------------------------------------------
/out/production/Binary-Search-Tree-DSA/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/out/production/Binary-Search-Tree-DSA/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/out/production/Binary-Search-Tree-DSA/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/out/production/Binary-Search-Tree-DSA/Binary-Search-Tree-DSA.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/out/production/Binary-Search-Tree-DSA/BinarySearchTree.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hoangsonww/Binary-Search-Tree-DSA/256713db8679d5d58abc9da3333b853969baf3a1/out/production/Binary-Search-Tree-DSA/BinarySearchTree.class
--------------------------------------------------------------------------------
/out/production/Binary-Search-Tree-DSA/Main.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hoangsonww/Binary-Search-Tree-DSA/256713db8679d5d58abc9da3333b853969baf3a1/out/production/Binary-Search-Tree-DSA/Main.class
--------------------------------------------------------------------------------
/out/production/Binary-Search-Tree-DSA/Node.class:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hoangsonww/Binary-Search-Tree-DSA/256713db8679d5d58abc9da3333b853969baf3a1/out/production/Binary-Search-Tree-DSA/Node.class
--------------------------------------------------------------------------------
/out/production/Binary-Search-Tree-DSA/README.md:
--------------------------------------------------------------------------------
1 | # Binary Search Tree - DSA Project
2 |
3 | ## Overview
4 | This Java project implements a Binary Search Tree (BST) with fundamental operations such as insertion, deletion, and traversal. Designed with efficiency and simplicity in mind, this class provides an intuitive way to manage and manipulate a collection of integers in a hierarchical structure.
5 |
6 | ## Features
7 | - **Dynamic Tree Construction**: Create a BST either empty, with a single root node, or by parsing an array of integers.
8 | - **Insertion**: Add new elements to the tree while maintaining the BST properties.
9 | - **Deletion**: Remove elements from the tree, adjusting the structure as necessary to preserve BST properties.
10 | - **Search**: Check whether a specific value exists within the tree.
11 | - **Traversal**: Inorder, preorder, and postorder traversal methods to explore the tree's contents.
12 | - **K-th Smallest Element**: Retrieve the k-th smallest element from the tree, demonstrating an application of inorder traversal.
13 | - **Create Tree from Array**: Build a new BST from an array of integers, replacing any existing tree.
14 |
15 | ## Prerequisites
16 | - Java Development Kit (JDK) 11 or later.
17 |
18 | ## Setup and Compilation
19 | 1. Ensure Java is installed on your system. You can verify this by running `java -version` in your command line or terminal.
20 | 2. Clone or download this repository to your local machine.
21 | 3. Navigate to the directory containing the `BinarySearchTree.java` file.
22 | 4. Compile the class using the Java compiler:
23 | ```bash
24 | javac BinarySearchTree.java
25 | ```
26 |
27 | ## Usage
28 | ### Creating a BST
29 | Instantiate a BST object in your Java program. You can create a tree in three ways:
30 | - Empty tree: `BinarySearchTree bst = new BinarySearchTree();`
31 | - Tree with an integer root: `BinarySearchTree bst = new BinarySearchTree(10);`
32 | - Tree with a Node as root: `BinarySearchTree bst = new BinarySearchTree(new Node(10));`
33 |
34 | ### Inserting Elements
35 | Add elements to your BST:
36 | ```java
37 | bst.insert(5);
38 | bst.insert(15);
39 | ```
40 |
41 | ### Deleting Elements
42 | Remove elements by value:
43 | ```java
44 | bst.delete(5);
45 | ```
46 |
47 | ### Searching for Elements
48 | Check if an element exists in the tree:
49 | ```java
50 | boolean found = bst.search(15);
51 | ```
52 |
53 | ### Traversing the Tree
54 | Perform different tree traversals:
55 | ```java
56 | bst.inorderRec();
57 | bst.preorderRec();
58 | bst.postorderRec();
59 | ```
60 |
61 | ### Finding the K-th Smallest Element
62 | Retrieve the k-th smallest element from the BST:
63 | ```java
64 | Node kthSmallest = bst.kthSmallest(3);
65 | if (kthSmallest != null) {
66 | System.out.println("K-th Smallest: " + kthSmallest.key);
67 | }
68 | ```
69 |
70 | ### Creating a Tree from an Array
71 | Overwrite the current tree with a new tree constructed from an array of integers:
72 | ```java
73 | int[] values = {3, 1, 4, 2};
74 | bst.createTree(values);
75 | ```
76 |
77 | ### Main File
78 | There is also a Main.java file, which comprehensively tests all the functions and methods of the BST class. Feel free to run it as follows:
79 | ```bash
80 | java Main
81 | ```
82 |
83 | ## Contributing
84 | Contributions to improve the Binary Search Tree implementation or extend its functionality are welcome. Please fork the repository, make your changes, and submit a pull request with a clear description of your modifications or additions.
85 |
86 | ---
87 |
--------------------------------------------------------------------------------