diff --git a/README.md b/README.md index 91058811..733e3f85 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,25 @@ # Data Structures & Algorithms -This repository contains implementation of some fundamental data structures and algorithms in Computer Science. It is primarily used as a teaching resource and is currently being developed by ex-2040s students. -The project uses Gradle and the structure is optimised for IntelliJ IDEA since implementation are mostly written in Java. +This repository contains implementation of some fundamental data structures and algorithms in Computer Science. It is +primarily used as a teaching resource and is currently being developed by ex-2040s students. -**Note**: This is still being developed. Those below with links mean that they are complete (alongside testing). We project to complete CS2040s course content by November and along the way, add interesting algorithms/problems. We are hopeful that the subsequent batches of students (from AY23/24 S2) will benefit greatly from this. +The project uses Gradle and the structure is optimised for IntelliJ IDEA since implementation are mostly written in +Java. + +**Note**: This is still being developed. Those below with links mean that they are complete (alongside testing). We +project to complete CS2040s course content by November and along the way, add interesting algorithms/problems. We are +hopeful that the subsequent batches of students (from AY23/24 S2) will benefit greatly from this. If you wish to contribute, do drop an email at andre_lin@u.nus.edu. ## Full List of Implementation (in alphabetical order): + ## Structures + - Adelson-Velskii and Landis (AVL) Binary Search Tree - Disjoint Set / Union Find * Quick Find - * Weighted Union + * Weighted Union * Path compression - [Hashing](src/main/java/dataStructures/hashSet) * [Chaining](src/main/java/dataStructures/hashSet/chaining) @@ -25,12 +32,12 @@ If you wish to contribute, do drop an email at andre_lin@u.nus.edu. - [Queue](src/main/java/dataStructures/queue) - Segment Tree * Array implementation - * TreeNode implementation + * TreeNode implementation - [Stack](src/main/java/dataStructures/stack) - Trie - ## Algorithms + - [Counting Sort](src/main/java/algorithms/sorting/countingSort) - [Cyclic Sort](src/main/java/algorithms/sorting/cyclicSort) * [Special case](src/main/java/algorithms/sorting/cyclicSort/simple) of O(n) time complexity @@ -49,8 +56,8 @@ If you wish to contribute, do drop an email at andre_lin@u.nus.edu. * [3-way Partitioning](src/main/java/algorithms/sorting/quickSort/threeWayPartitioning) - Radix Sort - ## Short-cut to CS2040S Material + 1. Basic structures * [Linked List](src/main/java/dataStructures/linkedList) * [Stack](src/main/java/dataStructures/stack) @@ -91,12 +98,13 @@ If you wish to contribute, do drop an email at andre_lin@u.nus.edu. * Dijkstra * Directed acyclic graphs 10. Minimum spanning tree - * Prim's + * Prim's * Kruskal's - ## Running Custom Inputs + See [here](scripts/README.md). ## Contributors + See the [team](docs/team/profiles.md)! diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index f455f74f..0162f2d1 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -1,374 +1,435 @@ - + + "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN" + "https://checkstyle.org/dtds/configuration_1_3.dtd"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + This configuration file enforces rules for the coding standard at + https://se-education.org/guides/conventions/java/intermediate.html +--> - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - + + + - - + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml new file mode 100644 index 00000000..6423654d --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/scripts/algorithms/patternFinding/RunKMP.java b/scripts/algorithms/patternFinding/RunKMP.java index 879aa338..05ec02e1 100644 --- a/scripts/algorithms/patternFinding/RunKMP.java +++ b/scripts/algorithms/patternFinding/RunKMP.java @@ -2,13 +2,16 @@ import java.util.List; +/** + * Runs the KMP algorithm. + */ public class RunKMP { -//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ -////////////////////////////////////////// This section is for user input \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ - static String seq = "abclaabcabcabc"; - static String pattern = "abc"; + //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\ + ////////////////////////////////////////// This section is for user input \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + private static String seq = "abclaabcabcabc"; + private static String pattern = "abc"; -//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ + //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\ public static void main(String[] args) { List indices = KMP.findOccurrences(seq, pattern); @@ -16,15 +19,25 @@ public static void main(String[] args) { prettyPrint(seq, pattern, indices); } + /** + * Prints the string representation of the List. + * @param arr the given List. + */ public static void display(List arr) { StringBuilder toDisplay = new StringBuilder("["); for (int num : arr) { toDisplay.append(String.format("%d ", num)); } - toDisplay = toDisplay.replace(toDisplay.length()-1, toDisplay.length(), "]"); + toDisplay = toDisplay.replace(toDisplay.length() - 1, toDisplay.length(), "]"); System.out.println(toDisplay); } + /** + * Pretty prints the sequence and pattern. + * @param seq the given string sequence. + * @param pattern the given string pattern. + * @param indices TODO. + */ public static void prettyPrint(String seq, String pattern, List indices) { StringBuilder prettySeq = new StringBuilder(" Sequence: "); StringBuilder prettyPtr = new StringBuilder("Start Pts: "); diff --git a/scripts/algorithms/sorting/RunCountingSort.java b/scripts/algorithms/sorting/RunCountingSort.java index 786a6d3d..f84a0b2a 100644 --- a/scripts/algorithms/sorting/RunCountingSort.java +++ b/scripts/algorithms/sorting/RunCountingSort.java @@ -2,26 +2,34 @@ import algorithms.sorting.countingSort.CountingSort; +/** + * Script to run Counting Sort. + */ public class RunCountingSort { -//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ -////////////////////////////////////////// This section is for user input \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ - static int[] toSort = + //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// + ////////////////////////////////////////// This section is for user input \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ + private static int[] toSort = new int[] {3, 4, 2, 65, 76, 93, 22, 1, 5, 7, 88, 54, 44, 7, 5, 6, 2, 64, 43, 22, 27, 33, 59, 64, 76, 99, 37, 7}; -//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ + //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\// public static void main(String[] args) { - toSort = CountingSort.sort(toSort); - display(toSort); + toSort = CountingSort.sort(toSort); + display(toSort); } + /** + * Prints the string representation of the array. + * + * @param arr the given array. + */ public static void display(int[] arr) { StringBuilder toDisplay = new StringBuilder("["); for (int num : arr) { toDisplay.append(String.format("%d ", num)); } - toDisplay = toDisplay.replace(toDisplay.length()-1, toDisplay.length(), "]"); + toDisplay = toDisplay.replace(toDisplay.length() - 1, toDisplay.length(), "]"); System.out.println(toDisplay); } } diff --git a/src/main/README.md b/src/main/README.md index e9b6f148..69e73789 100644 --- a/src/main/README.md +++ b/src/main/README.md @@ -1,3 +1,3 @@ -# Implementation +# Implementation -This folder contains the source code for all the algorithms and data structures listed on the home page. \ No newline at end of file +This folder contains the source code for all the algorithms and data structures listed on the home page. diff --git a/src/main/java/algorithms/binarySearch/BinarySearch.java b/src/main/java/algorithms/binarySearch/BinarySearch.java index 5a7771c1..068163f6 100644 --- a/src/main/java/algorithms/binarySearch/BinarySearch.java +++ b/src/main/java/algorithms/binarySearch/BinarySearch.java @@ -1,32 +1,34 @@ package algorithms.binarySearch; -/** Here, we are implementing BinarySearch where we search an array for a target value at O(log n) time complexity. - * +/** + * Here, we are implementing BinarySearch where we search an array for a target value at O(log n) time complexity. + *

* Assumptions: * The array is sorted in ascending order. * All elements in the array are unique. (to allow for easy testing) - * + *

* Brief Description and Implementation Invariant: * With the assumption that the array is sorted in ascending order, BinarySearch reduces the search range by half or * half + 1 (due to floor division) after every loop. This is done by reassigning the max (high) or min (low) of the * search range to the middle of the search range when the target value is smaller than or larger than the current * middle value respectively, and continuing the search thereafter. - * + *

* In both scenarios where the target is not equal to arr[mid], the high and low pointers are reassigned mid decremented * /incremented by 1. This ensures that there will never be an infinite loop as the search range will no longer include * the mid-value, causing the search range to constantly "shrink". We know we can decrement/increment mid by 1 during * the reassignment as the mid-value is definitely not the target and should no longer be in the search range. - * + *

* At the end of every loop, we know that the target value is either within the search range or does not exist in the * array, thereby ensuring the correctness of the algorithm. - * + *

* Since after each iteration, the search range is halved, it will only take a maximum of O(log n) times before the * target is either found or determined to not exist in the array. */ public class BinarySearch { /** * Searches for a target value within a sorted array using the binary search algorithm. - * @param arr the sorted array in which the search is to be performed. + * + * @param arr the sorted array in which the search is to be performed. * @param target the value to be searched for. * @return the index of the target if found, otherwise -1. */ @@ -46,4 +48,4 @@ public static int search(int[] arr, int target) { return -1; } -} \ No newline at end of file +} diff --git a/src/main/java/algorithms/binarySearch/BinarySearchTemplated.java b/src/main/java/algorithms/binarySearch/BinarySearchTemplated.java index 6557f567..f204a159 100644 --- a/src/main/java/algorithms/binarySearch/BinarySearchTemplated.java +++ b/src/main/java/algorithms/binarySearch/BinarySearchTemplated.java @@ -1,31 +1,33 @@ package algorithms.binarySearch; -/** Here, we are implementing BinarySearchTemplated where we search an array for a target value at O(log n) time +/** + * Here, we are implementing BinarySearchTemplated where we search an array for a target value at O(log n) time * complexity. - * + *

* Assumptions: * The array is sorted in ascending order. * All elements in the array are unique. (to allow for easy testing) - * + *

* Brief Description and Implementation Invariant: * With the assumption that the array is sorted in ascending order, BinarySearchTemplated reduces the search range by * half or half + 1 (due to floor division) after every loop. This is done by reassigning the max (high) or min (low) of * the search range to the middle of the search range when the target value is smaller than or larger than the current * middle value respectively, and continuing the search thereafter. - * + *

* In this version, there is no condition to check if the current mid is equal to the target to prematurely end the * search. Hence, the only way for the loop to complete is when low exceeds high. - * + *

* At the end of every loop, we know that the target value is either within the search range or does not exist in the * array, thereby ensuring the correctness of the algorithm. - * + *

* Since after each iteration, the search range is halved, it will only take a maximum of O(log n) times before the * target is either found or determined to not exist in the array. */ public class BinarySearchTemplated { /** * A utility method to compare two integers. - * @param value The current value from the array. + * + * @param value The current value from the array. * @param target The value being searched for. * @return true if the current value is less than the target, otherwise false. */ @@ -37,7 +39,7 @@ public static boolean condition(int value, int target) { /** * Conducts a binary search to find the target within the sorted array. * - * @param arr The sorted input array. + * @param arr The sorted input array. * @param target The value to be searched within the array. * @return The index of the target value if found, otherwise -1. */ @@ -63,4 +65,4 @@ public static int search(int[] arr, int target) { // Returns -1 if loop was exited without finding the target return -1; } -} \ No newline at end of file +} diff --git a/src/main/java/algorithms/binarySearch/README.md b/src/main/java/algorithms/binarySearch/README.md index 8715f179..f9dfc82c 100644 --- a/src/main/java/algorithms/binarySearch/README.md +++ b/src/main/java/algorithms/binarySearch/README.md @@ -1,19 +1,21 @@ # Binary Search -Binary search is a search algorithm that finds the position of a target value within a sorted array or list. It compares -the target value to the middle element of the search range, then, based on the comparison, narrows the search to the + +Binary search is a search algorithm that finds the position of a target value within a sorted array or list. It compares +the target value to the middle element of the search range, then, based on the comparison, narrows the search to the upper or lower half of the current search range. Two versions of binary search has been implemented in this repository - BinarySearch and BinarySearchTemplated. ## BinarySearch + ![binary search img](../../../../../docs/assets/images/BinarySearch.png) Image Source: GeeksforGeeks BinarySearch is a more straightforward and intuitive version of the binary search algorithm. In this approach, after the -mid-value is calculated, the high or low pointer is adjusted by just one unit. From the above example, after mid points -to index 4 in the first search, the low pointer moves to index 5 (+1 from 4) when narrowing the search. Similarly, when -mid points to index 7 in the second search, the high pointer shifts to index 6 (-1 from 7) when narrowing the search. -This prevents any possibility of infinite loops. During the search, the moment mid-value is equal to the target value, +mid-value is calculated, the high or low pointer is adjusted by just one unit. From the above example, after mid points +to index 4 in the first search, the low pointer moves to index 5 (+1 from 4) when narrowing the search. Similarly, when +mid points to index 7 in the second search, the high pointer shifts to index 6 (-1 from 7) when narrowing the search. +This prevents any possibility of infinite loops. During the search, the moment mid-value is equal to the target value, the search ends prematurely. Note that there is no need for a "condition" method as the condition is already captured in the predicates of the if-else blocks. @@ -26,18 +28,22 @@ the requirements of the implementation. The narrowing of the search space differs from BinarySearch - only the high pointer will be adjusted by one unit. This template will work for most binary search problems and will only require the following changes: + - Search space (high and low) - Condition method - Returned value (low or low - 1) ### Search Space (Requires change) + Simply modify the initialisation of the high and low pointer according to the [search space](#search-space-adjustment). ### Condition (Requires change) -We assume that when the condition returns true, the current value "passes" and when the condition returns false, the + +We assume that when the condition returns true, the current value "passes" and when the condition returns false, the current value "fails". Note that in this template, the conditional blocks + ``` if (condition(x)) { high = mid; @@ -45,23 +51,25 @@ if (condition(x)) { low = mid + 1; } ``` + requires elements that "fail" the condition to be on the left of the elements that "pass" the condition, see below, in a sorted array due to the way the high and low pointers are reassigned. ![binary search templated 1 img](../../../../../docs/assets/images/BinarySearchTemplated1.jpeg) Hence, we will need to implement a condition method that is able to discern between arrays that "pass" and "fail" -accurately and also place them in the correct relative positions i.e. "fail" on the left of "pass". Suppose we change -the condition method implementation in BinarySearchTemplated from `value >= target` to `value <= target`, what will -happen? +accurately and also place them in the correct relative positions i.e. "fail" on the left of "pass". Suppose we change +the condition method implementation in BinarySearchTemplated from `value >= target` to `value <= target`, what will +happen?

what will happen? The array becomes "P P F F F F" and the low and high pointers are now reassigned wrongly.
### Returned Value (Requires change) + In this implementation of BinarySearchTemplated, we return the first "pass" in the array with `return low`. This is -because our condition method implementation encompasses the target value that we are finding i.e. when +because our condition method implementation encompasses the target value that we are finding i.e. when `value == target`. ```java @@ -69,6 +77,7 @@ public static boolean condition(int value, int target) { return value >= target; } ``` + ![binary search templated 1 img](../../../../../docs/assets/images/BinarySearchTemplated2.jpeg) However, if we want to return the last "fail" in the array, we will `return low - 1`. @@ -81,8 +90,8 @@ Replace `return low` with `return low - 1` and replace arr[low] with arr[low - 1 "fail". - ### Search Space Adjustment + What should be the search space adjustment? Why is only low reassigned with an increment and not high? Due to the nature of floor division in Java's \ operator, if there are two mid-values within the search range, which is @@ -91,13 +100,13 @@ during reassignment, `low = mid`, let us take a look at the following example: ![binary search templated 1 img](../../../../../docs/assets/images/BinarySearchTemplated3.jpeg) -The search space has been narrowed down to the range of index 1 (low) to 2 (high). The mid-value is calculated, -`mid = (1 + 2) / 2`, to be 1 due to floor division. Since `2 < 5`, we enter the else block where there is reassignment +The search space has been narrowed down to the range of index 1 (low) to 2 (high). The mid-value is calculated, +`mid = (1 + 2) / 2`, to be 1 due to floor division. Since `2 < 5`, we enter the else block where there is reassignment of `low = mid`. This means that the low pointer is still pointing to index 1 and the high pointer remains unchanged at index 2. This results in an infinite loop as the search range is not narrowed down. -To resolve this issue, we need `low = mid + 1`, which will result in the low pointer pointing to index 2 in this -scenario. We still ensure correctness because the mid-value is not the target value, as the mid-value < target, and we +To resolve this issue, we need `low = mid + 1`, which will result in the low pointer pointing to index 2 in this +scenario. We still ensure correctness because the mid-value is not the target value, as the mid-value < target, and we can safely exclude it from the search range. Why do we not need to increment the high pointer during reassignment? This is because the mid-value could be the target @@ -108,12 +117,14 @@ See [here](./binarySearchTemplatedExamples/README.md) to use the template for ot Credits: [Powerful Ultimate Binary Search Template](https://leetcode.com/discuss/general-discussion/786126/python-powerful-ultimate-binary-search-template-solved-many-problems) ## Complexity Analysis + **Time**: + - Worst case: O(log n) - Average case: O(log n) -- Best case: - - BinarySearch O(1) - - BinarySearchTemplated O(log n) +- Best case: + - BinarySearch O(1) + - BinarySearchTemplated O(log n) BinarySearch: In the worst case, the target is either in the first index or does not exist in the array at all. diff --git a/src/main/java/algorithms/binarySearch/binarySearchTemplatedExamples/README.md b/src/main/java/algorithms/binarySearch/binarySearchTemplatedExamples/README.md index 0fe62793..b7df1bd4 100644 --- a/src/main/java/algorithms/binarySearch/binarySearchTemplatedExamples/README.md +++ b/src/main/java/algorithms/binarySearch/binarySearchTemplatedExamples/README.md @@ -1,8 +1,9 @@ # BinarySearchTemplated Examples -We will be utilising the BinarySearchTemplated implementation to solve some problems. +We will be utilising the BinarySearchTemplated implementation to solve some problems. The following will differ between problems: + - Search Space - Condition - Returned Value @@ -12,31 +13,35 @@ The following will differ between problems: A problem is well-suited for this approach when it meets the following criteria: - Clear Distinction Between Categories: There should be a definitive way to categorize elements into two + groups: "pass" and "fail". - Grouped Categories: All the elements in one category should be grouped together without interspersion. This + implies that if we move from a "fail" element to a "pass" one, all subsequent elements will be "pass". - Monotonic Property: There needs to be a consistent, unidirectional trend or property in the search space. -This means the condition being checked either consistently increases or decreases. For the classical binary search on a -sorted array, this monotonic property is the sorted order of the elements, where the array elements increase + +This means the condition being checked either consistently increases or decreases. For the classical binary search on a +sorted array, this monotonic property is the sorted order of the elements, where the array elements increase (or decrease) consistently. ## 1. First Bad Version [Easy] ### Problem: -You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of -your product fails the quality check. Since each version is developed based on the previous version, all the versions +You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of +your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad. -Suppose you have `n` versions `[1, 2, ..., n]` and you want to find out the first bad one, which causes all the +Suppose you have `n` versions `[1, 2, ..., n]` and you want to find out the first bad one, which causes all the following ones to be bad. -You are given an API bool `isBadVersion(version)` which returns whether `version` is bad. Implement a function to find +You are given an API bool `isBadVersion(version)` which returns whether `version` is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API. Example 1: + ``` Input: n = 5, bad = 4 Output: 4 @@ -94,14 +99,15 @@ public int firstBadVersion(int n) { A conveyor belt has packages that must be shipped from one port to another within `days` days. -The `ith` package on the conveyor belt has a weight of `weights[i]`. Each day, we load the ship with packages on the -conveyor belt (in the order given by `weights`). We may not load more weight than the maximum weight capacity of the +The `ith` package on the conveyor belt has a weight of `weights[i]`. Each day, we load the ship with packages on the +conveyor belt (in the order given by `weights`). We may not load more weight than the maximum weight capacity of the ship. -Return the least weight capacity of the ship that will result in all the packages on the conveyor belt being shipped +Return the least weight capacity of the ship that will result in all the packages on the conveyor belt being shipped within `days` days. Example 1: + ``` Input: weights = [1,2,3,4,5,6,7,8,9,10], days = 5 Output: 15 @@ -120,7 +126,7 @@ parts like (2, 3, 4, 5), (1, 6, 7), (8), (9), (10) is not allowed. On first glance, while we are given an array, simply binary searching the array does not solve the problem. However, since our goal is to determine the least weight capacity of the ship, could this be our "target"? The answer is yes. -By having each element being a least weight capacity case, with the lowest index being the heaviest weight and the +By having each element being a least weight capacity case, with the lowest index being the heaviest weight and the highest index being the sum of all weights, we will have our search space. The monotonic property present is that if we can ship all packages within `D` days with capacity `m`, we can definitely ship them within `D` days with any capacity greater than `m`, such as `m + 1`. @@ -200,4 +206,4 @@ public int shipWithinDays(int[] weights, int days) { return low; // return first "pass" } -``` \ No newline at end of file +``` diff --git a/src/main/java/algorithms/graphs/breadthFirstSearch.java b/src/main/java/algorithms/graphs/BreadthFirstSearch.java similarity index 51% rename from src/main/java/algorithms/graphs/breadthFirstSearch.java rename to src/main/java/algorithms/graphs/BreadthFirstSearch.java index d19a85bb..df737137 100644 --- a/src/main/java/algorithms/graphs/breadthFirstSearch.java +++ b/src/main/java/algorithms/graphs/BreadthFirstSearch.java @@ -1,71 +1,93 @@ package algorithms.graphs; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; import algorithms.graphs.util.BinaryTreeNode; import algorithms.graphs.util.GraphNode; /** * Implementation of BFS - * + *

* Breadth-First search is a graph traversal algorithm that utilizes a queue (FIFO) data structure * It is useful in finding a shortest-hops solution in graphs * This method is also used to obtain level order traversals of trees. - * + *

* In general, BFS works as such: - * - Start with a queue that contains the root node - * - While the queue is not empty: - * - Pop a vertex from the queue - * - Push all neighbours to the queue if they have not been visited yet - * - Update any variables as needed - * + * - Start with a queue that contains the root node + * - While the queue is not empty: + * - Pop a vertex from the queue + * - Push all neighbours to the queue if they have not been visited yet + * - Update any variables as needed + *

* Time: O(V + E), where V is the number of vertices/nodes, and E is the number of edges in the graph * Explanation: Each vertex is popped in O(1) time from the stack exactly once, hence O(V) - * For each edge, we must check in O(1) time if we have visited the adjacent vertex to know - * whether to push it into the queue, hence O(E). - * Note that if the graph is a forest, BFS will likely terminate earlier, as fewer vertices are traversed - * + * For each edge, we must check in O(1) time if we have visited the adjacent vertex to know + * whether to push it into the queue, hence O(E). + * Note that if the graph is a forest, BFS will likely terminate earlier, as fewer vertices are traversed + *

* Space: O(V): We utilize a Hashset to store the vertices we have visited already. In the worst case we have a - * connected graph where all vertices are traversed, and our Hashset stores all O(V) vertices. - * Further, we use a queue to hold the vertices to be traversed. In the worst case, the root will have all - * other vertices as neighbours, and our queue will contain O(V) vertices. - * + * connected graph where all vertices are traversed, and our Hashset stores all O(V) vertices. + * Further, we use a queue to hold the vertices to be traversed. In the worst case, the root will have all + * other vertices as neighbours, and our queue will contain O(V) vertices. + *

* ** Note: The above description assumes an adjacency list in order to consider neighbours in an efficient manner - * If an adjacency matrix were used, it would cost O(V) to find neighbours for a single vertex, making our - * average case time complexity O(V^2) for a connected graph - * - * The implementation demonstrates the use of BFS in finding the level-order (Root, Left, Right) traversal of a binary tree + * If an adjacency matrix were used, it would cost O(V) to find neighbours for a single vertex, making our + * average case time complexity O(V^2) for a connected graph + *

+ * The implementation demonstrates the use of BFS in finding the level-order (Root, Left, Right) traversal of a + * binary tree * The tree is represented using a custom BinaryTreeNode class - * */ -public class breadthFirstSearch { +public class BreadthFirstSearch { - // Prints level order traversal from left to right + /** + * Returns the levelOrder traversal of the tree. + * + * @param root the node to start the traversal from. + * @return the list of nodes in the order they were visited. + */ public static List levelOrder(BinaryTreeNode root) { - if (root == null) { return new ArrayList<>(); } + if (root == null) { + return new ArrayList<>(); + } List traversal = new ArrayList<>(); Queue queue = new LinkedList<>(); queue.add(root); while (!queue.isEmpty()) { BinaryTreeNode curr = queue.remove(); - traversal.add((Integer) curr.getVal()); - if (curr.getLeft() != null) { queue.add(curr.getLeft()); } - if (curr.getRight() != null) { queue.add(curr.getRight()); } + traversal.add(curr.getVal()); + if (curr.getLeft() != null) { + queue.add(curr.getLeft()); + } + if (curr.getRight() != null) { + queue.add(curr.getRight()); + } } return traversal; } - // Finds the number of friend hops needed to go from person A to person B - // Uses GraphNode, where a node holds a String of the persons name, and an edge represents a friendship + /** + * Finds the number of friend hops needed to go from person A to person B. + * Uses GraphNode, where a node holds a String of the persons name, and an edge represents a friendship + * + * @param personA the node to start from. + * @param personB the node to end. + * @return the number of hops needed. + */ public static int friendHops(GraphNode personA, GraphNode personB) { // Hashset to store the people we have seen already - HashSet checked = new HashSet<>(); + HashSet> checked = new HashSet<>(); // Hashmap to remember how many hops were needed to get to a specific friend * - HashMap map = new HashMap<>(); + HashMap, Integer> map = new HashMap<>(); - Queue queue = new LinkedList<>(); + Queue> queue = new LinkedList<>(); queue.add(personA); // the number of hops to the person themselves is 0, so we map: personA -> 0 map.put(personA, 0); @@ -79,9 +101,11 @@ public static int friendHops(GraphNode personA, GraphNode person // grab the number of hops from the hashmap and add 1 hops = map.get(currPerson) + 1; - List neighbours = currPerson.neighbours(); - for (GraphNode neighbour : neighbours) { - if (neighbour == personB) { return hops; } + List> neighbours = currPerson.neighbours(); + for (GraphNode neighbour : neighbours) { + if (neighbour == personB) { + return hops; + } if (!checked.contains(neighbour)) { queue.add(neighbour); map.put(neighbour, hops); @@ -95,20 +119,28 @@ public static int friendHops(GraphNode personA, GraphNode person // This is because the hashmap supports the map.containsKey() function. } + /** + * Finds the number of friend hops needed to go from person A to person B. Prints to std::out as a side-effect. + * Uses GraphNode, where a node holds a String of the persons name, and an edge represents a friendship + * + * @param personA the node to start from. + * @param personB the node to end. + * @return the number of hops needed. + */ public static int friendHopsVisualize(GraphNode personA, GraphNode personB) { // Hashset to store the people we have seen already - HashSet checked = new HashSet<>(); + HashSet> checked = new HashSet<>(); // Hashmap to remember how many hops were needed to get to a specific friend * - HashMap map = new HashMap<>(); + HashMap, Integer> map = new HashMap<>(); - Queue queue = new LinkedList<>(); + Queue> queue = new LinkedList<>(); queue.add(personA); // the number of hops to the person themselves is 0, so we map: personA -> 0 map.put(personA, 0); int hops = 0; while (!queue.isEmpty()) { - System.out.println("Current queue: " + String.valueOf(queue) + ", current hops: " + hops); + System.out.println("Current queue: " + queue + ", current hops: " + hops); // poll the queue to get the next person to consider GraphNode currPerson = queue.remove(); // add the person to the checked hashset so we don't consider them again @@ -117,15 +149,17 @@ public static int friendHopsVisualize(GraphNode personA, GraphNode neighbours = currPerson.neighbours(); - for (GraphNode neighbour : neighbours) { - if (neighbour == personB) { return hops; } + List> neighbours = currPerson.neighbours(); + for (GraphNode neighbour : neighbours) { + if (neighbour == personB) { + return hops; + } if (!checked.contains(neighbour)) { queue.add(neighbour); map.put(neighbour, hops); } } - System.out.println("Current queue: " + String.valueOf(queue) + ", current hops: " + hops); + System.out.println("Current queue: " + queue + ", current hops: " + hops); } // Returns -1 if person not found return -1; diff --git a/src/main/java/algorithms/graphs/DepthFirstSearch.java b/src/main/java/algorithms/graphs/DepthFirstSearch.java new file mode 100644 index 00000000..9238bd6a --- /dev/null +++ b/src/main/java/algorithms/graphs/DepthFirstSearch.java @@ -0,0 +1,156 @@ +package algorithms.graphs; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + +import algorithms.graphs.util.BinaryTreeNode; + +/** + * Implementation of DFS + *

+ * Depth-First search is a graph traversal algorithm that utilizes a stack (LIFO) data structure + * It is useful in finding a shortest-path solution (IN TREES), or a single solution in graphs + * This method is also used to obtain order-traversals of trees. + *

+ * In general, DFS works as such: + * - Start with a stack that contains the root node + * - While the stack is not empty: + * - Pop a vertex from the stack + * - Push all neighbours to the stack if they have not been visited yet + * - Update any variables as needed + *

+ * Time: O(V + E), where V is the number of vertices/nodes, and E is the number of edges in the graph + * Explanation: Each vertex is popped in O(1) time from the stack exactly once, hence O(V) + * For each edge, we must check in O(1) time if we have visited the adjacent vertex to know + * whether to push it into the stack, hence O(E). + * Note that if the graph is a forest, DFS will likely terminate earlier, as fewer vertices are traversed + *

+ * Space: O(V): We utilize a Hashset to store the vertices we have visited already. In the worst case we have a + * connected graph where all vertices are traversed, and our Hashset stores all O(V) vertices. + * Further, we use a stack to hold the vertices to be traversed. In the worst case, the root will have all + * other vertices as neighbours, and our stack will contain O(V) vertices. + *

+ * ** Note: The above description assumes an adjacency list in order to consider neighbours in an efficient manner + * If an adjacency matrix were used, it would cost O(V) to find neighbours for a single vertex, making our + * average case time complexity O(V^2) for a connected graph + *

+ * The implementation demonstrates the use of DFS in finding the order traversals of a binary tree + * The tree is represented using a custom BinaryTreeNode class + */ + +public class DepthFirstSearch { + + /** + * Returns the preorder traversal of the tree. + * + * @param root the node to start the traversal from. + * @return the list of nodes in the order they were visited. + */ + public static List preOrder(BinaryTreeNode root) { + if (root == null) { + return new ArrayList<>(); + } + List traversal = new ArrayList<>(); + Stack stack = new Stack<>(); + stack.push(root); + + while (!stack.empty()) { + BinaryTreeNode curr = stack.pop(); + traversal.add(curr.getVal()); + if (curr.getRight() != null) { + stack.push(curr.getRight()); + } + if (curr.getLeft() != null) { + stack.push(curr.getLeft()); + } + } + + return traversal; + } + + /** + * Returns the inorder traversal of the tree. + * + * @param root the node to start the traversal from. + * @return the list of nodes in the order they were visited. + */ + public static List inOrder(BinaryTreeNode root) { + if (root == null) { + return new ArrayList<>(); + } + List traversal = new ArrayList<>(); + traversal.add(root.getVal()); + if (root.getLeft() == null && root.getRight() == null) { + return traversal; + } else { + // we combine the traversal of the left subtree with the root and the traversal of the right subtree + traversal.addAll(inOrder(root.getRight())); + List left = inOrder(root.getLeft()); + left.addAll(traversal); + return left; + } + } + + /** + * Returns the postorder traversal of the tree. + * + * @param root the node to start the traversal from. + * @return the list of nodes in the order they were visited. + */ + public static List postOrder(BinaryTreeNode root) { + if (root == null) { + return new ArrayList<>(); + } + List traversal = new ArrayList<>(); + traversal.add(root.getVal()); + if (root.getLeft() == null && root.getRight() == null) { + return traversal; + } else { + // we combine the traversal of the left and right subtrees with the root + List leftAndRight = postOrder(root.getLeft()); + leftAndRight.addAll(postOrder(root.getRight())); + leftAndRight.addAll(traversal); + return leftAndRight; + } + } + + /** + * Returns the preorder traversal of the tree. Prints the nodes visited to std::out as a side-effect. + * + * @param root the node to start the traversal from. + * @return the list of nodes in the order they were visited. + */ + public static List preOrderVisualize(BinaryTreeNode root) { + if (root == null) { + return new ArrayList<>(); + } + List traversal = new ArrayList<>(); + Stack stack = new Stack<>(); + stack.push(root); + + while (!stack.empty()) { + System.out.println("Current stack: " + stack + ", Current traversal: " + traversal); + BinaryTreeNode curr = stack.pop(); + System.out.println("Popped Node: " + curr.getVal()); + traversal.add(curr.getVal()); + System.out.println("Current stack: " + stack + ", Current traversal: " + traversal); + + if (curr.getRight() != null) { + stack.push(curr.getRight()); + System.out.println("Pushing right node: " + curr.getRight().getVal()); + System.out.println( + "Current stack: " + stack + ", Current traversal: " + traversal); + } + if (curr.getLeft() != null) { + stack.push(curr.getLeft()); + System.out.println("Pushing left node: " + curr.getLeft().getVal()); + System.out.println( + "Current stack: " + stack + ", Current traversal: " + traversal); + } + } + + return traversal; + } + +} diff --git a/src/main/java/algorithms/graphs/depthFirstSearch.java b/src/main/java/algorithms/graphs/depthFirstSearch.java deleted file mode 100644 index 9f62b1f7..00000000 --- a/src/main/java/algorithms/graphs/depthFirstSearch.java +++ /dev/null @@ -1,117 +0,0 @@ -package algorithms.graphs; - -import java.util.*; -import algorithms.graphs.util.BinaryTreeNode; - -/** - * Implementation of DFS - * - * Depth-First search is a graph traversal algorithm that utilizes a stack (LIFO) data structure - * It is useful in finding a shortest-path solution (IN TREES), or a single solution in graphs - * This method is also used to obtain order-traversals of trees. - * - * In general, DFS works as such: - * - Start with a stack that contains the root node - * - While the stack is not empty: - * - Pop a vertex from the stack - * - Push all neighbours to the stack if they have not been visited yet - * - Update any variables as needed - * - * Time: O(V + E), where V is the number of vertices/nodes, and E is the number of edges in the graph - * Explanation: Each vertex is popped in O(1) time from the stack exactly once, hence O(V) - * For each edge, we must check in O(1) time if we have visited the adjacent vertex to know - * whether to push it into the stack, hence O(E). - * Note that if the graph is a forest, DFS will likely terminate earlier, as fewer vertices are traversed - * - * Space: O(V): We utilize a Hashset to store the vertices we have visited already. In the worst case we have a - * connected graph where all vertices are traversed, and our Hashset stores all O(V) vertices. - * Further, we use a stack to hold the vertices to be traversed. In the worst case, the root will have all - * other vertices as neighbours, and our stack will contain O(V) vertices. - * - * ** Note: The above description assumes an adjacency list in order to consider neighbours in an efficient manner - * If an adjacency matrix were used, it would cost O(V) to find neighbours for a single vertex, making our - * average case time complexity O(V^2) for a connected graph - * - * The implementation demonstrates the use of DFS in finding the order traversals of a binary tree - * The tree is represented using a custom BinaryTreeNode class - * - */ - -public class depthFirstSearch { - - public static List preOrder(BinaryTreeNode root) { - if (root == null) { return new ArrayList<>(); } - List traversal = new ArrayList<>(); - Stack stack = new Stack<>(); - stack.push(root); - - while (!stack.empty()) { - BinaryTreeNode curr = stack.pop(); - traversal.add(curr.getVal()); - if (curr.getRight() != null) { stack.push(curr.getRight()); } - if (curr.getLeft() != null) { stack.push(curr.getLeft()); } - } - - return traversal; - } - - public static List inOrder(BinaryTreeNode root) { - if (root == null) { return new ArrayList<>(); } - List traversal = new ArrayList<>(); - traversal.add(root.getVal()); - if (root.getLeft() == null && root.getRight() == null) { - return traversal; - } else { - // we combine the traversal of the left subtree with the root and the traversal of the right subtree - traversal.addAll(inOrder(root.getRight())); - List left = inOrder(root.getLeft()); - left.addAll(traversal); - return left; - } - } - - public static List postOrder(BinaryTreeNode root) { - if (root == null) { return new ArrayList<>(); } - List traversal = new ArrayList<>(); - traversal.add(root.getVal()); - if (root.getLeft() == null && root.getRight() == null) { - return traversal; - } else { - // we combine the traversal of the left and right subtrees with the root - List leftAndRight = postOrder(root.getLeft()); - leftAndRight.addAll(postOrder(root.getRight())); - leftAndRight.addAll(traversal); - return leftAndRight; - } - } - - // call this for visualization of process - public static List preOrderVisualize(BinaryTreeNode root) { - if (root == null) { return new ArrayList<>(); } - List traversal = new ArrayList<>(); - Stack stack = new Stack<>(); - stack.push(root); - - while (!stack.empty()) { - System.out.println("Current stack: " + stack.toString() + ", Current traversal: " + traversal.toString()); - BinaryTreeNode curr = stack.pop(); - System.out.println("Popped Node: " + Integer.toString(curr.getVal())); - traversal.add(curr.getVal()); - System.out.println("Current stack: " + stack.toString() + ", Current traversal: " + traversal.toString()); - - if (curr.getRight() != null) { - stack.push(curr.getRight()); - System.out.println("Pushing right node: " + Integer.toString(curr.getRight().getVal())); - System.out.println("Current stack: " + stack.toString() + ", Current traversal: " + traversal.toString()); - } - if (curr.getLeft() != null) { - stack.push(curr.getLeft()); - System.out.println("Pushing left node: " + Integer.toString(curr.getLeft().getVal())); - System.out.println("Current stack: " + stack.toString() + ", Current traversal: " + traversal.toString()); - } - } - - return traversal; - } - -} diff --git a/src/main/java/algorithms/graphs/util/BinaryTreeNode.java b/src/main/java/algorithms/graphs/util/BinaryTreeNode.java index 4fc6a034..9b7720f0 100644 --- a/src/main/java/algorithms/graphs/util/BinaryTreeNode.java +++ b/src/main/java/algorithms/graphs/util/BinaryTreeNode.java @@ -1,39 +1,75 @@ package algorithms.graphs.util; +/** + * Encapsulates a Binary Tree Node. + */ public class BinaryTreeNode { - int val; - BinaryTreeNode left = null; - BinaryTreeNode right = null; + private final int val; + private BinaryTreeNode left = null; + private BinaryTreeNode right = null; + /** + * Constructs a Binary Tree Node. + * + * @param val the value encapsulated in the node. + */ public BinaryTreeNode(int val) { this.val = val; } + /** + * Constructs a Binary Tree Node with a left and right child. + * + * @param val the value encapsulated in the node. + * @param left the left child. + * @param right the right child. + */ public BinaryTreeNode(int val, BinaryTreeNode left, BinaryTreeNode right) { this.val = val; this.left = left; this.right = right; } - public int getVal() { return this.val; } - public BinaryTreeNode getLeft() { return this.left; } - public BinaryTreeNode getRight() { return this.right; } + public int getVal() { + return this.val; + } + + public BinaryTreeNode getLeft() { + return this.left; + } + + public BinaryTreeNode getRight() { + return this.right; + } + + public void setLeft(BinaryTreeNode left) { + this.left = left; + } - public void setLeft(BinaryTreeNode left) { this.left = left; } - public void setRight(BinaryTreeNode right) { this.right = right; } + public void setRight(BinaryTreeNode right) { + this.right = right; + } @Override public boolean equals(Object other) { - if (other == this) { return true; } - if (!(other instanceof BinaryTreeNode)) { return false; } + if (other == this) { + return true; + } + if (!(other instanceof BinaryTreeNode)) { + return false; + } BinaryTreeNode node = (BinaryTreeNode) other; return this.val == node.val; } @Override - public int hashCode() { return this.val; } + public int hashCode() { + return this.val; + } @Override - public String toString() { return String.valueOf(this.val); } + public String toString() { + return String.valueOf(this.val); + } } diff --git a/src/main/java/algorithms/graphs/util/GraphNode.java b/src/main/java/algorithms/graphs/util/GraphNode.java index 5dbfc8b9..1862aa4b 100644 --- a/src/main/java/algorithms/graphs/util/GraphNode.java +++ b/src/main/java/algorithms/graphs/util/GraphNode.java @@ -1,39 +1,79 @@ package algorithms.graphs.util; -import java.util.*; +import java.util.ArrayList; +import java.util.List; -public class GraphNode { - public T val; - public List neighbour; +/** + * Encapsulates a Graph Node. + * + * @param the Generic type stored in the node. + */ +public class GraphNode { + private final T val; + private final List> neighbour; + /** + * Constructs a GraphNode. + * + * @param val the value to be encapsulated by the node. + */ public GraphNode(T val) { this.val = val; neighbour = new ArrayList<>(); } - public GraphNode(T val, List neighbours) { + /** + * Constructs a GraphNode with edges. + * + * @param val the value to be encapsulated. + * @param neighbours the neighbours of the node. + */ + public GraphNode(T val, List> neighbours) { this.val = val; this.neighbour = neighbours; } + /** + * Connects two nodes with an edge. + * + * @param a the node to be connected. + * @param b the other node to be connected. + * @param the generic type of the nodes to be connected. + */ public static void connect(GraphNode a, GraphNode b) { a.neighbour.add(b); b.neighbour.add(a); } + /** + * Returns the neighbours of this node. + * + * @return the neighbours of this node. + */ + public List> neighbours() { + return this.neighbour; + } - public List neighbours() { return this.neighbour; } - - public T getVal() { return this.val; } + public T getVal() { + return this.val; + } @Override - public String toString() { return String.valueOf(this.val); } + public String toString() { + return String.valueOf(this.val); + } @Override public boolean equals(Object other) { - if (other == this) { return true; } - if (!(other instanceof GraphNode)) { return false; } - GraphNode node = (GraphNode) other; + if (other == this) { + return true; + } + if (!(other instanceof GraphNode)) { + return false; + } + // Safe cast. + @SuppressWarnings("unchecked") + GraphNode node = (GraphNode) other; return this.val == node.val; } diff --git a/src/main/java/algorithms/minimumSpanningTree/kruskals/Kruskal.java b/src/main/java/algorithms/minimumSpanningTree/kruskals/Kruskal.java index 9c6f1d9f..51774e32 100644 --- a/src/main/java/algorithms/minimumSpanningTree/kruskals/Kruskal.java +++ b/src/main/java/algorithms/minimumSpanningTree/kruskals/Kruskal.java @@ -1,6 +1,7 @@ package algorithms.minimumSpanningTree.kruskals; -import java.util.*; +import java.util.Comparator; +import java.util.PriorityQueue; /** * Very ugly use of java generics - will refactor one day.. @@ -8,8 +9,8 @@ */ public class Kruskal { private class Node { - int size; - Node parent; + private int size; + private Node parent; public Node() { this.size = 1; @@ -28,6 +29,7 @@ public Node findParent() { /** * Make child a child of this node + * * @param child */ public void makeChild(Node child) { @@ -36,6 +38,12 @@ public void makeChild(Node child) { } } + /** + * TODO documentation. + * + * @param adjM the adjacency matrix. + * @return TODO return. + */ public int minCostConnectPoints(int[][] adjM) { int v = adjM.length; Node[] nodesMapping = new Node[v]; @@ -43,20 +51,20 @@ public int minCostConnectPoints(int[][] adjM) { nodesMapping[i] = new Node(); } - PriorityQueue>> heap = new PriorityQueue<>( - (a,b) -> a.key - b.key - ); + PriorityQueue>> heap = + new PriorityQueue<>(Comparator.comparingInt(Pair::getKey)); + for (int i = 0; i < v; i++) { for (int j = i + 1; j < v; j++) { Pair edge = new Pair<>(i, j); if (adjM[i][j] != 0) { - heap.add(new Pair>(adjM[i][j], edge)); + heap.add(new Pair<>(adjM[i][j], edge)); } } } int ans = 0; - while (heap.size() != 0) { + while (!heap.isEmpty()) { Pair> curr = heap.poll(); Pair edge = curr.getValue(); Node fr = nodesMapping[edge.getKey()]; diff --git a/src/main/java/algorithms/minimumSpanningTree/kruskals/Pair.java b/src/main/java/algorithms/minimumSpanningTree/kruskals/Pair.java index 29e1f808..6f19f184 100644 --- a/src/main/java/algorithms/minimumSpanningTree/kruskals/Pair.java +++ b/src/main/java/algorithms/minimumSpanningTree/kruskals/Pair.java @@ -1,8 +1,21 @@ package algorithms.minimumSpanningTree.kruskals; +/** + * Encapsulates a key-value pair. + * + * @param the generic type of the key. + * @param the generic type of the value. + */ public class Pair { - K key; - V val; + private final K key; + private final V val; + + /** + * Constructs a Pair containing a key and a value. + * + * @param key the key to be held. + * @param val the value to be held. + */ public Pair(K key, V val) { this.key = key; this.val = val; @@ -15,4 +28,4 @@ public K getKey() { public V getValue() { return this.val; } -} \ No newline at end of file +} diff --git a/src/main/java/algorithms/minimumSpanningTree/prims/Pair.java b/src/main/java/algorithms/minimumSpanningTree/prims/Pair.java index 5a8a3e4c..0d6cc805 100644 --- a/src/main/java/algorithms/minimumSpanningTree/prims/Pair.java +++ b/src/main/java/algorithms/minimumSpanningTree/prims/Pair.java @@ -1,15 +1,38 @@ package algorithms.minimumSpanningTree.prims; /** - * Helper class to encapsulate information about an edge; - * specifically, tells us the wieght of an edge and the node on the other end + * Helper class to encapsulate information about an edge; + * specifically, tells us the weight of an edge and the node on the other end * (assumes that the starting node of the edge is known) */ public class Pair { - int dist; - int endNode; + private int dist; + private int endNode; + + /** + * Constructs an instance of a pair, that represents an edge. + * + * @param dist the distance encapsulated. + * @param endNode the node at the end of the edge. + */ public Pair(int dist, int endNode) { this.dist = dist; this.endNode = endNode; } -} \ No newline at end of file + + public int getDist() { + return dist; + } + + public void setDist(int dist) { + this.dist = dist; + } + + public int getEndNode() { + return endNode; + } + + public void setEndNode(int endNode) { + this.endNode = endNode; + } +} diff --git a/src/main/java/algorithms/minimumSpanningTree/prims/Prim.java b/src/main/java/algorithms/minimumSpanningTree/prims/Prim.java index a1db5c20..f2c49504 100644 --- a/src/main/java/algorithms/minimumSpanningTree/prims/Prim.java +++ b/src/main/java/algorithms/minimumSpanningTree/prims/Prim.java @@ -1,35 +1,38 @@ package algorithms.minimumSpanningTree.prims; -import java.util.*; +import java.util.Comparator; +import java.util.HashSet; +import java.util.PriorityQueue; +import java.util.Set; -/** +/** * Implementation of Prim's Algorithm to find MSTs - * Idea: - * Starting from any source (this will be the first node to be in the MST), - * pick the lighest outgoing edge, and include the node at the other end as part of MST - * Now repeatedly do the above by picking the lighest outgoing edge adjacent to any node in the MST - * (ensuring the other end of the node is not already in the MST) - * Do until the MST has all the nodes. - * + * Idea: + * Starting from any source (this will be the first node to be in the MST), + * pick the lightest outgoing edge, and include the node at the other end as part of MST + * Now repeatedly do the above by picking the lightest outgoing edge adjacent to any node in the MST + * (ensuring the other end of the node is not already in the MST) + * Do until the MST has all the nodes. + *

* Motivating Example: Minimum Cost to Connect All Points - * - A -9- C -2- E - / / \ \ - 3 4 7 2 - / / \ / - F -1- B --5-- D -*/ - -/** + *

+ * A -9- C -2- E + * / / \ \ + * 3 4 7 2 + * / / \ / + * F -1- B --5-- D + *

* Implementation 1: Using heap - * Time: O(V) + O(ElogE) (since heap could possibly hold E number of weights) + O(E-V) (nodes that have been 'seen' are still added to the heap, just not expanded) + O(V^2) - * Space: O(V) (hashmap to decide on MST) + O(E) (heap) = O(V+E) = O(E) + * Time: O(V) + O(ElogE) (since heap could possibly hold E number of weights) + O(E-V) (nodes that have been 'seen' + * are still added to the heap, just not expanded) + O(V^2) + * Space: O(V) (hashmap to decide on MST) + O(E) (heap) = O(V+E) = O(E) */ - public class Prim { /** * points: Adjacency matrix that encapsulates the distance/weight between nodes - * adjM[i][j] is the weight of the edge connecting points i and j; a value of 0 suggests there is no connection between i and j + * adjM[i][j] is the weight of the edge connecting points i and j; a value of 0 suggests there is no connection + * between i and j + * * @param adjM Adjacency matrix that encapsulates the distance/weight between nodes * @return minimum weight of the spanning tree */ @@ -38,9 +41,7 @@ public int minCostConnectPoints(int[][] adjM) { int minCost = 0; Set mst = new HashSet<>(); mst.add(0); - PriorityQueue pq = new PriorityQueue<>( - (a, b) -> a.dist - b.dist - ); + PriorityQueue pq = new PriorityQueue<>(Comparator.comparingInt(Pair::getDist)); for (int i = 0; i < v; i++) { if (!mst.contains(i)) { if (adjM[0][i] != 0) { // ensure valid edge @@ -50,17 +51,17 @@ public int minCostConnectPoints(int[][] adjM) { } while (mst.size() != v) { Pair popped = pq.poll(); - if (mst.contains(popped.endNode)) { + if (mst.contains(popped.getEndNode())) { continue; } - minCost += popped.dist; - mst.add(popped.endNode); + minCost += popped.getDist(); + mst.add(popped.getEndNode()); for (int i = 0; i < v; i++) { if (mst.contains(i)) { continue; } - if (adjM[popped.endNode][i] != 0) { // ensure valid edge - pq.add(new Pair(adjM[popped.endNode][i], i)); + if (adjM[popped.getEndNode()][i] != 0) { // ensure valid edge + pq.add(new Pair(adjM[popped.getEndNode()][i], i)); } } } @@ -69,10 +70,11 @@ public int minCostConnectPoints(int[][] adjM) { /** * Alternative implementation that simply uses array to hold weights rather than heap. - * Note: Starts from the node labelled 0 and repeatedly update + * Note: Starts from the node labelled 0 and repeatedly update weights * which stores the minimum weight from any node in the MST to other nodes. * Time: O(V) + O(V*2V) * Space: O(V) + * * @param adjM Adjacency matrix that encapsulates the distance/weight between nodes * @return minimum weight of the spanning tree */ @@ -80,9 +82,7 @@ public int minCostConnectPoints2(int[][] adjM) { int v = adjM.length; int[] weights = new int[v]; - for (int i = 0; i < v; i++) { - weights[i] = adjM[0][i]; - } + System.arraycopy(adjM[0], 0, weights, 0, v); Set mst = new HashSet<>(); mst.add(0); // start from source 0 @@ -91,7 +91,8 @@ public int minCostConnectPoints2(int[][] adjM) { int next = v; for (int i = 0; i < v; i++) { if (!mst.contains(i)) { - if (weights[i] != 0 && (next == v || weights[i] < weights[next])) { // first check for valid connection, then try to find min weight + if (weights[i] != 0 && (next == v + || weights[i] < weights[next])) { // check for valid connection, then try to find min weight next = i; } } @@ -101,7 +102,8 @@ public int minCostConnectPoints2(int[][] adjM) { for (int i = 0; i < v; i++) { if (!mst.contains(i)) { - if (weights[i] == 0 || adjM[next][i] < weights[i]) { // update shortest dist to nodes that are not added to mst yet + if (weights[i] == 0 + || adjM[next][i] < weights[i]) { // update shortest dist to nodes that are not added to mst yet weights[i] = adjM[next][i]; } } diff --git a/src/main/java/algorithms/patternFinding/KMP.java b/src/main/java/algorithms/patternFinding/KMP.java index 17f6567e..0bb5d30b 100644 --- a/src/main/java/algorithms/patternFinding/KMP.java +++ b/src/main/java/algorithms/patternFinding/KMP.java @@ -5,50 +5,51 @@ /** * Implementation of KMP. - * + *

* Illustration of getPrefixIndices: with pattern ABCABCNOABCABCA * Here we make a distinction between position and index. The position is basically 1-indexed. * Note the return indices are still 0-indexed of the pattern string. - * Position: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - * Pattern: A B C A B C N O A B C A B C A ... - * Return: -1 0 0 0 1 2 3 0 0 1 2 3 4 5 6 4 ... - * Read: ^ an indexing trick; consider 1-indexed characters for clarity and simplicity in the main algor - * Read: ^ 'A' is the first character of the pattern string, - * there is no prefix ending before its index, 0, that can be matched with. - * Read: ^ ^ 'B' and 'C' cannot be matched with any prefix which are just 'A' and 'AB' respectively. - * Read: ^ Can be matched with an earlier 'A'. So we store 1. - * Prefix is the substring from idx 0 to 1 (exclusive). Note consider prefix from 0-indexed. - * Realise 1 can also be interpreted as the index of the next character to match against! - * Read: ^ ^ Similarly, continue matching - * Read: ^ ^ No matches, so 0 - * Read: ^ ^ ^ ^ ^ ^ Match with prefix until position 6! - * Read: ^ where the magic happens, we can't match 'N' - * at position 7 with 'A' at position 15, but - * we know ABC of position 1-3 (or index 0-2) - * exists and can 'restart' from there. - * - * - * + * Position: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * Pattern: A B C A B C N O A B C A B C A ... + * Return: -1 0 0 0 1 2 3 0 0 1 2 3 4 5 6 4 ... + * Read: ^ an indexing trick; consider 1-indexed characters for clarity and simplicity in the main algor + * Read: ^ 'A' is the first character of the pattern string, + * there is no prefix ending before its index, 0, that can be matched with. + * Read: ^ ^ 'B' and 'C' cannot be matched with any prefix which are just 'A' and 'AB' respectively. + * Read: ^ Can be matched with an earlier 'A'. So we store 1. + * Prefix is the substring from idx 0 to 1 (exclusive). Note consider prefix from 0-indexed. + * Realise 1 can also be interpreted as the index of the next character to match against! + * Read: ^ ^ Similarly, continue matching + * Read: ^ ^ No matches, so 0 + * Read: ^ ^ ^ ^ ^ ^ Match with prefix until position 6! + * Read: ^ where the magic happens, we can't match 'N' + * at position 7 with 'A' at position 15, but + * we know ABC of position 1-3 (or index 0-2) + * exists and can 'restart' from there. + *

+ *

+ *

* Illustration of main logic: * Pattern: ABABAB * String : ABABCABABABAB - * - * A B A B C A B A B A B A B - * Read: ^ to ^ Continue matching where possible, leading to Pattern[0:4] matched. - * unable to match Pattern[4]. But notice that last two characters of String[0:4] - * form a sub-pattern with Pattern[0:2] Maybe Pattern[2] == 'C' and we can 're-use' Pattern[0:2] - * Read: ^ try ^ by checking if Pattern[2] == 'C' - * Read: Turns out no. No previously identified sub-pattern with 'C'. Restart matching Pattern. - * Read: ^ to ^ Found complete match! But rather than restart, notice that last 4 characters - * Read: form a prefix sub-pattern of Pattern, which is Pattern[0:4] = "ABAB", so, - * Read: ^ ^ Start matching from Pattern[4] and finally Pattern[5] + *

+ * A B A B C A B A B A B A B + * Read: ^ to ^ Continue matching where possible, leading to Pattern[0:4] matched. + * unable to match Pattern[4]. But notice that last two characters of String[0:4] + * form a sub-pattern with Pattern[0:2] Maybe Pattern[2] == 'C' and we can 're-use' Pattern[0:2] + * Read: ^ try ^ by checking if Pattern[2] == 'C' + * Read: Turns out no. No previously identified sub-pattern with 'C'. Restart matching Pattern. + * Read: ^ to ^ Found complete match! But rather than restart, notice that last 4 characters + * Read: form a prefix sub-pattern of Pattern, which is Pattern[0:4] = "ABAB", so, + * Read: ^ ^ Start matching from Pattern[4] and finally Pattern[5] */ public class KMP { /** * Find and indicate all suffix that match with a prefix. + * * @param pattern to search * @return an array of indices where the suffix ending at each position of they array can be matched with - * corresponding a prefix of the pattern ending before the specified index + * corresponding a prefix of the pattern ending before the specified index */ private static int[] getPrefixIndices(String pattern) { int len = pattern.length(); @@ -59,7 +60,7 @@ private static int[] getPrefixIndices(String pattern) { int currPrefixMatched = 0; // num of chars of prefix pattern currently matched int pos = 2; // Starting from the 2nd character, recall 1-indexed while (pos <= len) { - if (pattern.charAt(pos-1) == pattern.charAt(currPrefixMatched)) { + if (pattern.charAt(pos - 1) == pattern.charAt(currPrefixMatched)) { currPrefixMatched += 1; // note, the line below can also be interpreted as the index of the next char to match prefixIndices[pos] = currPrefixMatched; // an indexing trick, store at the pos, num of chars matched @@ -79,8 +80,9 @@ private static int[] getPrefixIndices(String pattern) { /** * Main logic of KMP. Iterate the sequence, looking for patterns. If a difference is found, resume matching from * a previously identified sub-pattern, if possible. Length of pattern should be at least one. + * * @param sequence to search against - * @param pattern to search for + * @param pattern to search for * @return start indices of all occurrences of pattern found */ public static List findOccurrences(String sequence, String pattern) { diff --git a/src/main/java/algorithms/patternFinding/README.md b/src/main/java/algorithms/patternFinding/README.md index 82af91c1..f81f1e76 100644 --- a/src/main/java/algorithms/patternFinding/README.md +++ b/src/main/java/algorithms/patternFinding/README.md @@ -10,10 +10,11 @@ It is hence crucial that we develop an efficient algorithm. Image Source: GeeksforGeeks ## Analysis + **Time complexity**: Naively, we can look for patterns in a given sequence in O(nk) where n is the length of the sequence and k -is the length of the pattern. We do this by iterating every character of the sequence, and look at the +is the length of the pattern. We do this by iterating every character of the sequence, and look at the immediate k-1 characters that come after it. This is not a big issue if k is known to be small, but there's no guarantee this is the case. @@ -24,6 +25,7 @@ O(n) traversal of the sequence. More details found in the src code. **Space complexity**: O(k) auxiliary space to store suffix that matches with prefix of the pattern string ## Notes -A detailed illustration of how the algorithm works is shown in the code. -But if you have trouble understanding the implementation, + +A detailed illustration of how the algorithm works is shown in the code. +But if you have trouble understanding the implementation, here is a good [video](https://www.youtube.com/watch?v=EL4ZbRF587g) as well. diff --git a/src/main/java/algorithms/sorting/bubbleSort/BubbleSort.java b/src/main/java/algorithms/sorting/bubbleSort/BubbleSort.java index 29b9f61d..63e45b09 100644 --- a/src/main/java/algorithms/sorting/bubbleSort/BubbleSort.java +++ b/src/main/java/algorithms/sorting/bubbleSort/BubbleSort.java @@ -1,24 +1,26 @@ package algorithms.sorting.bubbleSort; -/** Here, we are implementing BubbleSort where we sort the array in increasing (or more precisely, non-decreasing) +/** + * Here, we are implementing BubbleSort where we sort the array in increasing (or more precisely, non-decreasing) * order. - * + *

* Brief Description and Implementation Invariant: * BubbleSort relies on the outer loop variant that after the kth iteration, the biggest k items are correctly sorted * at the final k positions of the array. The job of the kth iteration of the outer loop is to bubble the kth * largest element to the kth position of the array from the right (i.e. its correct position). This is done through * repeatedly comparing adjacent elements and swapping them if they are in the wrong order. - * + *

* At the end of the (n-1)th iteration of the outer loop, where n is the length of the array, the largest (n-1) * elements are correctly sorted at the final (n-1) positions of the array, leaving the last 1 element placed correctly * in the first position of the array. Therefore, (n-1) iterations of the outer loop is sufficient. - * + *

* At the kth iteration of the outer loop, we only require (n-k) adjacent comparisons to get the kth largest * element to its correct position. */ public class BubbleSort { /** * Sorts the given array in-place in non-decreasing order. + * * @param arr array to be sorted. * @return the same array arr that is sorted. */ @@ -26,7 +28,7 @@ public static int[] sort(int[] arr) { int n = arr.length; boolean swapped; // tracks of the presence of swaps within one iteration of the outer loop to // facilitate early termination - for (int i = 0; i < n - 1; i++ ) { //outer loop which supports the invariant + for (int i = 0; i < n - 1; i++) { //outer loop which supports the invariant swapped = false; for (int j = 0; j < n - 1 - i; j++) { //inner loop that does the adjacent comparisons if (arr[j] > arr[j + 1]) { //if we changed this to <, we will sort the array in non-increasing order diff --git a/src/main/java/algorithms/sorting/bubbleSort/README.md b/src/main/java/algorithms/sorting/bubbleSort/README.md index d389afe0..c7dc4ffd 100644 --- a/src/main/java/algorithms/sorting/bubbleSort/README.md +++ b/src/main/java/algorithms/sorting/bubbleSort/README.md @@ -1,4 +1,5 @@ # Bubble Sort + Bubble sort is one of the more intuitive comparison-based sorting algorithms. It makes repeated comparisons between neighbouring elements, 'bubbling' (side-by-side swaps) largest (or smallest) element in the unsorted region to the sorted region (often the front or the back). @@ -6,10 +7,12 @@ largest (or smallest) element in the unsorted region to the sorted region (often ![bubble sort img](../../../../../../docs/assets/images/BubbleSort.jpeg) ## Complexity Analysis + **Time**: - - Worst case (reverse sorted array): O(n^2) - - Average case: O(n^2) - - Best case (sorted array): O(n) + +- Worst case (reverse sorted array): O(n^2) +- Average case: O(n^2) +- Best case (sorted array): O(n) In the worst case, during each iteration of the outer loop, the number of adjacent comparisons is upper-bounded by n. Since BubbleSort requires (n-1) iterations of the outer loop to sort the entire array, the total number diff --git a/src/main/java/algorithms/sorting/countingSort/CountingSort.java b/src/main/java/algorithms/sorting/countingSort/CountingSort.java index 80ec2b03..6c867136 100644 --- a/src/main/java/algorithms/sorting/countingSort/CountingSort.java +++ b/src/main/java/algorithms/sorting/countingSort/CountingSort.java @@ -8,6 +8,7 @@ public class CountingSort { /** * Sorts the given array. + * * @param arr array to be sorted. * @return new array that is sorted. */ @@ -19,21 +20,21 @@ public static int[] sort(int[] arr) { k = Math.max(k, arr[i]); } // Obtain frequency map - int[] freq = new int[k+1]; + int[] freq = new int[k + 1]; for (int num : arr) { freq[num]++; } // Obtain prefix sum of freq map - for (int i = 1; i < k+1; i++) { - freq[i] += freq[i-1]; + for (int i = 1; i < k + 1; i++) { + freq[i] += freq[i - 1]; } // Sort the array by placing element in the output array int[] sorted = new int[n]; - for (int i = arr.length-1; i >= 0; i--) { // we start from the back to maintain stable property + for (int i = arr.length - 1; i >= 0; i--) { // we start from the back to maintain stable property int num = arr[i]; - sorted[freq[num]-1] = num; // freq[num]-1 because 0-indexed + sorted[freq[num] - 1] = num; // freq[num]-1 because 0-indexed freq[num]--; } return sorted; } -} \ No newline at end of file +} diff --git a/src/main/java/algorithms/sorting/countingSort/README.md b/src/main/java/algorithms/sorting/countingSort/README.md index 48bfa547..0e8586c0 100644 --- a/src/main/java/algorithms/sorting/countingSort/README.md +++ b/src/main/java/algorithms/sorting/countingSort/README.md @@ -1,27 +1,31 @@ # Counting Sort ## Background -Counting sort is a non-comparison-based sorting algorithm and isn't bounded by the O(nlogn) lower-bound + +Counting sort is a non-comparison-based sorting algorithm and isn't bounded by the O(nlogn) lower-bound of most sorting algorithms.
It first obtains the frequency map of all elements (i.e. counting the occurrence of every element), then computes the prefix sum for the map. This prefix map tells us which position an element should be inserted. -It is updated after each insertion to reflection the new position to insert the next time the same element is +It is updated after each insertion to reflection the new position to insert the next time the same element is encountered.
### Implementation Invariant -At the end of the ith iteration, the ith element (of the original array) from the back will be placed in + +At the end of the ith iteration, the ith element (of the original array) from the back will be placed in its correct position. -Note: An alternative implementation from the front is easily done with minor modification. +Note: An alternative implementation from the front is easily done with minor modification. The catch is that this implementation is not stable. ### Common Misconception + _Counting sort does not require total ordering of elements since it is non-comparison based._ This is incorrect. It requires total ordering of elements to determine their relative positions in the sorted output. In our implementation, the total ordering property is reflected by virtue of the structure of the frequency map. ## Complexity Analysis + **Time**: O(k+n)=O(max(k,n))
**Space**: O(k+n)=O(max(k,n))
where k is the value of the largest element and n is the number of elements. @@ -30,5 +34,6 @@ Counting sort is most efficient if the range of input values do not exceed the n Counting sort is NOT AN IN-PLACE algorithm. For one, it requires additional space to store freq map.
## Notes + 1. Counting sort (stable version) is often used as a sub-routine for radix sort. 2. Supplementary: Here is a [video](https://www.youtube.com/watch?v=OKd534EWcdk) if you are still having troubles. diff --git a/src/main/java/algorithms/sorting/cyclicSort/README.md b/src/main/java/algorithms/sorting/cyclicSort/README.md index c9e5e3d7..931d30a9 100644 --- a/src/main/java/algorithms/sorting/cyclicSort/README.md +++ b/src/main/java/algorithms/sorting/cyclicSort/README.md @@ -1,16 +1,19 @@ # Cyclic Sort ## Background -Cyclic sort is a comparison-based, in-place algorithm that performs sorting (generally) in O(n^2) time. + +Cyclic sort is a comparison-based, in-place algorithm that performs sorting (generally) in O(n^2) time. Though under some conditions (discussed later), the best case could be done in O(n) time. ### Implementation Invariant -At the end of the ith iteration, the ith element + +At the end of the ith iteration, the ith element (of the original array, from either the back or front depending on implementation), is correctly positioned. ### Comparison to Selection Sort + Its invariant is quite similar as selection sort's. But they differ slightly in maintaining this invariant. -The algorithm for cyclic sort does a bit more than selection sort's. +The algorithm for cyclic sort does a bit more than selection sort's. In the process of trying to find the correct element for the ith position, any element that was encountered will be correctly placed in their positions as well. @@ -18,10 +21,12 @@ This allows cyclic sort to have a time complexity of O(n) for certain inputs. (where the relative ordering of the elements is already known). This is discussed in the [**simple**](./simple) case. ## Simple and Generalised Case + We discuss more implementation-specific details and complexity analysis in the respective folders. In short, -1. The [**simple**](./simple) case discusses the non-comparison based implementation of cyclic sort under -certain conditions. This allows the best case to be better than O(n^2). -2. The [**generalised**](./generalised) case discusses cyclic sort for general inputs. This is comparison-based and is -usually implemented in O(n^2). + +1. The [**simple**](./simple) case discusses the non-comparison based implementation of cyclic sort under + certain conditions. This allows the best case to be better than O(n^2). +2. The [**generalised**](./generalised) case discusses cyclic sort for general inputs. This is comparison-based and is + usually implemented in O(n^2). diff --git a/src/main/java/algorithms/sorting/cyclicSort/generalised/CyclicSort.java b/src/main/java/algorithms/sorting/cyclicSort/generalised/CyclicSort.java index 34e186b1..d04880b9 100644 --- a/src/main/java/algorithms/sorting/cyclicSort/generalised/CyclicSort.java +++ b/src/main/java/algorithms/sorting/cyclicSort/generalised/CyclicSort.java @@ -3,17 +3,30 @@ * Implementation of cyclic sort in the generalised case where the input can contain any integer and duplicates. */ public class CyclicSort { + /** + * Sorts the given array using CyclicSort. Generalised case where the input array can contain any integer, and + * can contain duplicates. + * See documentation in README for more details. + * + * @param arr the given array to be sorted. Can contain any integers and can contain duplicates. + * @param n the size of the given array to be sorted. + */ public static void cyclicSort(int[] arr, int n) { - for (int currIdx = 0; currIdx < n-1; currIdx++) { - int currElement = arr[currIdx]; + for (int currIdx = 0; currIdx < n - 1; currIdx++) { + int currElement; int rightfulPos; + do { rightfulPos = currIdx; // initialization since elements before currIdx are correctly placed + currElement = arr[currIdx]; for (int i = currIdx + 1; i < n; i++) { // O(n) find rightfulPos for the currElement if (arr[i] < currElement) { rightfulPos++; } } + if (rightfulPos == currIdx) { // verified curr position is correct for curr element + break; + } while (currElement == arr[rightfulPos]) { // duplicates found, so find next suitable position rightfulPos++; } @@ -24,8 +37,9 @@ public static void cyclicSort(int[] arr, int n) { } } - /* - Overloaded helper method that calls internal implementation. + /** + * Overloaded helper method that calls internal implementation. + * @param arr the input array to be sorted. */ public static void sort(int[] arr) { cyclicSort(arr, arr.length); diff --git a/src/main/java/algorithms/sorting/cyclicSort/generalised/README.md b/src/main/java/algorithms/sorting/cyclicSort/generalised/README.md index aebf94e4..b2099c28 100644 --- a/src/main/java/algorithms/sorting/cyclicSort/generalised/README.md +++ b/src/main/java/algorithms/sorting/cyclicSort/generalised/README.md @@ -1,35 +1,38 @@ # Generalized Case ## More Details + Implementation of cyclic sort in the generalised case where the input can contain any integer and duplicates. -This is a comparison-based algorithm. In short, for the element encountered at the ith position of the original array, -the algorithm does a O(n) traversal to search for its rightful position and does a swap. It repeats this until a swap +This is a comparison-based algorithm. In short, for the element encountered at the ith position of the original array, +the algorithm does a O(n) traversal to search for its rightful position and does a swap. It repeats this until a swap placing the correct element at the ith position, before moving onto the next (i+1)th. ### Illustration -Result: 6 10 6 5 7 4 2 1
  - Read: ^ 6 should be placed at index 4, so we do a swap with 6 and 7,

-Result: 7 10 6 5 6 4 2 1
  - Read: ^ 7 should be placed at index 6, so we do a swap. Note that index 5 should be taken up by dup 6

-Result: 2 10 6 5 6 4 7 1
  - Read: ^ 2 should be placed at index 1, so swap.

-Result: 10 2 6 5 6 4 7 1
  - Read: ^ 10 is the largest, should be placed at last index, so swap.

-Result: 1 2 6 5 6 4 7 10
  - Read: ^ Correctly placed, so move on. Same for 2.
  - Read:       - ^ 6 should be placed at index 4. But index 4 already has a 6. So place at index 5 and so on.

-Result: 1 2 4 5 6 6 7 10
  - Read:          - ^ ^ ^ ^ ^ Continue with O(n) verification of correct position at each iteration +Result: 6 10 6 5 7 4 2 1
  +Read: ^ 6 should be placed at index 4, so we do a swap with 6 and 7,

+Result: 7 10 6 5 6 4 2 1
  +Read: ^ 7 should be placed at index 6, so we do a swap. Note that index 5 should be taken up by dup 6

+Result: 2 10 6 5 6 4 7 1
  +Read: ^ 2 should be placed at index 1, so swap.

+Result: 10 2 6 5 6 4 7 1
  +Read: ^ 10 is the largest, should be placed at last index, so swap.

+Result: 1 2 6 5 6 4 7 10
  +Read: ^ Correctly placed, so move on. Same for 2.
  +Read:       +^ 6 should be placed at index 4. But index 4 already has a 6. So place at index 5 and so on.

+Result: 1 2 4 5 6 6 7 10
  +Read:          +^ ^ ^ ^ ^ Continue with O(n) verification of correct position at each iteration ## Complexity Analysis + **Time**: + - Best: O(n^2) even if the ith element is encountered in the ith position, a O(n) traversal validation check is needed -- Worst: O(n^2) since we need O(n) time to find / validate the correct position of an element and -the total number of O(n) traversals is bounded by O(n). +- Worst: O(n^2) since we need O(n) time to find / validate the correct position of an element and + the total number of O(n) traversals is bounded by O(n). - Average: O(n^2), it's bounded by the above two **Space**: O(1) auxiliary space, this is an in-place algorithm diff --git a/src/main/java/algorithms/sorting/cyclicSort/simple/CyclicSort.java b/src/main/java/algorithms/sorting/cyclicSort/simple/CyclicSort.java index 09965619..12565835 100644 --- a/src/main/java/algorithms/sorting/cyclicSort/simple/CyclicSort.java +++ b/src/main/java/algorithms/sorting/cyclicSort/simple/CyclicSort.java @@ -4,9 +4,10 @@ * Implementation of cyclic sort in the simple case where the n elements of the given array are contiguous, * but not in sorted order. Below illustrates the idea using integers from 0 to n-1. */ - public class CyclicSort { +public class CyclicSort { /** * Sorts the given array. + * * @param arr the array to be sorted. */ public static void sort(int[] arr) { @@ -23,4 +24,4 @@ public static void sort(int[] arr) { } } } - } +} diff --git a/src/main/java/algorithms/sorting/cyclicSort/simple/FindFirstMissingNonNegative.java b/src/main/java/algorithms/sorting/cyclicSort/simple/FindFirstMissingNonNegative.java index 12a03a39..cc2cd069 100644 --- a/src/main/java/algorithms/sorting/cyclicSort/simple/FindFirstMissingNonNegative.java +++ b/src/main/java/algorithms/sorting/cyclicSort/simple/FindFirstMissingNonNegative.java @@ -5,6 +5,12 @@ * in O(n). */ public class FindFirstMissingNonNegative { + /** + * Finds the first missing non-negative integer in the array. + * + * @param arr the given array. + * @return the first missing, non-negative integer. + */ public static int findMissing(int[] arr) { int curr = 0; while (curr < arr.length) { diff --git a/src/main/java/algorithms/sorting/cyclicSort/simple/README.md b/src/main/java/algorithms/sorting/cyclicSort/simple/README.md index 0463d1f9..910b9500 100644 --- a/src/main/java/algorithms/sorting/cyclicSort/simple/README.md +++ b/src/main/java/algorithms/sorting/cyclicSort/simple/README.md @@ -1,44 +1,50 @@ # Simple Case ## More Details + Cyclic Sort can achieve O(n) time complexity in cases where the elements of the collection have a known, continuous range, and there exists a direct, O(1) time-complexity mapping from each element to its respective index in -the sorted order. +the sorted order. This is typically applicable when sorting a sequence of integers that are in a consecutive range or can be easily mapped to such a range. We illustrate the idea with n integers from 0 to n-1. -In this implementation, the algorithm is **not comparison-based**! (unlike the general case). +In this implementation, the algorithm is **not comparison-based**! (unlike the general case). It makes use of the known inherent ordering of the numbers, bypassing the nlogn lower bound for most sorting algorithms. ## Complexity Analysis + **Time**: + - Best: O(n) since the array has to be traversed - Worst: O(n) since each element is at most seen twice - Average: O(n), it's bounded by the above two -**Space**: O(1) auxiliary space, this is an in-place algorithm +**Space**: O(1) auxiliary space, this is an in-place algorithm ## Case Study: Find First Missing Non-negative Integer + Cyclic sort algorithm can be easily modified to find first missing non-negative integer (i.e. starting from 0) in O(n). - The invariant is the same, but for numbers that are out-of-range (negative or greater than n), +The invariant is the same, but for numbers that are out-of-range (negative or greater than n), simply ignore the number at the position and move on first. It may be subject to swap later. There are other ways of doing so, using a hash set for instance, but what makes cyclic sort stand out is that it is able to do so in O(1) space. In other words, it is in-place and require no additional space. -The algorithm does a 2-pass iteration. -1. In the 1st iteration, it places elements at its rightful position where possible. +The algorithm does a 2-pass iteration. + +1. In the 1st iteration, it places elements at its rightful position where possible. 2. In the 2nd iteration, it will look for the first out of place element (element that is not supposed -to be in that position). The answer will be the index of that position. + to be in that position). The answer will be the index of that position. Note that the answer is necessarily between 0 and n (inclusive), where n is the length of the array, otherwise there would be a contradiction. ## Misc -1. It may seem quite trivial to sort integers from 0 to n-1 when one could simply generate such a sequence. -But this algorithm is useful in cases where the integers to be sorted are keys to associated values (or some mapping) -and sorting needs to be done in O(1) auxiliary space. + +1. It may seem quite trivial to sort integers from 0 to n-1 when one could simply generate such a sequence. + But this algorithm is useful in cases where the integers to be sorted are keys to associated values (or some mapping) + and sorting needs to be done in O(1) auxiliary space. 2. The implementation here uses integers from 0 to n-1. This can be easily modified for n contiguous integers starting -at some arbitrary number (simply offset by this start number). + at some arbitrary number (simply offset by this start number). diff --git a/src/main/java/algorithms/sorting/insertionSort/InsertionSort.java b/src/main/java/algorithms/sorting/insertionSort/InsertionSort.java index f1916ca1..28aa6ebe 100644 --- a/src/main/java/algorithms/sorting/insertionSort/InsertionSort.java +++ b/src/main/java/algorithms/sorting/insertionSort/InsertionSort.java @@ -1,20 +1,22 @@ package algorithms.sorting.insertionSort; -/** Here, we are implementing InsertionSort where we sort the array in increasing (or more precisely, non-decreasing) +/** + * Here, we are implementing InsertionSort where we sort the array in increasing (or more precisely, non-decreasing) * order. - * + *

* Implementation Invariant: * The loop invariant is: at the end of kth iteration, the first (k+1) items in the array are in sorted order. * At the end of the (n-1)th iteration, all n items in the array will be in sorted order. - * + *

* Note: - * 1. the loop invariant here slightly differs from the lecture slides as we are using 0-based indexing - * 2. Insertion into the sorted portion is done byb 'bubbling' elements as in bubble sort + * 1. the loop invariant here slightly differs from the lecture slides as we are using 0-based indexing + * 2. Insertion into the sorted portion is done byb 'bubbling' elements as in bubble sort */ public class InsertionSort { /** * Sorts the given array in-place in non-decreasing order. + * * @param arr array to be sorted. * @return the same array arr that is sorted. */ @@ -26,7 +28,8 @@ public static int[] sort(int[] arr) { return arr; } - /** Inserts val within the sorted portion of the array. The sorted portion of the array is arr[0, idx - 1]. + /** + * Inserts val within the sorted portion of the array. The sorted portion of the array is arr[0, idx - 1]. * * @param arr the array to be sorted (of length idx) * @param idx index of the element to be inserted into the sorted portion of the array diff --git a/src/main/java/algorithms/sorting/insertionSort/README.md b/src/main/java/algorithms/sorting/insertionSort/README.md index c7a07c2b..c0e5ee6e 100644 --- a/src/main/java/algorithms/sorting/insertionSort/README.md +++ b/src/main/java/algorithms/sorting/insertionSort/README.md @@ -1,9 +1,9 @@ # Insertion Sort Insertion sort is a comparison-based sorting algorithm that builds the final sorted array one element at a -time. It works by repeatedly taking an element from the unsorted portion of the array and -inserting it correctly (portion remains sorted) into the sorted portion. Note that the position is not final -since subsequent elements from unsorted portion may displace previously inserted elements. What's important is +time. It works by repeatedly taking an element from the unsorted portion of the array and +inserting it correctly (portion remains sorted) into the sorted portion. Note that the position is not final +since subsequent elements from unsorted portion may displace previously inserted elements. What's important is the sorted region remains sorted. More succinctly:
At the kth iteration, we take the element arr[k] and insert it into arr[0, k-1] following sorted order, returning us arr[0, k] in sorted order. @@ -11,10 +11,12 @@ it into arr[0, k-1] following sorted order, returning us arr[0, k] in sorted ord ![InsertionSort](../../../../../../docs/assets/images/InsertionSort.png) ## Complexity Analysis + **Time**: - - Worst case (reverse sorted array): O(n^2) - - Average case: O(n^2) - - Best case (sorted array): O(n) + +- Worst case (reverse sorted array): O(n^2) +- Average case: O(n^2) +- Best case (sorted array): O(n) In the worst case, inserting an element into the sorted array of length m requires us to iterate through the entire array, requiring O(m) time. Since InsertionSort does this insertion (n - 1) times, the time complexity @@ -27,10 +29,12 @@ does this insertion (n-1) times, the time complexity of InsertionSort in the bes **Space**: O(1) since sorting is done in-place ## Notes + ### Common Misconception -Its invariant is often confused with selection sort's. In selection sort, an element in the unsorted region will + +Its invariant is often confused with selection sort's. In selection sort, an element in the unsorted region will be immediately placed in its correct and final position as it would be in the sorted array. This is not the case for insertion sort. However, it is because of this 'looser' invariant that allows for a better best case time complexity for insertion sort. -Image Source: https://www.hackerrank.com/challenges/correctness-invariant/problem \ No newline at end of file +Image Source: https://www.hackerrank.com/challenges/correctness-invariant/problem diff --git a/src/main/java/algorithms/sorting/mergeSort/iterative/MergeSort.java b/src/main/java/algorithms/sorting/mergeSort/iterative/MergeSort.java index 5b3b34c1..c7ca59ce 100644 --- a/src/main/java/algorithms/sorting/mergeSort/iterative/MergeSort.java +++ b/src/main/java/algorithms/sorting/mergeSort/iterative/MergeSort.java @@ -1,38 +1,39 @@ package algorithms.sorting.mergeSort.iterative; -/** Here, we are implementing MergeSort where we sort the array in increasing (or more precisely, non-decreasing) +/** + * Here, we are implementing MergeSort where we sort the array in increasing (or more precisely, non-decreasing) * order iteratively. - * + *

* Brief Description: * The iterative implementation of MergeSort takes a bottom-up approach, where the sorting process starts by merging * intervals of size 1. Intervals of size 1 are trivially in sorted order. The algorithm then proceeds to merge * adjacent sorted intervals, doubling the interval size with each merge step, until the entire array is fully sorted. - * + *

* Implementation Invariant: * At each iteration of the merging process, the main array is divided into sub-arrays of a certain interval - * size (). Each of these sub-arrays is sorted within itself. The last sub-array may not be of size - * but it is still sorted since its size is necessarily less than . - * + * size (interval). Each of these sub-arrays is sorted within itself. The last sub-array may not be of size + * interval, but it is still sorted since its size is necessarily less than interval. + *

* Complexity Analysis: * Time: * - Worst case: O(nlogn) * - Average case: O(nlogn) * - Best case: O(nlogn) - * + *

* Given two sorted arrays of size p and q, we need O(p + q) time to merge the two arrays into one sorted array, since * we have to iterate through every element in both arrays once. - * + *

* At the 1st level of the merge process, our merging subroutine involves n sub-arrays of size 1, taking O(n) time. * At the 2nd level of the merge process, our merging subroutine involves (n/2) sub-arrays of size 2, taking O(n) time. * At the kth level of the merge process, our merging subroutine involves (n/(2^(k-1))) sub-arrays of size (2^(k-1)), * taking O(n) time. - * - * Since doubles at every iteration of the merge process, there are logn such levels. Every level takes + *

+ * Since interval doubles at every iteration of the merge process, there are logn such levels. Every level takes * O(n) time, hence overall time complexity is n * logn = O(nlogn) - * + *

* Regardless of how sorted the input array is, MergeSort carries out the partitioning and merging process, so the * time complexity of MergeSort is O(nlogn) for all cases. - * + *

* Space: * - O(n) since we require a temporary array to temporarily store the merged elements in sorted order */ @@ -63,13 +64,13 @@ public static void sort(int[] arr) { * Merges two sorted sub-arrays within the given array. The two sub-arrays are arr[start, mid] and * arr[mid + 1, end]. Upon completion of this function, arr[start, end] will be in sorted order. * - * @param arr The array containing the sub-arrays to be merged. + * @param arr The array containing the sub-arrays to be merged. * @param start The starting index of the first sub-array to be merged. - * @param mid The ending index (inclusive) of the first sub-array to be merged. In the iterative implementation, - * the mid parameter is required in the merge function to determine the splitting point between the - * sub-arrays to be merged. - * @param end The ending index (inclusive) of the second sub-array to be merged. - * @param temp A temporary array used for merging intermediate results. + * @param mid The ending index (inclusive) of the first sub-array to be merged. In the iterative implementation, + * the mid parameter is required in the merge function to determine the splitting point between the + * sub-arrays to be merged. + * @param end The ending index (inclusive) of the second sub-array to be merged. + * @param temp A temporary array used for merging intermediate results. */ private static void merge(int[] arr, int start, int mid, int end, int[] temp) { int i = start; @@ -108,8 +109,8 @@ private static void merge(int[] arr, int start, int mid, int end, int[] temp) { } // Copy the merged elements back to the original array - for (int k = start; k <= end; k++) { - arr[k] = temp[k]; + if (end + 1 - start >= 0) { + System.arraycopy(temp, start, arr, start, end + 1 - start); } } diff --git a/src/main/java/algorithms/sorting/mergeSort/legacy/iterative/MergeSort.java b/src/main/java/algorithms/sorting/mergeSort/legacy/iterative/MergeSort.java index 3beee70c..927b6444 100644 --- a/src/main/java/algorithms/sorting/mergeSort/legacy/iterative/MergeSort.java +++ b/src/main/java/algorithms/sorting/mergeSort/legacy/iterative/MergeSort.java @@ -1,91 +1,94 @@ package algorithms.sorting.mergeSort.legacy.iterative; -import java.util.*; +import java.util.ArrayList; +import java.util.List; /** * Iterative implementation of Merge sort with O(n) space */ public class MergeSort { - /** - * Sorts a list from a specified start to end point. - * - * Note: starting with an interval size (call this var ) of 1 and iteratively *2, - * Merge two sub-lists of size together using merge routine. - * Invariant: Partitioning the main list into sub-lists of size , - * each of this sub-list is sorted within itself (the last sub-list may not be of - * size but it is still sorted since the size is necessarily less than . - * - * @param generic type of object - * @param lst list of objects to be sorted - * @param start sorting starts at this index - * @param end sorting ends (inclusive) at this index - */ - private static > void mergeSort(List lst, int start, int end) { - if (start == end) { - return; - } - int size = lst.size(); - int interval = 1; - List tmp = new ArrayList<>(); - for (T item : lst) { - tmp.add(item); - } - while (interval < lst.size()) { - for (int i = 0; i + interval < size; i += 2*interval) { - int start1 = i; - int start2 = i + interval; - int e = i + 2 * interval - 1; - if (e > size - 1) { - e = size - 1; + /** + * Sorts a list from a specified start to end point. + *

+ * Note: starting with an interval size (call this var interval) of 1 and iteratively *2, + * Merge two sub-lists of size interval together using merge routine. + * Invariant: Partitioning the main list into sub-lists of size interval, + * each of this sub-list is sorted within itself (the last sub-list may not be of + * size interval, but it is still sorted since the size is necessarily less than interval). + * + * @param generic type of object + * @param lst list of objects to be sorted + * @param start sorting starts at this index + * @param end sorting ends (inclusive) at this index + */ + private static > void mergeSort(List lst, int start, int end) { + if (start == end) { + return; + } + int size = lst.size(); + int interval = 1; + List tmp = new ArrayList<>(); + for (T item : lst) { + tmp.add(item); + } + while (interval < lst.size()) { + for (int i = 0; i + interval < size; i += 2 * interval) { + int start1 = i; + int start2 = i + interval; + int e = i + 2 * interval - 1; + if (e > size - 1) { + e = size - 1; + } + merge(lst, tmp, start1, start2, e); + } + interval *= 2; } - merge(lst, tmp, start1, start2, e); - } - interval *= 2; - } - - } - /** - * Merging algorithm that merges two sorted sub-lists into one final sorted list. - * @param generic type of object - * @param lst at the end, elements from s1 to e (inclusive) of lst are sorted - * @param s1 start index of first sub-list - * @param s2 start index of second sub-list; note that end index of first sub-list is s2-1 - * @param e end index of second sub-list - */ - private static > void merge(List lst, List tmp, int s1, int s2, int e) { - int startLeft = s1; - int startRight = s2; - int tmpIdx = s1; - while (startLeft < s2 && startRight < e + 1) { - if (lst.get(startLeft).compareTo(lst.get(startRight)) < 0) { - tmp.set(tmpIdx, lst.get(startLeft++)); - } else { - tmp.set(tmpIdx, lst.get(startRight++)); - } - tmpIdx++; } - while (startLeft < s2) { - tmp.set(tmpIdx++, lst.get(startLeft++)); - } + /** + * Merging algorithm that merges two sorted sub-lists into one final sorted list. + * + * @param generic type of object + * @param lst at the end, elements from s1 to e (inclusive) of lst are sorted + * @param s1 start index of first sub-list + * @param s2 start index of second sub-list; note that end index of first sub-list is s2-1 + * @param e end index of second sub-list + */ + private static > void merge(List lst, List tmp, int s1, int s2, int e) { + int startLeft = s1; + int startRight = s2; + int tmpIdx = s1; + while (startLeft < s2 && startRight < e + 1) { + if (lst.get(startLeft).compareTo(lst.get(startRight)) < 0) { + tmp.set(tmpIdx, lst.get(startLeft++)); + } else { + tmp.set(tmpIdx, lst.get(startRight++)); + } + tmpIdx++; + } - while (startRight < e + 1) { - tmp.set(tmpIdx++, lst.get(startRight++)); - } + while (startLeft < s2) { + tmp.set(tmpIdx++, lst.get(startLeft++)); + } + + while (startRight < e + 1) { + tmp.set(tmpIdx++, lst.get(startRight++)); + } - for (int i = s1; i < e+1; i++) { - lst.set(i, tmp.get(i)); + for (int i = s1; i < e + 1; i++) { + lst.set(i, tmp.get(i)); + } } - } - /** - * Sorting algorithm that clients calls - * @param generic type - * @param lst list to be sorted - */ - public static > void sort(List lst) { - mergeSort(lst, 0, lst.size() - 1); - } + /** + * Sorting algorithm that clients calls + * + * @param generic type + * @param lst list to be sorted + */ + public static > void sort(List lst) { + mergeSort(lst, 0, lst.size() - 1); + } } diff --git a/src/main/java/algorithms/sorting/mergeSort/legacy/recursive/MergeSort.java b/src/main/java/algorithms/sorting/mergeSort/legacy/recursive/MergeSort.java index daf765c0..dee1422d 100644 --- a/src/main/java/algorithms/sorting/mergeSort/legacy/recursive/MergeSort.java +++ b/src/main/java/algorithms/sorting/mergeSort/legacy/recursive/MergeSort.java @@ -1,73 +1,77 @@ package algorithms.sorting.mergeSort.legacy.recursive; -import java.util.*; +import java.util.ArrayList; +import java.util.List; /** - * Recursive implementation of merge sort (in ascending order) + * Recursive implementation of merge sort (in ascending order) * with only additional O(n) space required. */ public class MergeSort { - /** - * Sorts a list from a specified start to end point, making use of - * and additional copy list to store sorted elements. - * @param generic type of object - * @param lst list of objects to be sorted from start to end - * @param start sorting of elements starting from this index - * @param end sorting of elements up till and inclusive of this index - * @param copy copy of the list to store and extract elements in sorted position - */ - private static > void mergeSort(List lst, int start, int end, List copy) { - if (start == end) { - return; + /** + * Sorts a list from a specified start to end point, making use of + * and additional copy list to store sorted elements. + * + * @param generic type of object + * @param lst list of objects to be sorted from start to end + * @param start sorting of elements starting from this index + * @param end sorting of elements up till and inclusive of this index + * @param copy copy of the list to store and extract elements in sorted position + */ + private static > void mergeSort(List lst, int start, int end, List copy) { + if (start == end) { + return; + } + int mid = start + (end - start) / 2; + mergeSort(copy, start, mid, lst); + mergeSort(copy, mid + 1, end, lst); + merge(lst, start, mid, end, copy); } - int mid = start + (end - start) / 2; - mergeSort(copy, start, mid, lst); - mergeSort(copy, mid + 1, end, lst); - merge(lst, start, mid, end, copy); - } - /** - * Merging algorithm that merges two sorted sub-lists into one final sorted list. - * @param generic type of object - * @param arr at the end, elements from s to e (inclusive) of arr are sorted - * @param s start of first sub-list - * @param m end (inclusive) of first sub-list; note start of second sub-list is m+1 - * @param e end (inclusive) of second sub-list - * @param cpy copy of the sorted sub-lists to extract and insert sorted final list into arr - */ - private static > void merge(List arr, int s, int m, int e, List cpy) { - int start = s; - int startLeft = s; - int startRight = m + 1; + /** + * Merging algorithm that merges two sorted sub-lists into one final sorted list. + * + * @param generic type of object + * @param arr at the end, elements from s to e (inclusive) of arr are sorted + * @param s start of first sub-list + * @param m end (inclusive) of first sub-list; note start of second sub-list is m+1 + * @param e end (inclusive) of second sub-list + * @param cpy copy of the sorted sub-lists to extract and insert sorted final list into arr + */ + private static > void merge(List arr, int s, int m, int e, List cpy) { + int start = s; + int startLeft = s; + int startRight = m + 1; - while (startLeft < m + 1 && startRight < e + 1) { - if (cpy.get(startLeft).compareTo(cpy.get(startRight)) < 0) { - arr.set(start++, cpy.get(startLeft++)); - } else { - arr.set(start++, cpy.get(startRight++)); - } - } + while (startLeft < m + 1 && startRight < e + 1) { + if (cpy.get(startLeft).compareTo(cpy.get(startRight)) < 0) { + arr.set(start++, cpy.get(startLeft++)); + } else { + arr.set(start++, cpy.get(startRight++)); + } + } - while (startLeft < m + 1) { - arr.set(start++, cpy.get(startLeft++)); - } + while (startLeft < m + 1) { + arr.set(start++, cpy.get(startLeft++)); + } - while (startRight < e + 1) { - arr.set(start++, cpy.get(startRight++)); + while (startRight < e + 1) { + arr.set(start++, cpy.get(startRight++)); + } } - } - /** - * Sorting algorithm to be called by client. - * @param generic type - * @param lst list of elements to be sorted. - */ - public static > void sort(List lst) { - List copy = new ArrayList<>(); - for (int i = 0; i < lst.size(); i++) { - copy.add(lst.get(i)); + /** + * Sorting algorithm to be called by client. + * + * @param generic type + * @param lst list of elements to be sorted. + */ + public static > void sort(List lst) { + List copy = new ArrayList<>(); + for (int i = 0; i < lst.size(); i++) { + copy.add(lst.get(i)); + } + mergeSort(lst, 0, lst.size() - 1, copy); } - mergeSort(lst, 0, lst.size() - 1, copy); - } } diff --git a/src/main/java/algorithms/sorting/mergeSort/recursive/MergeSort.java b/src/main/java/algorithms/sorting/mergeSort/recursive/MergeSort.java index 95893ca5..86df4db5 100644 --- a/src/main/java/algorithms/sorting/mergeSort/recursive/MergeSort.java +++ b/src/main/java/algorithms/sorting/mergeSort/recursive/MergeSort.java @@ -1,17 +1,18 @@ package algorithms.sorting.mergeSort.recursive; -/** Here, we are implementing MergeSort where we sort the array in increasing (or more precisely, non-decreasing) +/** + * Here, we are implementing MergeSort where we sort the array in increasing (or more precisely, non-decreasing) * order recursively. - * + *

* Brief Description: * MergeSort is a divide-and-conquer sorting algorithm. The recursive implementation takes a top-down approach by * recursively dividing the array into two halves, sorting each half separately, and then merging the sorted halves * to produce the final sorted output. - * + *

* Implementation Invariant (for the merging subroutine): * The sub-array temp[start, (k-1)] consists of the (𝑘−start) smallest elements of arr[start, mid] and * arr[mid + 1, end], in sorted order. - * + *

* Complexity Analysis: * Time: * - Worst case: O(nlogn) @@ -19,12 +20,12 @@ * - Best case: O(nlogn) * Merging two sorted sub-arrays of size (n/2) requires O(n) time as we need to iterate through every element in both * sub-arrays in order to merge the two sorted sub-arrays into one sorted array. - * + *

* Recursion expression: T(n) = 2T(n/2) + O(n) => O(nlogn) - * + *

* Regardless of how sorted the input array is, MergeSort carries out the same divide-and-conquer strategy, so the * time complexity of MergeSort is O(nlogn) for all cases. - * + *

* Space: * - O(n) since we require a temporary array to temporarily store the merged elements in sorted order */ @@ -34,10 +35,10 @@ public class MergeSort { /** * Sorts the sub-array arr[start, end] using the MergeSort algorithm. * - * @param arr The given array to be sorted. + * @param arr The given array to be sorted. * @param start The starting index of the sub-array to be sorted. - * @param end The ending index (inclusive) of the sub-array to be sorted. - * @param temp A temporary array used for merging. + * @param end The ending index (inclusive) of the sub-array to be sorted. + * @param temp A temporary array used for merging. */ private static void mergeSort(int[] arr, int start, int end, int[] temp) { if (start >= end) { @@ -53,10 +54,10 @@ private static void mergeSort(int[] arr, int start, int end, int[] temp) { * Merges two sorted sub-arrays within the given array. The two sub-arrays are arr[start, mid] and * arr[mid + 1, end]. Upon completion of this function, arr[start, end] will be in sorted order. * - * @param arr The array containing the sub-arrays to be merged. + * @param arr The array containing the sub-arrays to be merged. * @param start The starting index of the first sub-array to be merged. - * @param end The ending index (inclusive) of the second sub-array to be merged. - * @param temp A temporary array used for merging intermediate results. + * @param end The ending index (inclusive) of the second sub-array to be merged. + * @param temp A temporary array used for merging intermediate results. */ private static void merge(int[] arr, int start, int end, int[] temp) { int mid = (start + end) / 2; @@ -96,8 +97,8 @@ private static void merge(int[] arr, int start, int end, int[] temp) { } // Copy the merged elements back to the original array - for (int k = start; k <= end; k++) { - arr[k] = temp[k]; + if (end + 1 - start >= 0) { + System.arraycopy(temp, start, arr, start, end + 1 - start); } } @@ -109,7 +110,7 @@ private static void merge(int[] arr, int start, int end, int[] temp) { public static void sort(int[] arr) { int n = arr.length; int[] temp = new int[n]; - mergeSort(arr, 0, n-1, temp); + mergeSort(arr, 0, n - 1, temp); } } diff --git a/src/main/java/algorithms/sorting/quickSort/README.md b/src/main/java/algorithms/sorting/quickSort/README.md index 84935c96..42533698 100644 --- a/src/main/java/algorithms/sorting/quickSort/README.md +++ b/src/main/java/algorithms/sorting/quickSort/README.md @@ -1,22 +1,22 @@ # QuickSort This page will provide a high-level overview of the different types of QuickSort and the significance of each -enhancement. +enhancement. ![QuickSort Overview](../../../../../../docs/assets/images/QuickSortOverview.jpeg) -We start off with Simple QuickSort, which utilises Hoare's / Lomuto's partitioning with a fixed pivot. But the time -complexity of Simple QuickSort degrades to O(n^2) when pivots chosen are continuously bad. This could be because of -inputs such as sorted arrays, where picking the first element as the pivot will always lead to an extremely imbalanced -partitioning. This could also be because of the presence of duplicates, as we now handle duplicates by putting them to -one side of the pivot. +We start off with Simple QuickSort, which utilises Hoare's / Lomuto's partitioning with a fixed pivot. But the time +complexity of Simple QuickSort degrades to O(n^2) when pivots chosen are continuously bad. This could be because of +inputs such as sorted arrays, where picking the first element as the pivot will always lead to an extremely imbalanced +partitioning. This could also be because of the presence of duplicates, as we now handle duplicates by putting them to +one side of the pivot. -Therefore, we introduced randomisation in pivot selection to prevent fixed pivot selection from continuously choosing -a bad pivot. We also added a good pivot check (Paranoid) to Simple QuickSort to ensure that the pivots we select are -"good" and will not cause extremely imbalanced partitions. However, the presence of the good pivot checks will cause +Therefore, we introduced randomisation in pivot selection to prevent fixed pivot selection from continuously choosing +a bad pivot. We also added a good pivot check (Paranoid) to Simple QuickSort to ensure that the pivots we select are +"good" and will not cause extremely imbalanced partitions. However, the presence of the good pivot checks will cause infinite recursion when the array contains many duplicates, since any pivot you choose in the array will fail the good -pivot check. +pivot check. -So, we introduced 3-way partitioning to handle duplicates by partitioning the array into three sections: elements less -than the pivot, elements equal to the pivot, and elements greater than the pivot. Now the good pivot check ignores the -size of the segment that comprises elements = to pivot. \ No newline at end of file +So, we introduced 3-way partitioning to handle duplicates by partitioning the array into three sections: elements less +than the pivot, elements equal to the pivot, and elements greater than the pivot. Now the good pivot check ignores the +size of the segment that comprises elements = to pivot. diff --git a/src/main/java/algorithms/sorting/quickSort/hoares/QuickSort.java b/src/main/java/algorithms/sorting/quickSort/hoares/QuickSort.java index a5e1cb3a..ab549352 100644 --- a/src/main/java/algorithms/sorting/quickSort/hoares/QuickSort.java +++ b/src/main/java/algorithms/sorting/quickSort/hoares/QuickSort.java @@ -4,17 +4,18 @@ * Here, we are implementing Hoares's QuickSort where we sort the array in increasing (or more precisely, * non-decreasing) order. We will follow lecture implementation here, which differs slightly from the usual * implementation of Hoare's. See more under notes in README. - * + *

* Implementation Invariant: * The pivot is in the correct position, with elements to its left being <= it, and elements to its right being > it. * (We edited the pseudocode a bit to keep the duplicates to the left of the pivot.) - * + *

* This implementation picks the first element as the pivot. */ public class QuickSort { /** * Sorts the given array in-place in non-decreasing order. + * * @param arr array to be sorted. */ public static void sort(int[] arr) { @@ -42,7 +43,7 @@ private static void quickSort(int[] arr, int start, int end) { * Partitions the sub-array from index 'start' to index 'end' around a randomly selected pivot element. * The elements less than or equal to the pivot are placed on the left side, and the elements greater than * the pivot are placed on the right side. - * + *

* Given a sub-array of length m, the time complexity of the partition subroutine is O(m) as we need to iterate * through every element in the sub-array once. * @@ -60,7 +61,7 @@ private static int partition(int[] arr, int start, int end) { do { low++; } while (low < high && arr[low] <= pivot); // we use <= as opposed to < to pack duplicates to the left side - // of the pivot + // of the pivot do { high--; } while (low < high && arr[high] > pivot); diff --git a/src/main/java/algorithms/sorting/quickSort/hoares/README.md b/src/main/java/algorithms/sorting/quickSort/hoares/README.md index db35dba4..934e4361 100644 --- a/src/main/java/algorithms/sorting/quickSort/hoares/README.md +++ b/src/main/java/algorithms/sorting/quickSort/hoares/README.md @@ -5,9 +5,9 @@ Note that the usual Hoare's QuickSort differs slightly from lecture implementati This version of QuickSort assumes the **absence of duplicates** in our array. -QuickSort is a sorting algorithm based on the divide-and-conquer strategy. It works by selecting a pivot element from -the array and rearranging the elements such that all elements less than the pivot are on its left, and -all elements greater than the pivot are on its right. This effectively partitions the array into two parts. The same +QuickSort is a sorting algorithm based on the divide-and-conquer strategy. It works by selecting a pivot element from +the array and rearranging the elements such that all elements less than the pivot are on its left, and +all elements greater than the pivot are on its right. This effectively partitions the array into two parts. The same process is then applied recursively to the two partitions until the entire array is sorted. Implementation Invariant: @@ -18,39 +18,44 @@ The pivot is in the correct position, with elements to its left being < it, and Example Credits: Prof Seth/Lecture Slides ## Complexity Analysis + Complexity Analysis: (this analysis is based on fixed index pivot selection) Time: + - Expected worst case (poor choice of pivot): O(n^2) - Expected average case: O(nlogn) - Expected best case (balanced pivot): O(nlogn) In the best case of a balanced pivot, the partitioning process divides the array in half, which leads to log n -levels of recursion. Given a sub-array of length m, the time complexity of the partition subroutine is O(m) as we +levels of recursion. Given a sub-array of length m, the time complexity of the partition subroutine is O(m) as we need to iterate through every element in the sub-array once. Therefore, the recurrence relation is: T(n) = 2T(n/2) + O(n) => O(nlogn). Even in the average case where the chosen pivot partitions the array by a fraction, there will still be log n levels of recursion. (e.g. T(n) = T(n/10) + T(9n/10) + O(n) => O(nlogn)) -However, using a fixed pivot, such as always choosing the first element as the pivot, can lead to worst-case behavior, -especially when the array is already sorted or has a specific pattern. This is because in such cases, the partitioning +However, using a fixed pivot, such as always choosing the first element as the pivot, can lead to worst-case behavior, +especially when the array is already sorted or has a specific pattern. This is because in such cases, the partitioning might create highly unbalanced sub-arrays, causing the algorithm to degrade to O(n^2) time complexity. Space: + - O(1) excluding memory allocated to the call stack, since partitioning is done in-place ## Notes ### Presence of Duplicates -The above analysis assumes the absence of duplicates in our array. We can change the implementation slightly to keep -all elements = pivot to the left of the pivot element. In this case, if there are many duplicates in the array, -e.g. {1, 1, 1, 1}, the 1st pivot will be placed in the 3rd idx, and 2nd pivot in 2nd idx, 3rd pivot in the 1st idx and -4th pivot in the 0th idx. As we observe, the presence of many duplicates in the array leads to extremely unbalanced + +The above analysis assumes the absence of duplicates in our array. We can change the implementation slightly to keep +all elements = pivot to the left of the pivot element. In this case, if there are many duplicates in the array, +e.g. {1, 1, 1, 1}, the 1st pivot will be placed in the 3rd idx, and 2nd pivot in 2nd idx, 3rd pivot in the 1st idx and +4th pivot in the 0th idx. As we observe, the presence of many duplicates in the array leads to extremely unbalanced partitioning, leading to a O(n^2) time complexity. ### Usual Implementation of Hoare's QuickSort -The usual implementation of Hoare's partition scheme does not necessarily put the pivot in its correct position. It + +The usual implementation of Hoare's partition scheme does not necessarily put the pivot in its correct position. It merely partitions the array into <= pivot and >= pivot portions. Brief Description: @@ -74,7 +79,8 @@ Implementation Invariant: All elements in A[start, returnIdx] are <= pivot and all elements in A[returnIdx + 1, end] are >= pivot. ## Hoare's vs Lomuto's QuickSort -Hoare's partition scheme is in contrast to Lomuto's partition scheme. Hoare's uses two pointers, while Lomuto's uses + +Hoare's partition scheme is in contrast to Lomuto's partition scheme. Hoare's uses two pointers, while Lomuto's uses one. Hoare's partition scheme is generally more efficient as it requires less swaps. See more at https://www.geeksforgeeks.org/hoares-vs-lomuto-partition-scheme-quicksort/. diff --git a/src/main/java/algorithms/sorting/quickSort/lomuto/QuickSort.java b/src/main/java/algorithms/sorting/quickSort/lomuto/QuickSort.java index c2985ce8..792ba4be 100644 --- a/src/main/java/algorithms/sorting/quickSort/lomuto/QuickSort.java +++ b/src/main/java/algorithms/sorting/quickSort/lomuto/QuickSort.java @@ -1,42 +1,40 @@ package algorithms.sorting.quickSort.lomuto; -import java.lang.Math; - /** * Here, we are implementing Lomuto's QuickSort where we sort the array in increasing (or more precisely, * non-decreasing) order. - * + *

* Basic Description: * QuickSort is a divide-and-conquer sorting algorithm. The basic idea behind Quicksort is to choose a pivot element, * places it in its correct position in the sorted array, and then recursively sorts the sub-arrays on either side of * the pivot. When we introduce randomization in pivot selection, every element has equal probability of being * selected as the pivot. This means the chance of an extreme element getting chosen as the pivot is decreased, so we * reduce the probability of encountering the worst-case scenario of imbalanced partitioning. - * + *

* Implementation Invariant: * The pivot is in the correct position, with elements to its left being <= it, and elements to its right being > it. - * + *

* We are implementing Lomuto's partition scheme here. This is opposed to Hoare's partition scheme, see more at * https://www.geeksforgeeks.org/hoares-vs-lomuto-partition-scheme-quicksort/. - * + *

* Complexity Analysis: * Time: * - Expected worst case (poor choice of pivot): O(n^2) * - Expected average case: O(nlogn) * - Expected best case (balanced pivot): O(nlogn) - * + *

* In the best case of a balanced pivot, the partitioning process divides the array in half, which leads to log n * levels of recursion. Given a sub-array of length m, the time complexity of the partition subroutine is O(m) as we * need to iterate through every element in the sub-array once. * Therefore, the recurrence relation is: T(n) = 2T(n/2) + O(n) => O(nlogn). - * + *

* Even in the average case where the chosen pivot partitions the array by a fraction, there will still be log n levels * of recursion. (e.g. T(n) = T(n/10) + T(9n/10) + O(n) => O(nlogn)) - * + *

* However, if there are many duplicates in the array, e.g. {1, 1, 1, 1}, the 1st pivot will be placed in the 3rd idx, * and 2nd pivot in 2nd idx, 3rd pivot in the 1st idx and 4th pivot in the 0th idx. As we observe, the presence of many * duplicates in the array leads to extremely unbalanced partitioning, leading to a O(n^2) time complexity. - * + *

* Space: * - O(1) excluding memory allocated to the call stack, since partitioning is done in-place */ @@ -44,6 +42,7 @@ public class QuickSort { /** * Sorts the given array in-place in non-decreasing order. + * * @param arr array to be sorted. */ public static void sort(int[] arr) { @@ -71,7 +70,7 @@ private static void quickSort(int[] arr, int start, int end) { * Partitions the sub-array from index 'start' to index 'end' around a randomly selected pivot element. * The elements less than or equal to the pivot are placed on the left side, and the elements greater than * the pivot are placed on the right side. - * + *

* Given a sub-array of length m, the time complexity of the partition subroutine is O(m) as we need to iterate * through every element in the sub-array once. * diff --git a/src/main/java/algorithms/sorting/quickSort/lomuto/README.md b/src/main/java/algorithms/sorting/quickSort/lomuto/README.md index ded07b7e..f88b5aa0 100644 --- a/src/main/java/algorithms/sorting/quickSort/lomuto/README.md +++ b/src/main/java/algorithms/sorting/quickSort/lomuto/README.md @@ -1,11 +1,11 @@ -This is how QuickSort works if we always pick the first element as the pivot. +This is how QuickSort works if we always pick the first element as the pivot. ![QuickSort with first element as pivot](../../../../../../../docs/assets/images/QuickSortFirstPivot.png) Image Source: https://www.geeksforgeeks.org/implement-quicksort-with-first-element-as-pivot/ -If we use randomised pivot selection, the idea is very similar to the above implementation. All we -need to do is to swap the random pivot to the first element in the array, then partition as per usual, -then swap the pivot back to its correct position. Below is an illustration: +If we use randomised pivot selection, the idea is very similar to the above implementation. All we +need to do is to swap the random pivot to the first element in the array, then partition as per usual, +then swap the pivot back to its correct position. Below is an illustration: -![Lomuto's QuickSort with random pivot](../../../../../../../docs/assets/images/Lomutos.jpeg) \ No newline at end of file +![Lomuto's QuickSort with random pivot](../../../../../../../docs/assets/images/Lomutos.jpeg) diff --git a/src/main/java/algorithms/sorting/quickSort/paranoid/QuickSort.java b/src/main/java/algorithms/sorting/quickSort/paranoid/QuickSort.java index dff5c860..fef9bc4e 100644 --- a/src/main/java/algorithms/sorting/quickSort/paranoid/QuickSort.java +++ b/src/main/java/algorithms/sorting/quickSort/paranoid/QuickSort.java @@ -1,33 +1,31 @@ package algorithms.sorting.quickSort.paranoid; -import java.lang.Math; - /** * Here, we are implementing Paranoid QuickSort where we sort the array in increasing (or more precisely, * non-decreasing) order. - * + *

* This is basically Lomuto's QuickSort, with an additional check to guarantee a good pivot. - * + *

* Complexity Analysis: * Time: (this analysis assumes the absence of many duplicates in our array) * - Expected worst case: O(nlogn) * - Expected average case: O(nlogn) * - Expected best case: O(nlogn) - * + *

* The additional check to guarantee a good pivot guards against the worst case scenario where the chosen pivot results * in an extremely imbalanced partitioning. Since the chosen pivot has to at least partition the array into a * 1/10, 9/10 split, the recurrence relation will be: T(n) = T(n/10) + T(9n/10) + n(# iterations of pivot selection). - * + *

* The number of iterations of pivot selection is expected to be <2 (more precisely, 1.25). This is because * P(good pivot) = 8/10. Expected number of tries to get a good pivot = 1 / P(good pivot) = 10/8 = 1.25. - * + *

* Therefore, the expected time-complexity is: T(n) = T(n/10) + T(9n/10) + 1.25n => O(nlogn). - * + *

* Edge case: does not terminate * The presence of this additional check and repeating pivot selection means that if we have an array of * length n >= 10 containing all/many duplicates of the same number, any pivot we pick will be a bad pivot and we will * enter an infinite loop of repeating pivot selection. - * + *

* Space: * - O(1) excluding memory allocated to the call stack, since partitioning is done in-place */ @@ -35,6 +33,7 @@ public class QuickSort { /** * Sorts the given array in-place in non-decreasing order. + * * @param arr array to be sorted. */ public static void sort(int[] arr) { @@ -56,8 +55,7 @@ private static void quickSort(int[] arr, int start, int end) { if (isGoodPivot(pIdx, start, end)) { //check to guarantee good pivot quickSort(arr, start, pIdx - 1); quickSort(arr, pIdx + 1, end); - } - else { + } else { quickSort(arr, start, end); } } @@ -120,9 +118,9 @@ private static int random(int start, int end) { /** * Checks if the given pivot index is a good pivot for the QuickSort algorithm. * A good pivot helps avoid worst-case behavior in QuickSort. - * + *

* For arrays of length greater than or equal to 10, a good pivot leaves at least 1/10th of the array on each side. - * + *

* If n < 10, such a pivot condition would be meaningless, therefore always return true. This would cause * the worst case recurrence relation to be T(n) = T(n-1) + O(n) => O(n^2) for small sub-arrays, but the overall * asymptotic time complexity of Paranoid QuickSort is still O(nlogn). @@ -130,7 +128,7 @@ private static int random(int start, int end) { * @param pIdx The index to be checked for being a good pivot. * @param start The starting index of the current sub-array. * @param end The ending index of the current sub-array. - * @return True if the given index is a good pivot, false otherwise. + * @return True if the given index is a good pivot, false otherwise. */ private static boolean isGoodPivot(int pIdx, int start, int end) { double n = end - start + 1; // express n as a double so n/10 will be calculated as a double diff --git a/src/main/java/algorithms/sorting/quickSort/threeWayPartitioning/QuickSort.java b/src/main/java/algorithms/sorting/quickSort/threeWayPartitioning/QuickSort.java index 05a4ea01..46667195 100644 --- a/src/main/java/algorithms/sorting/quickSort/threeWayPartitioning/QuickSort.java +++ b/src/main/java/algorithms/sorting/quickSort/threeWayPartitioning/QuickSort.java @@ -3,24 +3,24 @@ /** * Here, we are implementing Paranoid QuickSort with three-way partitioning where we sort the array in increasing (or * more precisely, non-decreasing) order. - * + *

* Three-way partitioning is used in QuickSort to tackle the scenario where there are many duplicate elements in the * array being sorted. - * + *

* The idea behind three-way partitioning is to divide the array into three sections: elements less than the pivot, * elements equal to the pivot, and elements greater than the pivot. By doing so, we can avoid unnecessary comparisons * and swaps with duplicate elements, making the sorting process more efficient. - * + *

* Implementation Invariant: * The pivot and any element numerically equal to the pivot will be in the correct positions in the array. Elements * to their left are < them and elements to their right are > than them. - * + *

* Complexity Analysis: * Time: * - Worst case: O(nlogn) * - Average case: O(nlogn) * - Best case: O(nlogn) - * + *

* Space: * - O(1) excluding memory allocated to the call stack, since partitioning is done in-place */ @@ -67,8 +67,9 @@ private static void quickSort(int[] arr, int start, int end) { * @param start the starting index (inclusive) of the sub-array to be partitioned. * @param end the ending index (inclusive) of the sub-array to be partitioned. * @return An integer array containing the indices that represent the boundaries - * of the three partitions: [ending idx of the < portion of the array, starting idx of the > portion of the array] - * if length of arr > 1, else null + * of the three partitions: [ending idx of the < portion of the array, + * starting idx of the > portion of the array] + * if length of arr > 1, else null */ private static int[] partition(int[] arr, int start, int end) { // ___<___ ___=___ ___IP___ ___>___ @@ -79,7 +80,7 @@ private static int[] partition(int[] arr, int start, int end) { int pivot = arr[pivotIdx]; swap(arr, pivotIdx, start); // swap the pivot to the start of the array, arr[start] is now our = portion - // of the array + // of the array int eqStart = start; int eqEnd = start; @@ -142,23 +143,23 @@ private static int random(int start, int end) { /** * Checks if the pivot is a good pivot for the QuickSort algorithm. * A good pivot helps avoid worst-case behavior in QuickSort. - * + *

* Since we have three-way partitioning, we cannot use 1/10, 9/10 split of the array as our good pivot condition. * Note that our goal here is to ensure the sizes of the sub-arrays QuickSort is to recurse on are roughly the same * to ensure that our partitioning is not too imbalanced. The pivot condition we chose is: the larger sub-array can * be at most 9 times the size of the smaller sub-array. - * + *

* If n < 10, such a pivot condition would be meaningless, therefore always return true. This would cause * the worst case recurrence relation to be T(n) = T(n-1) + O(n) => O(n^2) for small sub-arrays, but the overall * asymptotic time complexity of Paranoid QuickSort is still O(nlogn). - * + *

* For an all-duplicates array, all pivots will be considered good pivots, therefore return true. * - * @param firstPIdx The ending index of the < portion of the sub-array. - * @param secondPIdx The starting index of the > portion of the sub-array. - * @param start The starting index of the current sub-array. - * @param end The ending index of the current sub-array. - * @return True if the given index is a good pivot, false otherwise. + * @param firstPIdx The ending index of the < portion of the sub-array. + * @param secondPIdx The starting index of the > portion of the sub-array. + * @param start The starting index of the current sub-array. + * @param end The ending index of the current sub-array. + * @return True if the given index is a good pivot, false otherwise. */ private static boolean isGoodPivot(int firstPIdx, int secondPIdx, int start, int end) { int n = end - start + 1; @@ -178,4 +179,4 @@ private static boolean isGoodPivot(int firstPIdx, int secondPIdx, int start, int } } -} \ No newline at end of file +} diff --git a/src/main/java/algorithms/sorting/radixSort/README.md b/src/main/java/algorithms/sorting/radixSort/README.md index d0cc3b17..cc9b7d9e 100644 --- a/src/main/java/algorithms/sorting/radixSort/README.md +++ b/src/main/java/algorithms/sorting/radixSort/README.md @@ -2,36 +2,37 @@ ## Background -Radix Sort is a non-comparison based, stable sorting algorithm with a counting sort subroutine. +Radix Sort is a non-comparison based, stable sorting algorithm with a counting sort subroutine. -Radix Sort continuously sorts based on the least-significant segment of a element -to the most-significant value of a element. +Radix Sort continuously sorts based on the least-significant segment of a element +to the most-significant value of a element. -The definition of a 'segment' is user defined and defers from implementation to implementation. -Within our implementation, we define each segment as a bit chunk. +The definition of a 'segment' is user defined and defers from implementation to implementation. +Within our implementation, we define each segment as a bit chunk. -For example, if we aim to sort integers, we can sort each element -from the least to most significant digit, with the digits being our 'segments'. +For example, if we aim to sort integers, we can sort each element +from the least to most significant digit, with the digits being our 'segments'. -Within our implementation, we take the binary representation of the elements and -partition it into 8-bit segments, a integer is represented in 32 bits, -this gives us 4 total segments to sort through. +Within our implementation, we take the binary representation of the elements and +partition it into 8-bit segments, a integer is represented in 32 bits, +this gives us 4 total segments to sort through. -Note that the binary representation is weighted positional, -where each bit's value is dependent on its overall position -within the representation (the n-th bit from the right represents *2^n*), +Note that the binary representation is weighted positional, +where each bit's value is dependent on its overall position +within the representation (the n-th bit from the right represents *2^n*), hence we can actually increase / decrease the number segments we wish to conduct a split from. ![Radix Sort](https://miro.medium.com/v2/resize:fit:661/1*xFnpQ4UNK0TvyxiL8r1svg.png) We place each element into a queue based on the number of possible segments that could be generated. -Suppose the values of our segments are in base-10, (limited to a value within range *[0, 9]*), +Suppose the values of our segments are in base-10, (limited to a value within range *[0, 9]*), we get 10 queues. We can also see that radix sort is stable since they are enqueued in a manner where the first observed element remains at the head of the queue -*Source: Level Up Coding* +*Source: Level Up Coding* ### Implementation Invariant + At the start of the *i-th* segment we are sorting on, the array has already been sorted on the previous *(i - 1)-th* segments. @@ -45,21 +46,23 @@ Hence, a stable sort is required to maintain the order as the sorting is done with respect to each of the segments. ## Complexity Analysis + **Time**: Let *b* be the length of a single element we are sorting and *r* is the amount of bit-string -we plan to break each element into. -(Essentially, *b/r* represents the number of segments we +we plan to break each element into. +(Essentially, *b/r* represents the number of segments we sort on and hence the number of passes we do during our sort). -Note that we derive *(2^r + n)* from the counting sort subroutine, +Note that we derive *(2^r + n)* from the counting sort subroutine, since we have *2^r* represents the range since we have *r* bits. We get a general time complexity of *O((b/r) * (2^r + n))* -**Space**: *O(n + 2^r)* +**Space**: *O(n + 2^r)* ## Notes + - Radix sort's time complexity is dependent on the maximum number of digits in each element, -hence it is ideal to use it on integers with a large range and with little digits. -- This could mean that Radix Sort might end up performing worst on small sets of data -if any one given element has a in-proportionate amount of digits. + hence it is ideal to use it on integers with a large range and with little digits. +- This could mean that Radix Sort might end up performing worst on small sets of data + if any one given element has a in-proportionate amount of digits. diff --git a/src/main/java/algorithms/sorting/radixSort/RadixSort.java b/src/main/java/algorithms/sorting/radixSort/RadixSort.java index 100cb73d..866b5628 100644 --- a/src/main/java/algorithms/sorting/radixSort/RadixSort.java +++ b/src/main/java/algorithms/sorting/radixSort/RadixSort.java @@ -1,73 +1,72 @@ package algorithms.sorting.radixSort; /** -* This class implements a Radix Sort Algorithm. -*/ + * This class implements a Radix Sort Algorithm. + */ public class RadixSort { - private static final int NUM_BITS = 8; - private static final int NUM_SEGMENTS = 4; + private static final int NUM_BITS = 8; + private static final int NUM_SEGMENTS = 4; - /** - * Creates masking on the segment to obtain the value of the digit. - * - * @param num The number. - * @param segment The segment we are interested in. - * - * @return The value of the digit in the number at the given segment. - */ - private static int getSegmentMasked(int num, int segment) { - // Bit masking here to extract each segment from the integer. - int mask = ((1 << NUM_BITS) - 1) << (segment * NUM_BITS); - return (num & mask) >> (segment * NUM_BITS); - } + /** + * Creates masking on the segment to obtain the value of the digit. + * + * @param num The number. + * @param segment The segment we are interested in. + * @return The value of the digit in the number at the given segment. + */ + private static int getSegmentMasked(int num, int segment) { + // Bit masking here to extract each segment from the integer. + int mask = ((1 << NUM_BITS) - 1) << (segment * NUM_BITS); + return (num & mask) >> (segment * NUM_BITS); + } - /** - * Radix sorts a given input array and updates the output array in-place. - * - * @param arr original input array. - * @param sorted output array. - */ - private static void radixSort(int[] arr, int[] sorted) { - // sort the N numbers by segments, starting from left-most segment - for (int i = 0; i < NUM_SEGMENTS; i++) { - int[] freqMap = new int[1 << NUM_BITS]; // at most this number of elements + /** + * Radix sorts a given input array and updates the output array in-place. + * + * @param arr original input array. + * @param sorted output array. + */ + private static void radixSort(int[] arr, int[] sorted) { + // sort the N numbers by segments, starting from left-most segment + for (int i = 0; i < NUM_SEGMENTS; i++) { + int[] freqMap = new int[1 << NUM_BITS]; // at most this number of elements - // count each element - for (int num : arr) { - freqMap[getSegmentMasked(num, i)]++; - } - // get prefix sum - for (int j = 1; j < freqMap.length; j++) { - freqMap[j] += freqMap[j - 1]; - } - // place each number in its correct sorted position up until the given segment - for (int k = arr.length - 1; k >= 0; k--) { - int curr = arr[k]; - int id = getSegmentMasked(curr, i); - sorted[freqMap[id] - 1] = curr; - freqMap[id]--; - } - // We do a swap so that our results above for this segment is - // saved and passed as input to the next segment. - // By doing this we no longer need to create a new array - // every time we shift to a new segment to sort. - // We can constantly reuse the array, allowing us to only use O(n) space. - int[] tmp = arr; - arr = sorted; - sorted = tmp; + // count each element + for (int num : arr) { + freqMap[getSegmentMasked(num, i)]++; + } + // get prefix sum + for (int j = 1; j < freqMap.length; j++) { + freqMap[j] += freqMap[j - 1]; + } + // place each number in its correct sorted position up until the given segment + for (int k = arr.length - 1; k >= 0; k--) { + int curr = arr[k]; + int id = getSegmentMasked(curr, i); + sorted[freqMap[id] - 1] = curr; + freqMap[id]--; + } + // We do a swap so that our results above for this segment is + // saved and passed as input to the next segment. + // By doing this we no longer need to create a new array + // every time we shift to a new segment to sort. + // We can constantly reuse the array, allowing us to only use O(n) space. + int[] tmp = arr; + arr = sorted; + sorted = tmp; + } + sorted = arr; } - sorted = arr; - } - /** - * Calls radix sort inplace on a given array. - * - * @param arr The array to be sorted. - */ - public static void radixSort(int[] arr) { - int[] sorted = new int[arr.length]; - radixSort(arr, sorted); - arr = sorted; // swap back lol - } + /** + * Calls radix sort inplace on a given array. + * + * @param arr The array to be sorted. + */ + public static void radixSort(int[] arr) { + int[] sorted = new int[arr.length]; + radixSort(arr, sorted); + arr = sorted; // swap back lol + } } diff --git a/src/main/java/algorithms/sorting/selectionSort/README.md b/src/main/java/algorithms/sorting/selectionSort/README.md index b3be1784..d7ba0d7d 100644 --- a/src/main/java/algorithms/sorting/selectionSort/README.md +++ b/src/main/java/algorithms/sorting/selectionSort/README.md @@ -1,17 +1,19 @@ # Selection Sort -Selection sort is another intuitive comparison-based sorting algorithm. It works similarly to other sorting algorithms +Selection sort is another intuitive comparison-based sorting algorithm. It works similarly to other sorting algorithms like bubble and insertion in the sense that it maintains a sorted and unsorted region. It does so by repeatedly finding -smallest (or largest) element in the unsorted region, and places the element in the correct and final position as it +smallest (or largest) element in the unsorted region, and places the element in the correct and final position as it would be in the sorted array. ![SelectionSort](../../../../../../docs/assets/images/SelectionSort.png) ## Complexity Analysis + **Time**: - - Worst case: O(n^2) - - Average case: O(n^2) - - Best case: O(n^2) + +- Worst case: O(n^2) +- Average case: O(n^2) +- Best case: O(n^2) Regardless of how sorted the input array is, selectionSort will run the minimum element finding algorithm (n-1) times. For an input array of length m, finding the minimum element necessarily takes O(m) time. Therefore, the @@ -19,4 +21,4 @@ time complexity of selectionSort is n + (n-1) + (n-2) + ... + 2 = O(n^2) **Space**: O(1) since sorting is done in-place -Image Source: https://www.hackerearth.com/practice/algorithms/sorting/selection-sort/tutorial/ \ No newline at end of file +Image Source: https://www.hackerearth.com/practice/algorithms/sorting/selection-sort/tutorial/ diff --git a/src/main/java/algorithms/sorting/selectionSort/SelectionSort.java b/src/main/java/algorithms/sorting/selectionSort/SelectionSort.java index 80f30636..9fdcc92b 100644 --- a/src/main/java/algorithms/sorting/selectionSort/SelectionSort.java +++ b/src/main/java/algorithms/sorting/selectionSort/SelectionSort.java @@ -1,22 +1,23 @@ package algorithms.sorting.selectionSort; -/** Here, we are implementing SelectionSort where we sort the array in increasing (or more precisely, non-decreasing) +/** + * Here, we are implementing SelectionSort where we sort the array in increasing (or more precisely, non-decreasing) * order. - * + *

* Implementation Invariant: * Let the array of length n to be sorted be A. * The loop invariant is: * At the end of the kth iteration, the smallest k items are correctly sorted in the first k positions of the array. - * + *

* So, at the end of the (n-1)th iteration of the loop, the smallest (n-1) items are correctly sorted in the first (n-1) * positions of the array, leaving the last item correctly positioned in the last index of the array. Therefore, * (n-1) iterations of the loop is sufficient. - * */ public class SelectionSort { /** * Sorts the given array in-place in non-decreasing order. + * * @param arr array to be sorted. * @return the same array arr that is sorted. */ @@ -36,11 +37,11 @@ public static int[] sort(int[] arr) { * The range is from 'start' (inclusive) to 'end' (exclusive). * * @param start the starting index (inclusive) of the range to be considered. - * @param end the ending index (exclusive) of the range to be considered. - * @param arr the array to be sorted. + * @param end the ending index (exclusive) of the range to be considered. + * @param arr the array to be sorted. * @return the index of the minimum element within the range. - * - * We can easily modify this method to find maxElemIdx instead to sort the array in non-increasing order. + *

+ * We can easily modify this method to find maxElemIdx instead to sort the array in non-increasing order. */ private static int minElemIdx(int start, int end, int[] arr) { int min = Integer.MAX_VALUE; diff --git a/src/main/java/algorithms/util/matrixToListConverter.java b/src/main/java/algorithms/util/MatrixToListConverter.java similarity index 65% rename from src/main/java/algorithms/util/matrixToListConverter.java rename to src/main/java/algorithms/util/MatrixToListConverter.java index e0604140..90db4580 100644 --- a/src/main/java/algorithms/util/matrixToListConverter.java +++ b/src/main/java/algorithms/util/MatrixToListConverter.java @@ -1,41 +1,55 @@ package algorithms.util; -import java.util.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + /** * Supports method to convert an Adjacency Matrix into an Adjacency list - * + *

* Importance: In some cases, an algorithm's efficiency depends on the use of an Adjacency list over a Matrix * This is because an Adjacency list supports certain functions more efficiently, such as: - * - O(1) time for adding vertices as opposed to O(V^2) in a matrix - * - O(V + E) space complexity as opposed to O(V^2) <- Much more efficient for sparse graphs - * - Much easier to iterate over all edges/compute all neighbours - * + * - O(1) time for adding vertices as opposed to O(V^2) in a matrix + * - O(V + E) space complexity as opposed to O(V^2) <- Much more efficient for sparse graphs + * - Much easier to iterate over all edges/compute all neighbours + *

* Adjacency Matrix in the form of a 2D Integer Array int[][]. * Example matrix: [[0, 1, 1], <- Matrix[i][j] == 1 iff Vertex i shares an edge with Vertex j - * [1, 0, 0] - * [1, 0, 0]] - * - * Adjacency list in the form of an array of lists: ArrayList[] , where array[i] is a list of neighbour indexes + * [1, 0, 0] + * [1, 0, 0]] + *

+ * Adjacency list in the form of an array of lists: ArrayList[] , where array[i] is a list of neighbour indexes * Example list: [(1, 2), (1), (1)] <- Here, square brackets [] denote arrays and round brackets () denote lists - * (This is the list representation of the example matrix above, - * where Vertex 0 shares an edge with Vertex 1 and 2) - * - * While a list implementation is used here, the conventional implementation of adjacency lists utilizes linked lists or nodes + * (This is the list representation of the example matrix above, + * where Vertex 0 shares an edge with Vertex 1 and 2) + *

+ * While a list implementation is used here, the conventional implementation of adjacency lists utilizes linked + * lists or nodes * To implement this, we can create a private node class to hold our original object and whatever data that comes * alongside (eg weights), as well as additional node attribute(s) that serve as pointers to next (or prev) node. * Then, initialize an array of V nodes, where the ith node represents a list of all neighbouring vertices. * Neighbouring vertices can be added simply by chaining a new node to the ith node using pointers - * + *

* Note: in the event where integers aren't used to denote elements or perhaps are sparse (eg 0, 1, 123456545), * one can consider using a dictionary where the key is the element and value is the head node. */ +public class MatrixToListConverter { -public class matrixToListConverter { - + /** + * Converts a given adjacency matrix to an adjacency list. + * + * @param adjM the adjacency matrix to be converted. + * @return the adjacency list representation of the adjacency matrix. + * + * @throws InvalidMatrixException if an error occurs while converting the matrix. + */ public static List[] convert(int[][] adjM) throws InvalidMatrixException { int numOfVertices = adjM.length; // Handling empty matrix case so no index out of bounds error - if (Arrays.equals(adjM, new int[0][0])) { return new ArrayList[0]; } + if (Arrays.equals(adjM, new int[0][0])) { + return new ArrayList[0]; + } // Handling invalid matrix case (Graph matrix must be nxn) if (numOfVertices != adjM[0].length) { throw new InvalidMatrixException("The matrix must be N x N, not N x M!"); @@ -54,6 +68,9 @@ public static List[] convert(int[][] adjM) throws InvalidMatrixExceptio return adjL; } + /** + * Custom Exception representing an invalid matrix. + */ public static class InvalidMatrixException extends RuntimeException { public InvalidMatrixException(String message) { super(message); diff --git a/src/main/java/dataStructures/avlTree/AVLTree.java b/src/main/java/dataStructures/avlTree/AVLTree.java index 1d5f8719..3724de73 100644 --- a/src/main/java/dataStructures/avlTree/AVLTree.java +++ b/src/main/java/dataStructures/avlTree/AVLTree.java @@ -1,461 +1,484 @@ package dataStructures.avlTree; -import java.util.*; +import java.util.LinkedList; +import java.util.Queue; /** * Implementation of Adelson-Velsky, Landis (AVL) Tree. + * * @param generic type of object to be stored; must be comparable - * client methods: - * height(Node n) - * height(T key) - * root() - * insert(T key) - * delete(T key) - * search(T key) - * predecessor(T key) - * successor(T key) - * printInorder() - * printPreorder() - * printPostorder() - * printLevelorder() + * client methods: + * height(Node n) + * height(T key) + * root() + * insert(T key) + * delete(T key) + * search(T key) + * predecessor(T key) + * successor(T key) + * printInorder() + * printPreorder() + * printPostorder() + * printLevelOrder() */ public class AVLTree> { - private Node root; - - /** - * Get height of node in avl tree. - * @param n node whose height is to be queried - * @return int value denoting height - */ - public int height(Node n) { - return n == null ? -1 : n.height; - } - - /** - * Update height of node in avl tree during rebalancing. - * @param n node whose height is to be updated - */ - private void updateHeight(Node n) { - n.height = 1 + Math.max(height(n.left), height(n.right)); - } - - /** - * Get balance factor to check if height-balanced property is violated. - * Note: negative value means tree is right heavy, - * positive value means tree is left heavy, - * 0 means tree is balanced in weight. - * @param n check balance factor of node - * @return int value representing the balance factor - */ - private int getBalance(Node n) { - return n == null ? 0 : height(n.left) - height(n.right); - } - - /** - * Performs a right rotation on the specified node. - * Note that function should be called only if the - * node has a left child since it will be the - * new root. - * @param n node to perform right rotation on. - * @return the new root after rotation. - */ - private Node rotateRight(Node n) { - Node newRoot = n.left; - Node newLeftSub = newRoot.right; - newRoot.right = n; - n.left = newLeftSub; - - newRoot.parent = n.parent; - n.parent = newRoot; - - updateHeight(n); - updateHeight(newRoot); - return newRoot; - } - - /** - * Performs a left rotation on the specified node. - * Note that function should be called only if the - * node has a right child since it will be the - * new root. - * @param n node to perform left rotation on - * @return new root after rotation - */ - private Node rotateLeft(Node n) { - Node newRoot = n.right; - Node newRightSub = newRoot.left; - newRoot.left = n; - n.right = newRightSub; - - newRoot.parent = n.parent; - n.parent = newRoot; - - updateHeight(n); - updateHeight(newRoot); - return newRoot; - } - - /** - * Rebalances a node in the tree based on balance factor. - * @param n node to be rebalanced - * @return new root after rebalancing - */ - private Node rebalance(Node n) { - updateHeight(n); - int balance = getBalance(n); - if (balance < -1) { // right-heavy case - if (height(n.right.left) > height(n.right.right)) { - n.right = rotateRight(n.right); - } - n = rotateLeft(n); - } else if (balance > 1) { // left-heavy case - if (height(n.left.right) > height(n.left.left)) { - n.left = rotateLeft(n.left); - } - n = rotateRight(n); + private Node root; + + /** + * Get height of node in avl tree. + * + * @param n node whose height is to be queried + * @return int value denoting height + */ + public int height(Node n) { + return n == null ? -1 : n.getHeight(); } - return n; - } - - /** - * Insert a key which will be wrapped in a node, into the tree rooted at a specified node. - * NOTE: ASSUMPTION THAT NO TWO NODES SHARE THE SAME KEY VALUE. - * @param node the (sub)tree rooted at node which the key will be inserted into - * @param key the key to insert - * @return the (new) node which the tree is rooted at after rebalancing - */ - private Node insert(Node node, T key) { - if (node == null) { - return new Node<>(key); - } else if (node.key.compareTo(key) < 0) { - node.right = insert(node.right, key); - node.right.parent = node; - // note that insufficient to update parent in rotateLeft & rotateRight if still considered balanced - } else if (node.key.compareTo(key) > 0) { - node.left = insert(node.left, key); - node.left.parent = node; - } else { - throw new RuntimeException("Duplicate key not supported!"); + + /** + * Update height of node in avl tree during rebalancing. + * + * @param n node whose height is to be updated + */ + private void updateHeight(Node n) { + n.setHeight(1 + Math.max(height(n.getLeft()), height(n.getRight()))); } - return rebalance(node); - } - - /** - * Delete a key from the avl tree rooted at a specified node. - * Find the node that holds the key and remove the node from the tree. - * @param node the (sub)tree rooted at node which the key will be deleted from - * @param key the key to remove - * @return the (new) root which the tree is rooted at after rebalancing - */ - private Node delete(Node node, T key) { - if (node == null) { - return null; - } else if (node.key.compareTo(key) < 0) { - node.right = delete(node.right, key); - } else if (node.key.compareTo(key) > 0) { - node.left = delete(node.left, key); - } else { - if (node.left == null || node.right == null) { // case of 1 or 0 child - // node = node.left == null ? node.right : node.left; - if (node.left == null && node.right == null) { - node = null; // 0-child case - } else if (node.right == null) { - node.left.parent = node.parent; - node = node.left; - } else { - node.right.parent = node.parent; - node = node.right; - } - } else { // 2-children case - Node successor = getMostLeft(node.right); - node.key = successor.key; - // since this is a 2-children case, successor of deleted node have - // at most one child; right-child (else it would continue going left) - node.right = delete(node.right, successor.key); - } + + /** + * Get balance factor to check if height-balanced property is violated. + * Note: negative value means tree is right heavy, + * positive value means tree is left heavy, + * 0 means tree is balanced in weight. + * + * @param n check balance factor of node + * @return int value representing the balance factor + */ + private int getBalance(Node n) { + return n == null ? 0 : height(n.getLeft()) - height(n.getRight()); } - if (node != null) { // make sure it isnt the 0-child case - rebalance(node); + /** + * Performs a right rotation on the specified node. + * Note that function should be called only if the + * node has a left child since it will be the + * new root. + * + * @param n node to perform right rotation on. + * @return the new root after rotation. + */ + private Node rotateRight(Node n) { + Node newRoot = n.getLeft(); + Node newLeftSub = newRoot.getRight(); + newRoot.setRight(n); + n.setLeft(newLeftSub); + + newRoot.setParent(n.getParent()); + n.setParent(newRoot); + + updateHeight(n); + updateHeight(newRoot); + return newRoot; } - return node; - } - - /** - * Find the left-most child of the (sub)tree rooted at a specified node - * @param n tree is rooted at this node - * @return left-most node - */ - private Node getMostLeft(Node n) { - if (n.left == null) { - return n; - } else { - return getMostLeft(n.left); + + /** + * Performs a left rotation on the specified node. + * Note that function should be called only if the + * node has a right child since it will be the + * new root. + * + * @param n node to perform left rotation on + * @return new root after rotation + */ + private Node rotateLeft(Node n) { + Node newRoot = n.getRight(); + Node newRightSub = newRoot.getLeft(); + newRoot.setLeft(n); + n.setRight(newRightSub); + + newRoot.setParent(n.getParent()); + n.setParent(newRoot); + + updateHeight(n); + updateHeight(newRoot); + return newRoot; } - } - private Node getMostRight(Node n) { - if (n.right == null) { - return n; - } else { - return getMostRight(n.right); + /** + * Rebalances a node in the tree based on balance factor. + * + * @param n node to be rebalanced + * @return new root after rebalancing + */ + private Node rebalance(Node n) { + updateHeight(n); + int balance = getBalance(n); + if (balance < -1) { // right-heavy case + if (height(n.getRight().getLeft()) > height(n.getRight().getRight())) { + n.setRight(rotateRight(n.getRight())); + } + n = rotateLeft(n); + } else if (balance > 1) { // left-heavy case + if (height(n.getLeft().getRight()) > height(n.getLeft().getLeft())) { + n.setLeft(rotateLeft(n.getLeft())); + } + n = rotateRight(n); + } + return n; } - } - - /** - * Find the key of the predecessor of a specified node that exists in the tree - * NOTE: the input node is assumed to be in the tree - * @param node node that exists in the tree - * @return key value; null if node has no predecessor - */ - private T predecessor(Node node) { - Node curr = node; - if (curr.left != null) { // has left-child - return getMostRight(curr.left).key; - } else { // so pred must be an ancestor - while (curr != null) { - if (curr.key.compareTo(node.key) < 0) { - return curr.key; - } - curr = curr.parent; - } + + /** + * Insert a key which will be wrapped in a node, into the tree rooted at a specified node. + * NOTE: ASSUMPTION THAT NO TWO NODES SHARE THE SAME KEY VALUE. + * + * @param node the (sub)tree rooted at node which the key will be inserted into + * @param key the key to insert + * @return the (new) node which the tree is rooted at after rebalancing + */ + private Node insert(Node node, T key) { + if (node == null) { + return new Node<>(key); + } else if (node.getKey().compareTo(key) < 0) { + node.setRight(insert(node.getRight(), key)); + node.getRight().setParent(node); + // note that insufficient to update parent in rotateLeft & rotateRight if still considered balanced + } else if (node.getKey().compareTo(key) > 0) { + node.setLeft(insert(node.getLeft(), key)); + node.getLeft().setParent(node); + } else { + throw new RuntimeException("Duplicate key not supported!"); + } + return rebalance(node); } - return null; - } - - /** - * Find the key of the successor of a specified node that exists in the tree - * NOTE: the input node is assumed to be in the tree - * @param node node that exists in the tree - * @return key value; null if node has no successor - */ - private T successor(Node node) { - Node curr = node; - if (curr.right != null) { // has right-child - return getMostLeft(curr.right).key; + + /** + * Delete a key from the avl tree rooted at a specified node. + * Find the node that holds the key and remove the node from the tree. + * + * @param node the (sub)tree rooted at node which the key will be deleted from + * @param key the key to remove + * @return the (new) root which the tree is rooted at after rebalancing + */ + private Node delete(Node node, T key) { + if (node == null) { + return null; + } else if (node.getKey().compareTo(key) < 0) { + node.setRight(delete(node.getRight(), key)); + } else if (node.getKey().compareTo(key) > 0) { + node.setLeft(delete(node.getLeft(), key)); + } else { + if (node.getLeft() == null || node.getRight() == null) { // case of 1 or 0 child + // node = node.left == null ? node.right : node.left; + if (node.getLeft() == null && node.getRight() == null) { + node = null; // 0-child case + } else if (node.getRight() == null) { + node.getLeft().setParent(node.getParent()); + node = node.getLeft(); + } else { + node.getRight().setParent(node.getParent()); + node = node.getRight(); + } + } else { // 2-children case + Node successor = getMostLeft(node.getRight()); + node.setKey(successor.getKey()); + // since this is a 2-children case, successor of deleted node have + // at most one child; right-child (else it would continue going left) + node.setRight(delete(node.getRight(), successor.getKey())); + } + } + + if (node != null) { // make sure it isnt the 0-child case + rebalance(node); + } + return node; } - while (curr != null) { - if (curr.key.compareTo(node.key) > 0) { - return curr.key; - } - curr = curr.parent; + + /** + * Find the left-most child of the (sub)tree rooted at a specified node + * + * @param n tree is rooted at this node + * @return left-most node + */ + private Node getMostLeft(Node n) { + if (n.getLeft() == null) { + return n; + } else { + return getMostLeft(n.getLeft()); + } + } + + private Node getMostRight(Node n) { + if (n.getRight() == null) { + return n; + } else { + return getMostRight(n.getRight()); + } } - return null; - } - - /** - * Prints out in-order traversal of tree rooted at node - * @param node node which the tree is rooted at - */ - private void printInorder(Node node) { - if (node == null) { - return; + + /** + * Find the key of the predecessor of a specified node that exists in the tree + * NOTE: the input node is assumed to be in the tree + * + * @param node node that exists in the tree + * @return key value; null if node has no predecessor + */ + private T predecessor(Node node) { + Node curr = node; + if (curr.getLeft() != null) { // has left-child + return getMostRight(curr.getLeft()).getKey(); + } else { // so pred must be an ancestor + while (curr != null) { + if (curr.getKey().compareTo(node.getKey()) < 0) { + return curr.getKey(); + } + curr = curr.getParent(); + } + } + return null; } - if (node.left != null) { - printInorder(node.left); - } - System.out.print(node.toString() + " "); - if (node.right != null) { - printInorder(node.right); + + /** + * Find the key of the successor of a specified node that exists in the tree + * NOTE: the input node is assumed to be in the tree + * + * @param node node that exists in the tree + * @return key value; null if node has no successor + */ + private T successor(Node node) { + Node curr = node; + if (curr.getRight() != null) { // has right-child + return getMostLeft(curr.getRight()).getKey(); + } + while (curr != null) { + if (curr.getKey().compareTo(node.getKey()) > 0) { + return curr.getKey(); + } + curr = curr.getParent(); + } + return null; + } + + /** + * Prints out in-order traversal of tree rooted at node + * + * @param node node which the tree is rooted at + */ + private void printInorder(Node node) { + if (node == null) { + return; + } + if (node.getLeft() != null) { + printInorder(node.getLeft()); + } + System.out.print(node + " "); + if (node.getRight() != null) { + printInorder(node.getRight()); + } } - } - - /** - * Prints out pre-order traversal of tree rooted at node - * @param node node which the tree is rooted at - */ - private void printPreorder(Node node) { - if (node == null) { - return; + + /** + * Prints out pre-order traversal of tree rooted at node + * + * @param node node which the tree is rooted at + */ + private void printPreorder(Node node) { + if (node == null) { + return; + } + System.out.print(node + " "); + if (node.getLeft() != null) { + printPreorder(node.getLeft()); + } + if (node.getRight() != null) { + printPreorder(node.getRight()); + } } - System.out.print(node.toString() + " "); - if (node.left != null) { - printPreorder(node.left); + + /** + * Prints out post-order traversal of tree rooted at node + * + * @param node node which the tree is rooted at + */ + private void printPostorder(Node node) { + if (node.getLeft() != null) { + printPostorder(node.getLeft()); + } + if (node.getRight() != null) { + printPostorder(node.getRight()); + } + System.out.print(node + " "); } - if (node.right != null) { - printPreorder(node.right); + + /** + * Prints out level-order traversal of tree rooted at node + * + * @param node node which the tree is rooted at + */ + private void printLevelorder(Node node) { + if (node == null) { + return; + } + Queue> q = new LinkedList<>(); + q.add(node); + while (!q.isEmpty()) { + Node curr = q.poll(); + System.out.print(curr.toString() + " "); + if (curr.getLeft() != null) { + q.add(curr.getLeft()); + } + if (curr.getRight() != null) { + q.add(curr.getRight()); + } + } } - } - - /** - * Prints out post-order traversal of tree rooted at node - * @param node node which the tree is rooted at - */ - private void printPostorder(Node node) { - if (node.left != null) { - printPostorder(node.left); + + /** + * Get root of tree. + * + * @return root + */ + public Node root() { + return root; } - if (node.right != null) { - printPostorder(node.right); + + /** + * Inserts a key into the tree + * + * @param key to be inserted + */ + public void insert(T key) { + root = insert(root, key); } - System.out.print(node.toString() + " "); - } - - /** - * Prints out level-order traversal of tree rooted at node - * @param node node which the tree is rooted at - */ - private void printLevelorder(Node node) { - if (node == null) { - return; + + /** + * Removes a key from the tree, if it exists + * + * @param key to be removed + */ + public void delete(T key) { + root = delete(root, key); } - Queue> q = new LinkedList<>(); - q.add(node); - while (!q.isEmpty()) { - Node curr = q.poll(); - System.out.print(curr.toString() + " "); - if (curr.left != null) { - q.add(curr.left); - } - if (curr.right != null) { - q.add(curr.right); - } + + /** + * Search for a node with the specified key. + * + * @param key the key to look for + * @return node that has the specified key; null if not found + */ + public Node search(T key) { + Node curr = root; + while (curr != null) { + if (curr.getKey().compareTo(key) < 0) { + curr = curr.getRight(); + } else if (curr.getKey().compareTo(key) > 0) { + curr = curr.getLeft(); + } else { + return curr; + } + } + return null; } - return; - } - - /** - * Get root of tree. - * @return root - */ - public Node root() { - return root; - } - - /** - * Inserts a key into the tree - * @param key to be inserted - */ - public void insert(T key) { - root = insert(root, key); - } - - /** - * Removes a key from the tree, if it exists - * @param key to be removed - */ - public void delete(T key) { - root = delete(root, key); - } - - /** - * Search for a node with the specified key. - * @param key the key to look for - * @return node that has the specified key; null if not found - */ - public Node search(T key) { - Node curr = root; - while (curr != null) { - if (curr.key.compareTo(key) < 0) { - curr = curr.right; - } else if (curr.key.compareTo(key) > 0) { - curr = curr.left; - } else { - return curr; - } + + /** + * Get height of node that holds the specified key + * + * @param key the key value of the node whose height is to be found + * @return int value representing height + */ + public int height(T key) { + return height(search(key)); } - return null; - } - - /** - * Get height of node that holds the specified key - * @param key the key value of the node whose height is to be found - * @return int value representing height - */ - public int height(T key) { - return height(search(key)); - } - - /** - * Search for the predecessor of a given key. - * @param key find predecessor of this key - * @return generic type value; null if key has no predecessor - */ - public T predecessor(T key) { - Node curr = root; - while (curr != null) { - if (curr.key.compareTo(key) == 0) { - break; - } else if (curr.key.compareTo(key) < 0) { - if (curr.right == null) { - break; - } - curr = curr.right; - } else { - if (curr.left == null) { - break; + + /** + * Search for the predecessor of a given key. + * + * @param key find predecessor of this key + * @return generic type value; null if key has no predecessor + */ + public T predecessor(T key) { + Node curr = root; + while (curr != null) { + if (curr.getKey().compareTo(key) == 0) { + break; + } else if (curr.getKey().compareTo(key) < 0) { + if (curr.getRight() == null) { + break; + } + curr = curr.getRight(); + } else { + if (curr.getLeft() == null) { + break; + } + curr = curr.getLeft(); + } + } + if (curr.getKey().compareTo(key) < 0) { // we are done + return curr.getKey(); } - curr = curr.left; - } + return predecessor(curr); // pred could be an ancestor or child of curr node and hence handled separately } - if (curr.key.compareTo(key) < 0) { // we are done - return curr.key; - } - return predecessor(curr); // pred could be an ancestor or child of curr node and hence handled separately - } - - /** - * Search for the successor of a given key. - * @param key find successor of this key - * @return generic type value; null if key has no successor - */ - public T successor(T key) { - Node curr = root; - while (curr != null) { - if (curr.key.compareTo(key) == 0) { - break; - } else if (curr.key.compareTo(key) < 0) { - if (curr.right == null) { - break; + + /** + * Search for the successor of a given key. + * + * @param key find successor of this key + * @return generic type value; null if key has no successor + */ + public T successor(T key) { + Node curr = root; + while (curr != null) { + if (curr.getKey().compareTo(key) == 0) { + break; + } else if (curr.getKey().compareTo(key) < 0) { + if (curr.getRight() == null) { + break; + } + curr = curr.getRight(); + } else { + if (curr.getLeft() == null) { + break; + } + curr = curr.getLeft(); + } } - curr = curr.right; - } else { - if (curr.left == null) { - break; + if (curr.getKey().compareTo(key) > 0) { // we are done + return curr.getKey(); } - curr = curr.left; - } + return successor(curr); // same exp as in the pred fn } - if (curr.key.compareTo(key) > 0) { // we are done - return curr.key; + + /** + * prints in order traversal of the entire tree. + */ + public void printInorder() { + System.out.print("In-order: "); + printInorder(root); + System.out.println(); + } + + /** + * prints pre-order traversal of the entire tree + */ + public void printPreorder() { + System.out.print("Pre-order: "); + printPreorder(root); + System.out.println(); + } + + /** + * prints post-order traversal of the entire tree + */ + public void printPostorder() { + System.out.print("Post-order: "); + printPostorder(root); + System.out.println(); + } + + /** + * prints level-order traversal of the entire tree + */ + public void printLevelorder() { + System.out.print("Level-order: "); + printLevelorder(root); + System.out.println(); } - return successor(curr); // same exp as in the pred fn - } - - /** - * prints in order traversal of the entire tree. - */ - public void printInorder() { - System.out.print("In-order: "); - printInorder(root); - System.out.println(); - } - - /** - * prints pre-order traversal of the entire tree - */ - public void printPreorder() { - System.out.print("Pre-order: "); - printPreorder(root); - System.out.println(); - } - - /** - * prints post-order traversal of the entire tree - */ - public void printPostorder() { - System.out.print("Post-order: "); - printPostorder(root); - System.out.println(); - } - - /** - * prints level-order traversal of the entire tree - */ - public void printLevelorder() { - System.out.print("Level-order: "); - printLevelorder(root); - System.out.println(); - } } diff --git a/src/main/java/dataStructures/avlTree/Node.java b/src/main/java/dataStructures/avlTree/Node.java index 1709f43d..36228a51 100644 --- a/src/main/java/dataStructures/avlTree/Node.java +++ b/src/main/java/dataStructures/avlTree/Node.java @@ -2,34 +2,75 @@ /** * TreeNode implementation for AVL Tree. - * Note: Properties should rightfully be kept private + * Note: Properties should rightfully be kept private * and accessed/modified via public getters/setters. * But it was left as such to avoid clutter. + * * @param generic type of objects to be stored in the tree; must be comparable */ -public class Node > { - public T key; - public Node left; - public Node right; - public Node parent; - public int height; - /* - * Can insert more properties here. - * If key is not unique, introduce a value property - * so when nodes are being compared, a distinction - * can be made - */ - - public Node(T key) { - this.key = key; - } - - public boolean isLeaf() { - return this.left == null && this.right == null; - } - - @Override - public String toString() { - return key.toString(); - } +public class Node> { + private T key; + private Node left; + private Node right; + private Node parent; + private int height; + /* + * Can insert more properties here. + * If key is not unique, introduce a value property + * so when nodes are being compared, a distinction + * can be made + */ + + public Node(T key) { + this.key = key; + } + + public boolean isLeaf() { + return this.left == null && this.right == null; + } + + public T getKey() { + return key; + } + + public void setKey(T key) { + this.key = key; + } + + public Node getLeft() { + return left; + } + + public void setLeft(Node left) { + this.left = left; + } + + public Node getRight() { + return right; + } + + public void setRight(Node right) { + this.right = right; + } + + public Node getParent() { + return parent; + } + + public void setParent(Node parent) { + this.parent = parent; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + @Override + public String toString() { + return key.toString(); + } } diff --git a/src/main/java/dataStructures/binarySearchTree/BinarySearchTree.java b/src/main/java/dataStructures/binarySearchTree/BinarySearchTree.java index 8313ca80..013a91ab 100644 --- a/src/main/java/dataStructures/binarySearchTree/BinarySearchTree.java +++ b/src/main/java/dataStructures/binarySearchTree/BinarySearchTree.java @@ -1,23 +1,27 @@ package dataStructures.binarySearchTree; -import java.util.*; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; /** * Implementation of Binary Search Tree. + * * @param generic type of object to be stored; must be comparable - * client methods: - * root() - * insert(T key) - * delete(T key) - * search(T key) - * predecessor(T key) - * successor(T key) - * searchMin() - * searchMax() - * getInorder() - * getPreorder() - * getPostorder() - * getLevelorder() + * client methods: + * root() + * insert(T key) + * delete(T key) + * search(T key) + * predecessor(T key) + * successor(T key) + * searchMin() + * searchMax() + * getInorder() + * getPreorder() + * getPostorder() + * getLevelorder() */ public class BinarySearchTree, V> { @@ -26,22 +30,23 @@ public class BinarySearchTree, V> { /** * Insert a key-value pair into the tree rooted at a specified node. * NOTE: ASSUMPTION THAT NO TWO NODES SHARE THE SAME KEY VALUE. - * @param node the (sub)tree rooted at node which the key will be inserted into - * @param key the key to insert + * + * @param node the (sub)tree rooted at node which the key will be inserted into + * @param key the key to insert * @param value the value tied to the key to insert */ private void insert(Node node, T key, V value) { - if (node.key.compareTo(key) < 0) { - if (node.right == null) { - node.right = new Node<>(key, value); + if (node.getKey().compareTo(key) < 0) { + if (node.getRight() == null) { + node.setRight(new Node<>(key, value)); } else { - insert(node.right, key, value); + insert(node.getRight(), key, value); } - } else if (node.key.compareTo(key) > 0) { - if (node.left == null) { - node.left = new Node<>(key, value); + } else if (node.getKey().compareTo(key) > 0) { + if (node.getLeft() == null) { + node.setLeft(new Node<>(key, value)); } else { - insert(node.left, key, value); + insert(node.getLeft(), key, value); } } else { throw new RuntimeException("Duplicate key not supported!"); @@ -51,45 +56,46 @@ private void insert(Node node, T key, V value) { /** * Delete a key from the binary search tree rooted at a specified node. * Find the node that holds the key and remove the node from the tree. + * * @param node the (sub)tree rooted at node which the key will be deleted from - * @param key the key to remove + * @param key the key to remove * @return the (new) root which the tree is rooted at after rebalancing */ private Node delete(Node node, T key) { - if (node.key.compareTo(key) < 0) { // key > current node - if (node.right == null) { + if (node.getKey().compareTo(key) < 0) { // key > current node + if (node.getRight() == null) { throw new RuntimeException("Key does not exist!"); } else { - node.right = delete(node.right, key); + node.setRight(delete(node.getRight(), key)); } - } else if (node.key.compareTo(key) > 0) { // key < current node - if (node.left == null) { + } else if (node.getKey().compareTo(key) > 0) { // key < current node + if (node.getLeft() == null) { throw new RuntimeException("Key does not exist!"); } else { - node.left = delete(node.left, key); + node.setLeft(delete(node.getLeft(), key)); } } else { // key == current node - if (node.left == null && node.right == null) { // 0 child case + if (node.getLeft() == null && node.getRight() == null) { // 0 child case node = null; - } else if (node.left == null || node.right == null) { // 1 child case - if (node.right != null) { - node.right.parent = node.parent; - return node.right; + } else if (node.getLeft() == null || node.getRight() == null) { // 1 child case + if (node.getRight() != null) { + node.getRight().setParent(node.getParent()); + return node.getRight(); } else { - node.left.parent = node.parent; - return node.left; + node.getLeft().setParent(node.getParent()); + return node.getLeft(); } } else { // 2-children case T successorKey = successor(key); Node successor = search(successorKey); // replaces the current node with successor - node.key = successor.key; - node.value = successor.value; + node.setKey(successor.getKey()); + node.setValue(successor.getValue()); // delete the original successor // successor will definitely be in right subtree of current node and not an ancestor - node.right = delete(node.right, successor.key); + node.setRight(delete(node.getRight(), successor.getKey())); } } @@ -101,13 +107,13 @@ private Node delete(Node node, T key) { * * @param n the (sub)tree rooted at node which the minimum key will be searched for * @return node with the minimum key - * NOTE: ASSUMPTION THAT TREE IS NOT EMPTY + * NOTE: ASSUMPTION THAT TREE IS NOT EMPTY */ private Node searchMin(Node n) { - if (n.left == null) { + if (n.getLeft() == null) { return n; } else { - return searchMin(n.left); + return searchMin(n.getLeft()); } } @@ -116,13 +122,13 @@ private Node searchMin(Node n) { * * @param n the (sub)tree rooted at node which the maximum key will be searched for * @return node with the maximum key - * NOTE: ASSUMPTION THAT TREE IS NOT EMPTY + * NOTE: ASSUMPTION THAT TREE IS NOT EMPTY */ private Node searchMax(Node n) { - if (n.right == null) { + if (n.getRight() == null) { return n; } else { - return searchMax(n.right); + return searchMax(n.getRight()); } } @@ -135,14 +141,14 @@ private Node searchMax(Node n) { */ private T predecessor(Node node) { Node curr = node; - if (curr.left != null) { // predecessor in children - return searchMax(curr.left).key; + if (curr.getLeft() != null) { // predecessor in children + return searchMax(curr.getLeft()).getKey(); } else { // predecessor in ancestor while (curr != null) { - if (curr.key.compareTo(node.key) < 0) { - return curr.key; + if (curr.getKey().compareTo(node.getKey()) < 0) { + return curr.getKey(); } - curr = curr.parent; + curr = curr.getParent(); } } return null; @@ -151,19 +157,20 @@ private T predecessor(Node node) { /** * Find the key of the successor of a specified node that exists in the tree * NOTE: the input node is assumed to be in the tree + * * @param node node that exists in the tree * @return key value; null if node has no successor */ private T successor(Node node) { Node curr = node; - if (curr.right != null) { // successor in children - return searchMin(curr.right).key; + if (curr.getRight() != null) { // successor in children + return searchMin(curr.getRight()).getKey(); } else { // successor in ancestor while (curr != null) { - if (curr.key.compareTo(node.key) > 0) { // finds the cloests - return curr.key; + if (curr.getKey().compareTo(node.getKey()) > 0) { // finds the closest + return curr.getKey(); } - curr = curr.parent; + curr = curr.getParent(); } } return null; @@ -179,14 +186,14 @@ private void getInorder(Node node, List result) { return; } - if (node.left != null) { - getInorder(node.left, result); + if (node.getLeft() != null) { + getInorder(node.getLeft(), result); } result.add(node.toString()); - if (node.right != null) { - getInorder(node.right, result); + if (node.getRight() != null) { + getInorder(node.getRight(), result); } } @@ -202,12 +209,12 @@ private void getPreorder(Node node, List result) { result.add(node.toString()); - if (node.left != null) { - getPreorder(node.left, result); + if (node.getLeft() != null) { + getPreorder(node.getLeft(), result); } - if (node.right != null) { - getPreorder(node.right, result); + if (node.getRight() != null) { + getPreorder(node.getRight(), result); } } @@ -221,12 +228,12 @@ private void getPostorder(Node node, List result) { return; } - if (node.left != null) { - getPostorder(node.left, result); + if (node.getLeft() != null) { + getPostorder(node.getLeft(), result); } - if (node.right != null) { - getPostorder(node.right, result); + if (node.getRight() != null) { + getPostorder(node.getRight(), result); } result.add(node.toString()); @@ -248,17 +255,18 @@ private void getLevelorder(Node node, List result) { Node current = queue.poll(); result.add(current.toString()); - if (current.left != null) { - queue.add(current.left); + if (current.getLeft() != null) { + queue.add(current.getLeft()); } - if (current.right != null) { - queue.add(current.right); + if (current.getRight() != null) { + queue.add(current.getRight()); } } } /** * Get root of tree. + * * @return root */ public Node root() { @@ -267,6 +275,7 @@ public Node root() { /** * Inserts a key into the tree + * * @param key to be inserted */ public void insert(T key, V value) { @@ -279,6 +288,7 @@ public void insert(T key, V value) { /** * Removes a key from the tree, if it exists + * * @param key to be removed */ public void delete(T key) { @@ -287,16 +297,17 @@ public void delete(T key) { /** * Search for a node with the specified key. + * * @param key the key to look for * @return node that has the specified key; null if not found */ public Node search(T key) { Node curr = root; while (curr != null) { - if (curr.key.compareTo(key) < 0) { - curr = curr.right; - } else if (curr.key.compareTo(key) > 0) { - curr = curr.left; + if (curr.getKey().compareTo(key) < 0) { + curr = curr.getRight(); + } else if (curr.getKey().compareTo(key) > 0) { + curr = curr.getLeft(); } else { return curr; } @@ -306,18 +317,19 @@ public Node search(T key) { /** * Search for the predecessor of a given key. + * * @param key find predecessor of this key * @return generic type value; null if key has no predecessor */ public T predecessor(T key) { Node curr = root; while (curr != null) { - if (curr.key.compareTo(key) == 0) { + if (curr.getKey().compareTo(key) == 0) { break; - } else if (curr.key.compareTo(key) < 0) { - curr = curr.right; + } else if (curr.getKey().compareTo(key) < 0) { + curr = curr.getRight(); } else { - curr = curr.left; + curr = curr.getLeft(); } } @@ -326,18 +338,19 @@ public T predecessor(T key) { /** * Search for the successor of a given key. + * * @param key find successor of this key * @return generic type value; null if key has no successor */ public T successor(T key) { Node curr = root; while (curr != null) { - if (curr.key.compareTo(key) == 0) { + if (curr.getKey().compareTo(key) == 0) { break; - } else if (curr.key.compareTo(key) < 0) { - curr = curr.right; + } else if (curr.getKey().compareTo(key) < 0) { + curr = curr.getRight(); } else { - curr = curr.left; + curr = curr.getLeft(); } } diff --git a/src/main/java/dataStructures/binarySearchTree/Node.java b/src/main/java/dataStructures/binarySearchTree/Node.java index 559d13b4..0f731543 100644 --- a/src/main/java/dataStructures/binarySearchTree/Node.java +++ b/src/main/java/dataStructures/binarySearchTree/Node.java @@ -2,36 +2,81 @@ /** * Node implementation for Binary Search Tree. - * Note: Properties should rightfully be kept private + * Note: Properties should rightfully be kept private * and accessed/modified via public getters/setters. * But it was left as such to avoid clutter. + * * @param generic type of objects to be stored in the tree; must be comparable */ -public class Node , V> { - public T key; - public Node left; - public Node right; - public Node parent; - public V value; - - /* - * Can insert more properties here. - * If key is not unique, introduce a value property - * so when nodes are being compared, a distinction - * can be made - */ - - public Node(T key, V value) { - this.key = key; - this.value = value; - } - - public boolean isLeaf() { - return this.left == null && this.right == null; - } - - @Override - public String toString() { - return "Key: " + key.toString() + ", Value: " + (value == null ? "null" : value.toString()); - } +public class Node, V> { + private T key; + private Node left; + private Node right; + private Node parent; + private V value; + + /** + * Constructor for a BST node. + * Can insert more properties here. + * If key is not unique, introduce a value property, so when nodes are being compared, a distinction can be made. + * @param key the key for the BST Node. + * @param value the value encapsulated by the BST node. + */ + public Node(T key, V value) { + this.key = key; + this.value = value; + } + + /** + * Determines whether the node is a leaf node. + * @return true if the node is a leaf node, false if not. + */ + public boolean isLeaf() { + return this.left == null && this.right == null; + } + + public T getKey() { + return key; + } + + public void setKey(T key) { + this.key = key; + } + + public Node getLeft() { + return left; + } + + public void setLeft(Node left) { + this.left = left; + } + + public Node getRight() { + return right; + } + + public void setRight(Node right) { + this.right = right; + } + + public Node getParent() { + return parent; + } + + public void setParent(Node parent) { + this.parent = parent; + } + + public V getValue() { + return value; + } + + public void setValue(V value) { + this.value = value; + } + + @Override + public String toString() { + return "Key: " + key.toString() + ", Value: " + (value == null ? "null" : value.toString()); + } } diff --git a/src/main/java/dataStructures/binarySearchTree/README.md b/src/main/java/dataStructures/binarySearchTree/README.md index 8c3457fa..b8368371 100644 --- a/src/main/java/dataStructures/binarySearchTree/README.md +++ b/src/main/java/dataStructures/binarySearchTree/README.md @@ -1,22 +1,25 @@ # Binary Search Tree ## Overview -A Binary Search Tree (BST) is a tree-based data structure in which each node has at most two children, referred to as -the left child and the right child. Each node in a BST contains a unique key and an associated value. The tree is + +A Binary Search Tree (BST) is a tree-based data structure in which each node has at most two children, referred to as +the left child and the right child. Each node in a BST contains a unique key and an associated value. The tree is structured so that, for any given node: 1. The left subtree contains nodes with keys less than the node's key. 2. The right subtree contains nodes with keys greater than the node's key. -This property makes BSTs efficient for operations like searching, as the average time complexity for many operations is +This property makes BSTs efficient for operations like searching, as the average time complexity for many operations is proportional to the tree's height. Note: in the following explanation a "smaller" node refers to a node with a smaller key and a "larger" node refers to a node with a larger key. ## Implementation + ### BinarySearchTree Class -The BinarySearchTree class is a generic implementation of a BST. It supports a variety of operations that allow + +The BinarySearchTree class is a generic implementation of a BST. It supports a variety of operations that allow interaction with the tree: - root(): Retrieve the root node of the tree. @@ -35,7 +38,8 @@ interaction with the tree: We will expand on the delete implementation due to its relative complexity. #### Delete Implementation Details -The delete operation is split into three different cases - when the node to be deleted has no children, one child or + +The delete operation is split into three different cases - when the node to be deleted has no children, one child or two children. **No children:** Simply delete the node. @@ -45,12 +49,14 @@ the binary search tree property as the right child will definitely be smaller th **Two children:** Replace the deleted node with its successor. This works because the binary search tree property is maintained: -1. the entire left subtree will definitely be smaller than the successor as the successor is larger than the deleted -node -2. the entire right subtree will definitely be larger than the successor as the successor will be the smallest node in -the right subtree + +1. the entire left subtree will definitely be smaller than the successor as the successor is larger than the deleted + node +2. the entire right subtree will definitely be larger than the successor as the successor will be the smallest node in + the right subtree ### Node + The Node class represents the nodes within the BinarySearchTree. Each Node instance contains: - key: The unique key associated with the node. @@ -60,8 +66,9 @@ The Node class represents the nodes within the BinarySearchTree. Each Node insta - parent: Reference to the parent node. ## Complexity Analysis + **Time Complexity:** For a balanced tree, most operations (insert, delete, search) can be performed in O(log n) time, -except tree traversal operations which can be performed in O(n) time. However, in the worst case (an unbalanced tree), +except tree traversal operations which can be performed in O(n) time. However, in the worst case (an unbalanced tree), these operations may degrade to O(n). -**Space Complexity:** O(n), where n is the number of elements in the tree. \ No newline at end of file +**Space Complexity:** O(n), where n is the number of elements in the tree. diff --git a/src/main/java/dataStructures/disjointSet/README.md b/src/main/java/dataStructures/disjointSet/README.md index 237f7f43..6f8234b1 100644 --- a/src/main/java/dataStructures/disjointSet/README.md +++ b/src/main/java/dataStructures/disjointSet/README.md @@ -1,47 +1,50 @@ # Union Find / Disjoint Set -A disjoint-set structure also known as a union-find or merge-find set, is a data structure -keeps track of a partition of a set into disjoint (non-overlapping) subsets. In CS2040s, this -is primarily used to check for dynamic connectivity. For instance, Kruskal's algorithm +A disjoint-set structure also known as a union-find or merge-find set, is a data structure +keeps track of a partition of a set into disjoint (non-overlapping) subsets. In CS2040s, this +is primarily used to check for dynamic connectivity. For instance, Kruskal's algorithm in graph theory to find minimum spanning tree of the graph utilizes disjoint set to efficiently query if there exists a path between 2 nodes.
It supports 2 main operations: + 1. Union: Join two subsets into a single subset 2. Find: Determine which subset a particular element is in. In practice, this is often done to check -if two elements are in the same subset or component. + if two elements are in the same subset or component. The Disjoint Set structure is often introduced in 3 parts, with each iteration being better than the previous in terms of time and space complexity. Below is a brief overview: ## Quick Find -Every object will be assigned a component identity. The implementation of Quick Find often involves + +Every object will be assigned a component identity. The implementation of Quick Find often involves an underlying array that tracks the component identity of each object. **Union**: Between the two components, decide on the component d, to represent the combined set. Let the other -component's identity be d'. Simply iterate over the component identifier array, and for any element with +component's identity be d'. Simply iterate over the component identifier array, and for any element with identity d', assign it to d. **Find**: Simply use the component identifier array to query for the component identity of the two elements -and check if they are equal. This is why this implementation is known as "Quick Find". +and check if they are equal. This is why this implementation is known as "Quick Find". #### Analysis + Let n be the number of elements in consideration. **Time**: O(n) for Union and O(1) for Find operations **Space**: O(n) auxiliary space for the component identifier - ## Quick Union + Here, we consider a completely different approach. We consider the use of trees. Every element can be -thought of as a tree node and starts off in its own component. Under this representation, it is likely +thought of as a tree node and starts off in its own component. Under this representation, it is likely that at any given point, we might have a forest of trees, and that's perfectly fine. The root node of each tree simply represents the component / set of all elements in the same set.
Note that the trees here are not necessarily binary trees. In fact, more often than not, we will have nodes with multiple children nodes. **Union**: Between the two components, decide on the component to represent the combined set as before. -Now, union is simply assigning the root node of one tree to be the child of the root node of another. Hence, its name. +Now, union is simply assigning the root node of one tree to be the child of the root node of another. Hence, its name. One thing to note is that to identify the component of the object involves traversing to the root node of the tree. @@ -49,34 +52,37 @@ tree. two roots are the same #### Analysis + **Time**: O(n) for Union and Find operations. While union-ing is indeed quick, it is possibly undermined by O(n) traversal in the case of a degenerate tree. Note that at this stage, there is nothing to ensure the trees are balanced. **Space**: O(n), implementation still involves wrapping the n elements with some structure / wrapper. - ## Weighted Union + Now, we augment and improve upon the Quick Union structure by ensuring trees constructed are 'balanced'. Balanced -trees have a nice property that the height of the tree will be upper-bounded by O(log(n)). This considerably speeds +trees have a nice property that the height of the tree will be upper-bounded by O(log(n)). This considerably speeds up Union operations.
We additionally track the size of each tree and ensure that whenever there is a union between 2 elements, the smaller tree will be the child of a larger tree. It can be mathematically shown the height of the tree is bounded by O(log(n)). #### Analysis + **Time**: O(log(n)) for Union and Find operations. **Space**: Remains at O(n) - ### Path Compression + We can further improve on the time complexity of Weighted Union by introducing path compression. Specifically, during -the traversal of a node up to the root, we re-assign each node's parent to be the root (or as shown in CS2040s, +the traversal of a node up to the root, we re-assign each node's parent to be the root (or as shown in CS2040s, assigning to its grandparent actually suffice and yield the same big-O upper-bound! This allows path compression to be done in a single pass.). By doing so, we greatly reduce the height of the trees formed. #### Analysis -The analysis is a bit trickier here and talks about the inverse-Ackermann function. Interested readers can find out more + +The analysis is a bit trickier here and talks about the inverse-Ackermann function. Interested readers can find out more [here](https://dl.acm.org/doi/pdf/10.1145/321879.321884) **Time**: O(alpha) diff --git a/src/main/java/dataStructures/disjointSet/quickFind/generalised/QuickFind.java b/src/main/java/dataStructures/disjointSet/quickFind/generalised/QuickFind.java index c7986c6b..cd75ab6c 100644 --- a/src/main/java/dataStructures/disjointSet/quickFind/generalised/QuickFind.java +++ b/src/main/java/dataStructures/disjointSet/quickFind/generalised/QuickFind.java @@ -5,20 +5,29 @@ /** * Implementation of quick-find structure; Turns a list of objects into a data structure that supports union operations + * * @param generic type of object to be stored */ public class QuickFind { - private List objects; - private List identity; + private final List objects; + private final List identity; + /** + * TODO documentation + */ public QuickFind() { - this.objects = new ArrayList(); - this.identity = new ArrayList(); + this.objects = new ArrayList<>(); + this.identity = new ArrayList<>(); } + /** + * TODO documentation + * + * @param input + */ public QuickFind(List input) { this.objects = input; - this.identity = new ArrayList(); + this.identity = new ArrayList<>(); for (int i = 0; i < input.size(); i++) { this.identity.add(i); } @@ -26,6 +35,7 @@ public QuickFind(List input) { /** * Adds a new item into the existing list. + * * @param item to be added */ public void add(T item) { @@ -35,6 +45,7 @@ public void add(T item) { /** * Merges the objects in two different components identified by a member from each. + * * @param objOne object in one of the components * @param objTwo object in another component */ @@ -58,6 +69,7 @@ public void union(T objOne, T objTwo) { /** * Retrieves all elements in the same component as the given object. + * * @param objOne * @return list of elements. */ diff --git a/src/main/java/dataStructures/disjointSet/quickFind/simplified/QuickFind.java b/src/main/java/dataStructures/disjointSet/quickFind/simplified/QuickFind.java index 9324f25e..bb58478c 100644 --- a/src/main/java/dataStructures/disjointSet/quickFind/simplified/QuickFind.java +++ b/src/main/java/dataStructures/disjointSet/quickFind/simplified/QuickFind.java @@ -1,15 +1,20 @@ package dataStructures.disjointSet.quickFind.simplified; -import java.util.List; import java.util.ArrayList; +import java.util.List; /** * Simplified implementation of quick-find where the objects are integers range from 1-n. */ public class QuickFind { - private int[] identity; - private int size; + private final int[] identity; + private final int size; + /** + * TODO documentation + * + * @param size + */ public QuickFind(int size) { // we will ignore index 0. So index 1 corresponds to element 1, index 2 corresponds with element 2 and so on. this.identity = new int[size + 1]; @@ -20,7 +25,9 @@ public QuickFind(int size) { } /** - * Forms a union between elements of two different groups merging all the elements with the same identity as the former element to that of the latter element. + * Forms a union between elements of two different groups merging all the elements with the same identity as + * the former element to that of the latter element. + * * @param fr identity of the first element * @param to identity of the second element */ @@ -35,6 +42,7 @@ public void union(int fr, int to) { /** * Retrieves all the elements in the component whose identity is the same as that of the given element. + * * @param element whose component we would lie to find. * @return all elements in the component */ diff --git a/src/main/java/dataStructures/disjointSet/weightedUnion/Union.java b/src/main/java/dataStructures/disjointSet/weightedUnion/Union.java index 386d317f..411835df 100644 --- a/src/main/java/dataStructures/disjointSet/weightedUnion/Union.java +++ b/src/main/java/dataStructures/disjointSet/weightedUnion/Union.java @@ -1,29 +1,45 @@ package dataStructures.disjointSet.weightedUnion; + import java.util.HashMap; -import java.util.Map; import java.util.List; +import java.util.Map; /** - * Implementation of weighted-union algorithm; Turns a list of objects into a data structure that supports union operations. + * Implementation of weighted-union algorithm; Turns a list of objects into a data structure that supports + * union operations. * Note that the commented out section below incorporates path compression to improve time complexity. + * * @param generic type of object to be stored - * Time: O(logn) without path compression. - * O(a) with path compression where a is the inverse Ackermann function. + * Time: O(logn) without path compression. + * O(a) with path compression where a is the inverse Ackermann function. */ public class Union { - Map> items; + private final Map> items; + /** + * TODO documentation + * + * @param items + */ public Union(List items) { this.items = new HashMap<>(); for (T item : items) { - this.items.put(item, new Node(item)); + this.items.put(item, new Node<>(item)); } } + /** + * TODO documentation + */ public Union() { this.items = new HashMap<>(); } + /** + * TOOD documentation + * + * @param item + */ public void add(T item) { if (items.containsKey(item)) { System.out.println("Item already exists!"); @@ -31,6 +47,12 @@ public void add(T item) { this.items.put(item, new Node(item)); } + /** + * TODO documentation + * + * @param obj1 + * @param obj2 + */ public void combine(T obj1, T obj2) { Node node1 = items.get(obj1); Node node2 = items.get(obj2); @@ -39,7 +61,7 @@ public void combine(T obj1, T obj2) { Node p2 = node2.findParent(); if (p1 == p2) { - return; // already combined + // already combined } else if (p1.getSize() > p2.getSize()) { p1.makeChild(p2); } else { @@ -47,6 +69,13 @@ public void combine(T obj1, T obj2) { } } + /** + * TODO documentation + * + * @param obj1 + * @param obj2 + * @return + */ public boolean isSameComponent(T obj1, T obj2) { if (!items.containsKey(obj1) || !items.containsKey(obj2)) { System.out.println("One or both of the items are not tracked."); @@ -56,37 +85,35 @@ public boolean isSameComponent(T obj1, T obj2) { } private static class Node { - private T val; + private final T val; private int size; private Node parent; - + Node(T val) { this.val = val; this.size = 1; this.parent = this; } - + Node findParent() { Node trav = this; while (trav.parent != trav) { Node curr = trav; trav = trav.parent; // start of path compression - /** - * NOTE: we can correctly update the size of each subtree rooted at a node - * after each compression for consistency's sake, - * but doing so does not affect correctness of algorithm. - * - * Because all the algorithm needs is the correct size of each subtree to decide on how to union - * and shifting descendants around does not affect size of subtree! - * trav.size -= curr.size - */ + // NOTE: we can correctly update the size of each subtree rooted at a node + // after each compression for consistency's sake, + // but doing so does not affect correctness of algorithm. + // + // Because all the algorithm needs is the correct size of each subtree to decide on how to union + // and shifting descendants around does not affect size of subtree! + // trav.size -= curr.size curr.parent = curr.parent.parent; // end } return trav; } - + void makeChild(Node child) { child.parent = this; this.size += child.size; diff --git a/src/main/java/dataStructures/hashSet/chaining/HashSet.java b/src/main/java/dataStructures/hashSet/chaining/HashSet.java index 419e5256..3fadc212 100644 --- a/src/main/java/dataStructures/hashSet/chaining/HashSet.java +++ b/src/main/java/dataStructures/hashSet/chaining/HashSet.java @@ -1,32 +1,34 @@ package dataStructures.hashSet.chaining; -import dataStructures.linkedList.LinkedList; - import java.util.ArrayList; import java.util.List; +import dataStructures.linkedList.LinkedList; + /** * Implementation of a HashSet that uses chaining to resolve collisions. * - *

A HashSet is a data structure that provides expected constant-time complexity for basic operations such as add, remove, and contains. - * It achieves this performance by leveraging the Simple Uniform Hashing Assumption (SUHA), where elements are uniformly distributed across - * the available hash buckets. The hash function maps elements to their respective buckets, allowing efficient insertion, retrieval, - * and removal of elements from the set. + *

A HashSet is a data structure that provides expected constant-time complexity for basic operations such as add, + * remove, and contains. + * It achieves this performance by leveraging the Simple Uniform Hashing Assumption (SUHA), where elements are + * uniformly distributed across the available hash buckets. The hash function maps elements to their respective + * buckets, allowing efficient insertion, retrieval, and removal of elements from the set. * - *

This approach is inspired by the need to efficiently store and retrieve elements from a large universe of possible keys, - * where the number of actual keys is relatively small. - * Chaining is used to handle collisions, where multiple elements are mapped to the same bucket, by maintaining a linked list - * of elements within each bucket. + *

This approach is inspired by the need to efficiently store and retrieve elements from a large universe of possible + * keys, where the number of actual keys is relatively small. + * Chaining is used to handle collisions, where multiple elements are mapped to the same bucket, by maintaining a + * linked list of elements within each bucket. * - *

The expected load, or load factor, for this approach is an important consideration. The load factor is defined as the ratio - * of the number of elements in the set to the number of buckets (n/m). A higher load factor increases the likelihood of collisions, - * leading to longer linked lists and potentially degrading performance. To maintain optimal time complexity, the hash function - * should aim for an equal distribution of elements across the buckets, avoiding clustering and ensuring efficient access to elements. + *

The expected load, or load factor, for this approach is an important consideration. The load factor is defined as + * the ratio of the number of elements in the set to the number of buckets (n/m). A higher load factor increases the + * likelihood of collisions, leading to longer linked lists and potentially degrading performance. To maintain optimal + * time complexity, the hash function should aim for an equal distribution of elements across the buckets, avoiding + * clustering and ensuring efficient access to elements. * - *

The time complexity of operations in this HashSet implementation consists of two components. Firstly, there is the time to - * compute the hash value, which is typically a constant-time operation. Secondly, there is the time to access the corresponding - * bucket, which involves traversing the linked list in case of collisions. On average, these operations have a constant-time - * complexity. + *

The time complexity of operations in this HashSet implementation consists of two components. Firstly, there is + * the time to compute the hash value, which is typically a constant-time operation. Secondly, there is the time to + * access the corresponding bucket, which involves traversing the linked list in case of collisions. On average, + * these operations have a constant-time complexity. * *

Public methods (along with their time-complexity): * int size() gets the number of elements (cardinality) in this HashSet. O(1). @@ -34,10 +36,10 @@ * boolean add(T element) adds the given element into the HashSet. Expected O(1) assuming SUHA. * boolean contains(T element) checks if the given element is present in the HashSet. Expected O(1) assuming SUHA. * boolean remove(T element) removes the given element in the HashSet. Expected O(1) assuming SUHA. - * List toList() returns a List representation of this HashSet. O(n). + * List toList() returns a List representation of this HashSet. O(n). * * @param the type of objects that are contained within this HashSet. T must override - * Object::equals and Object::hashCode for the methods add, remove, and contains to be well-defined. + * Object::equals and Object::hashCode for the methods add, remove, and contains to be well-defined. */ public class HashSet> { private static final int NUMBER_OF_BUCKETS = 256; // Arbitrary number of buckets. @@ -82,6 +84,7 @@ public boolean isEmpty() { * Simple hash function to hash the element into their respective bucket. * Currently uses the division method (k % m). * T must override both Object::equals and Object::hashCode. + * * @param element the specified element to be hashed. * @return the bucket to add the element to. */ @@ -98,7 +101,7 @@ private int hashFunction(T element) { * * @param element the element to be added to this set * @return true if this set did not already contain the specified - * element + * element */ public boolean add(T element) { int bucket = this.hashFunction(element); @@ -145,6 +148,11 @@ public boolean remove(T element) { return true; } + /** + * Returns the elements of this Hash Set in a List representation. + * + * @return a List containing the elements in this set. + */ public List toList() { List outputList = new ArrayList<>(); for (LinkedList bucket : this.buckets) { diff --git a/src/main/java/dataStructures/hashSet/openAddressing/HashSet.java b/src/main/java/dataStructures/hashSet/openAddressing/HashSet.java index 2cdcf47b..b71f02f0 100644 --- a/src/main/java/dataStructures/hashSet/openAddressing/HashSet.java +++ b/src/main/java/dataStructures/hashSet/openAddressing/HashSet.java @@ -8,29 +8,30 @@ /** * Implementation of a HashSet that uses Open Addressing and linear probing to resolve collisions. * - *

The time complexity of operations in this HashSet implementation consists of two components. Firstly, there is the time to - * compute the hash value, which is typically a constant-time operation. Secondly, there is the time to access the corresponding - * bucket, which involves probing the buckets using linear probing. + *

The time complexity of operations in this HashSet implementation consists of two components. Firstly, + * there is the time to compute the hash value, which is typically a constant-time operation. + * Secondly, there is the time to access the corresponding bucket, which involves probing the buckets using + * linear probing. * *

Public methods (along with their time-complexity): * boolean add(T element) adds the given element into the HashSet. Expected O(1) assuming SUHA. * boolean contains(T element) checks if the given element is present in the HashSet. Expected O(1) assuming SUHA. * boolean remove(T element) removes the given element in the HashSet. Expected O(1) assuming SUHA. - * List toList() returns a List representation of this HashSet. O(n). + * List toList() returns a List representation of this HashSet. O(n). * int size() gets the number of elements (cardinality) in this HashSet. O(1). * boolean isEmpty() checks if the HashSet is empty. O(1). * int capacity() returns the capacity of this HashSet. O(1). * * @param the type of objects that are contained within this HashSet. T must override - * Object::equals and Object::hashCode for the methods add, remove, and contains to be well-defined. + * Object::equals and Object::hashCode for the methods add, remove, and contains to be well-defined. */ -public class HashSet{ - private final int INITIAL_CAPACITY = 16; // Initial capacity of the hash set. - private final double LOAD_FACTOR = 0.75; // Load factor threshold for resizing. - private final int ELEMENT_NOT_FOUND = -1; +public class HashSet { + private static final int INITIAL_CAPACITY = 16; // Initial capacity of the hash set. + private static final double LOAD_FACTOR = 0.75; // Load factor threshold for resizing. + private static final int ELEMENT_NOT_FOUND = -1; private int size; // Number of elements present in the Set (its cardinality). private T[] buckets; - private final T TOMBSTONE; + private final T tombstone; /** * Creates a HashSet with an initial capacity of 16. @@ -46,20 +47,20 @@ public HashSet() { // There is no way to retrieve an instance of Tombstone. Therefore, it is safe to cast Tombstone to T. @SuppressWarnings("unchecked") T tempVar = (T) Tombstone.TOMBSTONE; - this.TOMBSTONE = tempVar; + this.tombstone = tempVar; } /** * Adds the specified element to this set if it is not already present * If this set already contains the element, the call leaves the set unchanged and returns false. *

- * If load factor (0.75) is exceeded, triggers a resize operation and double the current capacity. - * It's important to note that resizing is not performed with every add operation but rather when the load - * factor exceeds the threshold. Therefore, the amortized time complexity of adding elements remains O(1) + * If load factor (0.75) is exceeded, triggers a resize operation and double the current capacity. + * It's important to note that resizing is not performed with every add operation but rather when the load + * factor exceeds the threshold. Therefore, the amortized time complexity of adding elements remains O(1) * * @param element the element to be added to this set * @return true if this set did not already contain the specified - * element + * element */ public boolean add(T element) { if (this.contains(element)) { @@ -71,7 +72,8 @@ public boolean add(T element) { } int bucketIndex = this.linearProbe(element); - if (!this.isEmptyBucket(bucketIndex)) { // probe function returns the index of an empty bucket or the index containing the element. + if (!this.isEmptyBucket( + bucketIndex)) { // probe function returns the index of an empty bucket or the index containing the element. return false; // Duplicate elements are not added to the set. } this.buckets[bucketIndex] = element; @@ -83,14 +85,14 @@ public boolean add(T element) { * Removes the specified element from this set if it is present. Returns true if this set * contained the element (or equivalently, if this set changed as a result of the call). * (This set will not contain the element once the call returns.) - *

- * Removed elements are replaced with a Tombstone instead of NULL. This is to prevent search from terminating earlier - * than expected when looking for an element. *

- * If load factor falls below 0.25, trigger a resize and halve the current capacity. - * It's important to note that resizing is not performed with every remove operation but rather when the - * load factor falls below a certain limit. Therefore, the amortized time complexity of removing elements - * remains O(1) + * Removed elements are replaced with a Tombstone instead of NULL. This is to prevent search from terminating + * earlier than expected when looking for an element. + *

+ * If load factor falls below 0.25, trigger a resize and halve the current capacity. + * It's important to note that resizing is not performed with every remove operation but rather when the + * load factor falls below a certain limit. Therefore, the amortized time complexity of removing elements + * remains O(1) * * @param element the element to be removed from this set, if present * @return true if this set contained the specified element @@ -102,10 +104,12 @@ public boolean remove(T element) { } int bucketIndex = this.search(element); + // If the index returned by the probe function contains an empty bucket, then the element is not present in + // the set. if (bucketIndex == ELEMENT_NOT_FOUND) { - return false; // If the index returned by the probe function contains an empty bucket, then the element is not present in the set. + return false; } - this.buckets[bucketIndex] = this.TOMBSTONE; // marks the current bucket with a TOMBSTONE. + this.buckets[bucketIndex] = this.tombstone; // marks the current bucket with a tombstone. this.size--; return true; } @@ -125,7 +129,7 @@ public boolean contains(T element) { // Checks equality of element using Object::equals and Object::hashCode return element.equals(this.buckets[bucketIndex]) - && element.hashCode() == this.buckets[bucketIndex].hashCode(); + && element.hashCode() == this.buckets[bucketIndex].hashCode(); } /** @@ -153,8 +157,8 @@ public int size() { */ public List toList() { return Arrays.stream(this.buckets) - .filter(element -> element != null && !element.equals(this.TOMBSTONE)) - .collect(Collectors.toList()); + .filter(element -> element != null && !element.equals(this.tombstone)) + .collect(Collectors.toList()); } /** @@ -174,12 +178,12 @@ public int capacity() { * 1. Obtains the hash code of the element using its `hashCode` method. *

* 2. Applies a bitwise AND operation with `0x7FFFFFFF` to clear the sign bit of the hash code, - * ensuring that the resulting value is a non-negative integer. - * This is necessary because array indices must be non-negative to access elements correctly. + * ensuring that the resulting value is a non-negative integer. + * This is necessary because array indices must be non-negative to access elements correctly. *

* 3. Performs the modulus operation (%) with the length of the `buckets` array to wrap the index - * within the valid range of the array bounds. - * This ensures that the index falls within the range of available buckets. + * within the valid range of the array bounds. + * This ensures that the index falls within the range of available buckets. * * @param element the element to be hashed * @return the bucket index where the element should be placed @@ -200,17 +204,17 @@ private int linearProbe(T element) { int startingProbeIndex = hashFunction(element); int currentBucketIndex = startingProbeIndex; - for (int i = 0; i < this.capacity() - 1; i ++) { + for (int i = 0; i < this.capacity() - 1; i++) { T existingElement = this.buckets[currentBucketIndex]; // check for empty / available bucket. if (this.isEmptyBucket(currentBucketIndex)) { return currentBucketIndex; } - + // check if element is equals to the element in the bucket. // Checks equality of element using Object::equals and Object::hashCode - if (element.equals(existingElement) - && element.hashCode() == existingElement.hashCode()) { + if (element.equals(existingElement) + && element.hashCode() == existingElement.hashCode()) { return currentBucketIndex; } currentBucketIndex = (currentBucketIndex + 1) % this.capacity(); @@ -230,16 +234,16 @@ private int search(T element) { int currentBucketIndex = startingProbeIndex; for (int i = 0; i < this.capacity() - 1; i++) { - // if bucket contains NULL, means element is not present because deleted elements are marked with TOMBSTONE. + // if bucket contains NULL, means element is not present because deleted elements are marked with tombstone. // That is to say given an arbitrary probe sequence of index 1, 2, 3, ..., there can never be a case where - // there is a NULL bucket in the middle of the probe sequence; only TOMBSTONE markers. + // there is a NULL bucket in the middle of the probe sequence; only tombstone markers. if (this.isNullBucket(currentBucketIndex)) { return ELEMENT_NOT_FOUND; } // Checks equality of elements using Object::equals and Object::hashCode. if (this.buckets[currentBucketIndex].equals(element) - && this.buckets[currentBucketIndex].hashCode() == element.hashCode()) { + && this.buckets[currentBucketIndex].hashCode() == element.hashCode()) { return currentBucketIndex; } currentBucketIndex = (currentBucketIndex + 1) % this.capacity(); @@ -275,16 +279,18 @@ private boolean isNullBucket(int bucketIndex) { * @return true if the bucket contains a Tombstone at the given bucketIndex. */ private boolean isTombstoneBucket(int bucketIndex) { - return this.TOMBSTONE.equals(this.buckets[bucketIndex]); + return this.tombstone.equals(this.buckets[bucketIndex]); } /** - * If the load factor is exceeded, the capacity is increased by doubling it (possibly triggered after an add operation), - * or if the load factor falls below 1/4 (arbitrary) of the capacity (and the capacity is larger than the minimum capacity), the + * If the load factor is exceeded, the capacity is increased by doubling it (possibly triggered after an + * add operation), + * or if the load factor falls below 1/4 (arbitrary) of the capacity (and the capacity is larger than the + * minimum capacity), the * capacity is decreased by halving it (possibly triggered after a remove operation). *

- * The resizing operation involves rehashing all existing elements into a new array with the updated capacity. - * This process takes O(n) time, where n is the number of elements in the hash set. + * The resizing operation involves rehashing all existing elements into a new array with the updated capacity. + * This process takes O(n) time, where n is the number of elements in the hash set. */ private void resize(int newCapacity) { // creates a temporary reference to the original bucket @@ -299,9 +305,9 @@ private void resize(int newCapacity) { // re-hashes every element and re-insert into the newly created buckets. Arrays.stream(temp) - .filter(Objects::nonNull) - .filter(element -> !element.equals(this.TOMBSTONE)) - .forEach(this::add); + .filter(Objects::nonNull) + .filter(element -> !element.equals(this.tombstone)) + .forEach(this::add); } /** @@ -311,7 +317,7 @@ private void resize(int newCapacity) { * @return true if the current load factor is exceeded, false otherwise. */ private boolean isLoadFactorExceeded() { - return this.size() >= this.capacity() * this.LOAD_FACTOR; + return this.size() >= this.capacity() * LOAD_FACTOR; } /** @@ -321,9 +327,10 @@ private boolean isLoadFactorExceeded() { * and a bucket that previously contained an element. */ private static class Tombstone { - /**The singleton instance of the Tombstone.*/ + /** The singleton instance of the Tombstone. */ private static final Tombstone TOMBSTONE = new Tombstone(); - /**Private constructor to prevent instantiation of `Tombstone` objects from outside the class.*/ + + /** Private constructor to prevent instantiation of `Tombstone` objects from outside the class. */ private Tombstone() {} /** diff --git a/src/main/java/dataStructures/hashSet/openAddressing/README.md b/src/main/java/dataStructures/hashSet/openAddressing/README.md index 2d45cd40..33dadf22 100644 --- a/src/main/java/dataStructures/hashSet/openAddressing/README.md +++ b/src/main/java/dataStructures/hashSet/openAddressing/README.md @@ -1,13 +1,15 @@ # HashSet (Open-addressing) + Open-addressing is another approach to resolving collisions in hash tables. -A hash collision is resolved by probing, or searching through alternative locations in +A hash collision is resolved by probing, or searching through alternative locations in the array (the probe sequence) until either the target element is found, or an unused array slot is found, which indicates that there is no such key in the table. ## Probing Strategies ### Linear Probing + The probing strategy used in our implementation. Simplest form of probing and involves linearly searching the hash table for an empty spot upon collision. @@ -18,18 +20,20 @@ occupied slots builds up, which can drastically degrade the performance of add, h(k, i) = (h'(k) + i) mod m where h'(k) is an ordinary hash function ### Quadratic Probing -This method of probing involves taking the original hash index, and adding successive values of an arbitrary quadratic + +This method of probing involves taking the original hash index, and adding successive values of an arbitrary quadratic polynomial until an open slot is found. This helps to avoid primary clustering of entries (like in Linear Probing), but might still result in secondary clustering where keys that hash to the same value probe the same alternative cells when a collision occurs. -h(k, i) = ( h`(k) + c1 * i + c2 * (i^2) ) mod m where c1 and c2 are arbitrary constants +h(k, i) = ( h`(k) + c1 * i + c2 * (i^2) ) mod m where c1 and c2 are arbitrary constants ### Double Hashing + This is a method of probing where a secondary hash function is used for probing whenever a collision occurs. -If h2(k) is relatively prime to m for all k, Uniform Hashing Assumption can hold true, as all permutations of probe +If h2(k) is relatively prime to m for all k, Uniform Hashing Assumption can hold true, as all permutations of probe sequences occur in equal probability. h(k, i) = (h1(k) + i * h2(k)) mod m where h1(k) and h2(k) are two ordinary hash functions @@ -37,6 +41,7 @@ h(k, i) = (h1(k) + i * h2(k)) mod m where h1(k) and h2(k) are two ordinary hash *Source: https://courses.csail.mit.edu/6.006/fall11/lectures/lecture10.pdf* ## Analysis + let α = n / m where α is the load factor of the table For n items, in a table of size m, assuming uniform hashing, the expected cost of an operation is: diff --git a/src/main/java/dataStructures/heap/MaxHeap.java b/src/main/java/dataStructures/heap/MaxHeap.java index 0e90e120..ce72a5c8 100644 --- a/src/main/java/dataStructures/heap/MaxHeap.java +++ b/src/main/java/dataStructures/heap/MaxHeap.java @@ -7,7 +7,7 @@ /** * Implementation of an (0-indexed) array-based max heap structure derived from a binary heap. - * + *

* Callable methods are: * size() - O(1) * peek() - O(1) @@ -16,282 +16,297 @@ * remove(T obj) - O(log(n)) * decreaseKey(T obj) - O(log(n)) * increaseKey(T obj) - O(log(n)) - * heapify(List lst) - O(n) + * heapify(List lst) - O(n) * heapify(T ...seq) - O(n) * toString() * * @param generic type for objects to be stored and queried */ public class MaxHeap> { - private List heap; - private Map indexOf; // Identify nodes given a key + private List heap; + private final Map indexOf; // Identify nodes given a key - public MaxHeap() { - heap = new ArrayList<>(); - indexOf = new HashMap<>(); - } + /** + * Constructs a Max Heap. + */ + public MaxHeap() { + heap = new ArrayList<>(); + indexOf = new HashMap<>(); + } - public int size() { - return this.heap.size(); - } + public int size() { + return this.heap.size(); + } - /** - * Return, but does not remove, the element with the highest priority. - * @return head of heap - */ - public T peek() { - if (size() > 0) { - return heap.get(0); - } else { - return null; + /** + * Return, but does not remove, the element with the highest priority. + * + * @return head of heap + */ + public T peek() { + if (size() > 0) { + return heap.get(0); + } else { + return null; + } } - } - /** - * Return element with the highest priority. - * @return head of heap - */ - public T poll() { - if (size() > 0) { - return remove(0); - } else { - return null; + /** + * Return element with the highest priority. + * + * @return head of heap + */ + public T poll() { + if (size() > 0) { + return remove(0); + } else { + return null; + } } - } - /** - * Inserts item into heap. - * @param item item to be inserted - */ - public void offer(T item) { - assert !indexOf.containsKey(item) : "Duplicate objects found!"; + /** + * Inserts item into heap. + * + * @param item item to be inserted + */ + public void offer(T item) { + assert !indexOf.containsKey(item) : "Duplicate objects found!"; - heap.add(item); // add to the end of the arraylist - indexOf.put(item, size() - 1); // add item into index map; here becomes problematic if there are duplicates - bubbleUp(size() - 1); // bubbleUp to rightful place - } + heap.add(item); // add to the end of the arraylist + indexOf.put(item, size() - 1); // add item into index map; here becomes problematic if there are duplicates + bubbleUp(size() - 1); // bubbleUp to rightful place + } - /** - * Remove specified object from the heap. - * @param obj object to be removed - */ - public void remove(T obj) { - if (!indexOf.containsKey(obj)) { - System.out.println(String.format("%s does not exist!", obj)); - return; + /** + * Remove specified object from the heap. + * + * @param obj object to be removed + */ + public void remove(T obj) { + if (!indexOf.containsKey(obj)) { + System.out.printf("%s does not exist!%n", obj); + return; + } + this.remove(indexOf.get(obj)); } - this.remove(indexOf.get(obj)); - } - /** - * Decrease the corresponding value of the object. - * @param obj old object - * @param updatedObj updated object - */ - public void decreaseKey(T obj, T updatedObj) { - assert updatedObj.compareTo(obj) <= 0 : "Value should reduce or remain the same"; + /** + * Remove item at index i + * + * @param i given index + * @return deleted element + */ + private T remove(int i) { + T item = this.get(i); // remember element to be removed + swap(i, this.size() - 1); // O(1) swap with last element in the heap + heap.remove(this.size() - 1); // O(1) + indexOf.remove(item); // remove from index map + bubbleDown(i); // O(log n) + return item; + } - int idx = indexOf.get(obj); // get the index of the object in the array implementation - heap.set(idx, updatedObj); // simply replace - indexOf.remove(obj); // no longer exists - indexOf.put(updatedObj, idx); - bubbleDown(idx); - } + /** + * Decrease the corresponding value of the object. + * + * @param obj old object + * @param updatedObj updated object + */ + public void decreaseKey(T obj, T updatedObj) { + assert updatedObj.compareTo(obj) <= 0 : "Value should reduce or remain the same"; - /** - * Increase the corresponding value of the object. - * @param obj old object - * @param updatedObj updated object - */ - public void increaseKey(T obj, T updatedObj) { - assert updatedObj.compareTo(obj) >= 0 : "Value should reduce or remain the same"; + int idx = indexOf.get(obj); // get the index of the object in the array implementation + heap.set(idx, updatedObj); // simply replace + indexOf.remove(obj); // no longer exists + indexOf.put(updatedObj, idx); + bubbleDown(idx); + } - int idx = indexOf.get(obj); // get the index of the object in the array implementation - heap.set(idx, updatedObj); // simply replace - indexOf.remove(obj); // no longer exists - indexOf.put(updatedObj, idx); - bubbleUp(idx); - } + /** + * Increase the corresponding value of the object. + * + * @param obj old object + * @param updatedObj updated object + */ + public void increaseKey(T obj, T updatedObj) { + assert updatedObj.compareTo(obj) >= 0 : "Value should reduce or remain the same"; - /** - * Takes in a list of objects and convert it into a heap structure. - * @param lst the list of objects - */ - public void heapify(List lst) { - heap = new ArrayList<>(lst); - for (int i = 0; i < this.size(); i++) { - indexOf.put(this.get(i), i); - } - for (int i = this.size() - 1; i >= 0; i--) { - bubbleDown(i); + int idx = indexOf.get(obj); // get the index of the object in the array implementation + heap.set(idx, updatedObj); // simply replace + indexOf.remove(obj); // no longer exists + indexOf.put(updatedObj, idx); + bubbleUp(idx); } - } - /** - * Takes in a sequence of objects and insert into a heap. - * @param <...> seq sequence of T objects - */ - public void heapify(T ...seq) { - heap = new ArrayList(); - int j = 0; - for (T obj : seq) { - heap.add(obj); - indexOf.put(obj, j); - j++; - } - for (int i = this.size() - 1; i >= 0; i--) { - bubbleDown(i); + /** + * Takes in a list of objects and convert it into a heap structure. + * + * @param lst the list of objects + */ + public void heapify(List lst) { + heap = new ArrayList<>(lst); + for (int i = 0; i < this.size(); i++) { + indexOf.put(this.get(i), i); + } + for (int i = this.size() - 1; i >= 0; i--) { + bubbleDown(i); + } } - } - /** - * @return the array representation of the heap in string. - */ - @Override - public String toString() { - StringBuilder ret = new StringBuilder("["); - for (int i = 0; i < this.size(); i++) { - ret.append(heap.get(i)); - ret.append(", "); + /** + * Takes in a sequence of objects and insert into a heap. + * + * @param seq sequence of T objects + */ + public void heapify(T... seq) { + heap = new ArrayList(); + int j = 0; + for (T obj : seq) { + heap.add(obj); + indexOf.put(obj, j); + j++; + } + for (int i = this.size() - 1; i >= 0; i--) { + bubbleDown(i); + } } - return ret.substring(0, ret.length() - 2) + "]"; - } - /** - * @param i given index - * @return element at index i - */ - private T get(int i) { - return heap.get(i); - } - - /** - * Swaps two objects at the specified indices in the heap. - * @param idx1 index of the first object - * @param idx2 index of the second object - */ - private void swap(int idx1, int idx2) { - // update the index of each value in the map - indexOf.put(this.get(idx1), idx2); - indexOf.put(this.get(idx2), idx1); + /** + * @return the array representation of the heap in string. + */ + @Override + public String toString() { + StringBuilder ret = new StringBuilder("["); + for (int i = 0; i < this.size(); i++) { + ret.append(heap.get(i)); + ret.append(", "); + } + return ret.substring(0, ret.length() - 2) + "]"; + } - T tmp = get(idx1); // Recall internally implemented with an ArrayList - heap.set(idx1, this.get(idx2)); - heap.set(idx2, tmp); - } + /** + * @param i given index + * @return element at index i + */ + private T get(int i) { + return heap.get(i); + } - /** - * @param i given index - * @return index of the parent of the element at index i - */ - private int getParentIndex(int i) { - return (i + 1) / 2 - 1; - } + /** + * Swaps two objects at the specified indices in the heap. + * + * @param idx1 index of the first object + * @param idx2 index of the second object + */ + private void swap(int idx1, int idx2) { + // update the index of each value in the map + indexOf.put(this.get(idx1), idx2); + indexOf.put(this.get(idx2), idx1); - /** - * @param i given index - * @return parent of element at index i - */ - private T getParent(int i) { - return this.get(getParentIndex(i)); - } - - /** - * @param i given index - * @return index of left child of element at index i - */ - private int getLeftIndex(int i) { - return (i + 1) * 2 - 1; - } + T tmp = get(idx1); // Recall internally implemented with an ArrayList + heap.set(idx1, this.get(idx2)); + heap.set(idx2, tmp); + } - /** - * @param i given index - * @return left child of element at index i - */ - private T getLeft(int i) { - return this.get(getLeftIndex(i)); - } + /** + * @param i given index + * @return index of the parent of the element at index i + */ + private int getParentIndex(int i) { + return (i + 1) / 2 - 1; + } - /** - * @param i given index - * @return right child of element at index i - */ - private int getRightIndex(int i) { - return (i + 1) * 2; - } + /** + * @param i given index + * @return parent of element at index i + */ + private T getParent(int i) { + return this.get(getParentIndex(i)); + } - /** - * @param i given index - * @return right child of element at index i - */ - private T getRight(int i) { - return this.get(getRightIndex(i)); - } + /** + * @param i given index + * @return index of left child of element at index i + */ + private int getLeftIndex(int i) { + return (i + 1) * 2 - 1; + } - /** - * Bubbles up element at index i until heap property is achieved - * i.e. its value is not larger than its parent - * @param i given index - */ - private void bubbleUp(int i) { - while (i > 0 && this.get(i).compareTo(getParent(i)) > 0) { // the furthest up you can go is the root - int parentIdx = getParentIndex(i); - this.swap(i, parentIdx); - i = parentIdx; + /** + * @param i given index + * @return left child of element at index i + */ + private T getLeft(int i) { + return this.get(getLeftIndex(i)); } - } - /** - * Checks if element at index i is a leaf node in the binary tree representation - * @param i given index - * @return boolean value that determines is leaf or not - */ - private boolean isLeaf(int i) { - // actually, suffice to compare index of left child of a node and size of heap - return this.getRightIndex(i) >= this.size() && this.getLeftIndex(i) >= this.size(); - } + /** + * @param i given index + * @return right child of element at index i + */ + private int getRightIndex(int i) { + return (i + 1) * 2; + } - /** - * Bubble down element at index i until heap property is achieved - * i.e. its value is not smaller than any of its children - * @param i given index - */ - private void bubbleDown(int i) { - while (!this.isLeaf(i)) { - T maxItem = this.get(i); - int maxIndex = i; // index of max item + /** + * @param i given index + * @return right child of element at index i + */ + private T getRight(int i) { + return this.get(getRightIndex(i)); + } - // check if left child is greater in priority, if left exists - if (getLeftIndex(i) < this.size() && maxItem.compareTo(getLeft(i)) < 0) { - maxItem = getLeft(i); - maxIndex = getLeftIndex(i); - } - // check if right child is greater in priority, if right exists - if (getRightIndex(i) < this.size() && maxItem.compareTo(getRight(i)) < 0) { - maxIndex = getRightIndex(i); - } + /** + * Bubbles up element at index i until heap property is achieved + * i.e. its value is not larger than its parent + * + * @param i given index + */ + private void bubbleUp(int i) { + while (i > 0 && this.get(i).compareTo(getParent(i)) > 0) { // the furthest up you can go is the root + int parentIdx = getParentIndex(i); + this.swap(i, parentIdx); + i = parentIdx; + } + } - if (maxIndex != i) { - swap(i, maxIndex); - i = maxIndex; - } else { - break; - } + /** + * Checks if element at index i is a leaf node in the binary tree representation + * + * @param i given index + * @return boolean value that determines is leaf or not + */ + private boolean isLeaf(int i) { + // actually, suffice to compare index of left child of a node and size of heap + return this.getRightIndex(i) >= this.size() && this.getLeftIndex(i) >= this.size(); } - } - /** - * Remove item at index i - * @param i given index - * @return deleted element - */ - private T remove(int i) { - T item = this.get(i); // remember element to be removed - swap(i, this.size() - 1); // O(1) swap with last element in the heap - heap.remove(this.size() - 1); // O(1) - indexOf.remove(item); // remove from index map - bubbleDown(i); // O(log n) - return item; - } + /** + * Bubble down element at index i until heap property is achieved + * i.e. its value is not smaller than any of its children + * + * @param i given index + */ + private void bubbleDown(int i) { + while (!this.isLeaf(i)) { + T maxItem = this.get(i); + int maxIndex = i; // index of max item + // check if left child is greater in priority, if left exists + if (getLeftIndex(i) < this.size() && maxItem.compareTo(getLeft(i)) < 0) { + maxItem = getLeft(i); + maxIndex = getLeftIndex(i); + } + // check if right child is greater in priority, if right exists + if (getRightIndex(i) < this.size() && maxItem.compareTo(getRight(i)) < 0) { + maxIndex = getRightIndex(i); + } + + if (maxIndex != i) { + swap(i, maxIndex); + i = maxIndex; + } else { + break; + } + } + } } diff --git a/src/main/java/dataStructures/heap/README.md b/src/main/java/dataStructures/heap/README.md index 8a0a6f93..7ac53e1d 100644 --- a/src/main/java/dataStructures/heap/README.md +++ b/src/main/java/dataStructures/heap/README.md @@ -1,29 +1,34 @@ # Heap ## Background + ### Binary Heap -A binary heap is often used to introduce the concept of heaps. It is a tree-based data structure that satisfies the + +A binary heap is often used to introduce the concept of heaps. It is a tree-based data structure that satisfies the following properties: + 1. Complete binary tree - every level, except possibly the last, is completely filled 2. Max (min) heap property - the value of every vertex in the binary tree is >= (<=) than that of its child nodes -This makes it a powerful data structure that provides efficient access to the highest (or lowest) priority element, +This makes it a powerful data structure that provides efficient access to the highest (or lowest) priority element, making it suitable as an underlying implementation of the ADT, priority queue. ![max heap](../../../../../docs/assets/images/max_heap.png) ### Array-based Heap + The complete binary tree property actually allows the heap to be implemented as a contiguous array (since no gaps!). The parent-child relationships are derived based on the indices of the elements. Theoretically, there isn't any fundamental difference in order of growth for either implementation. Both implementation provide the same asymptotic time complexity, and supports most operations in O(log(n)). -That said, in practice, the array-based implementation of a heap often provides better performance as opposed to the -former, in cache efficiency and memory locality. This is due to its contiguous memory layout. As such, +That said, in practice, the array-based implementation of a heap often provides better performance as opposed to the +former, in cache efficiency and memory locality. This is due to its contiguous memory layout. As such, the implementation shown here is a 0-indexed array-based heap. ### Relevance of increaseKey and decreaseKey operations + The decision not to include explicit "decrease key" and "increase key" operations in the standard implementations of heaps in Python and Java is primarily due to design choices and considerations of the typical intended use cases. Further, this operation, without augmentation, would take O(n) due to having to search for the object to begin with @@ -34,37 +39,40 @@ One can circumvent the lack of such operations by simply removing and re-inserti **This is worth a mention:**
In cases like Dijkstra algorithm, there is no need to strictly maintain V nodes in the priority queue for O(nlogn). One can just insert all edges rather than constantly updating (hence, no need for updateKey operations).
-After all, the log factor in the order of growth will turn log(E) = log(V^2) in the worst case of a complete graph, +After all, the log factor in the order of growth will turn log(E) = log(V^2) in the worst case of a complete graph, to 2log(V) = O(log(V)). ### Heapify - Choice between bubbleUp and bubbleDown + Heapify deals with bubbling down (for max heap) all elements starting from the back.
What about bubbling-up all elements starting from the front instead?
**No issue with correctness, problem lies with efficiency of operation.** The number of operations required for bubbleUp and bubbleDown (to maintain heap property), is proportional to the distance the node have to move. bubbleDown starts from the bottom level whereas bubbleUp starts from the top level. -Only 1 node is at the top whereas (approx) half the nodes is at the bottom level. It therefore makes sense to use +Only 1 node is at the top whereas (approx) half the nodes is at the bottom level. It therefore makes sense to use bubbleDown. -## Complexity Analysis -**Time**: O(log(n)) in general for most native operations, - except heapify (building a heap from a sequence of elements) that takes o(n) +## Complexity Analysis -**Space**: O(n) +**Time**: O(log(n)) in general for most native operations, +except heapify (building a heap from a sequence of elements) that takes o(n) -where n is the number of elements (whatever the structure, it must store at least n nodes) +**Space**: O(n) + +where n is the number of elements (whatever the structure, it must store at least n nodes) ## Notes + 1. Heaps are often presented as max-heaps (eg. in textbooks), hence the implementation follows a max-heap structure - Still, it is not too difficult to convert a max heap to a min heap, simply negate the values of the nodes -2. The heap implemented here is actually augmented with a Map data type. This allows identification of nodes by key. - - Java's PriorityQueue and Python's heap actually support the removal of a node identified by its value / key. - Note that this is not a typical operation introduced alongside the concept heap simply because the time complexity - would now be O(n), no longer log(n). And indeed, both Java's and Python's version have time complexities +2. The heap implemented here is actually augmented with a Map data type. This allows identification of nodes by key. + - Java's PriorityQueue and Python's heap actually support the removal of a node identified by its value / key. + Note that this is not a typical operation introduced alongside the concept heap simply because the time complexity + would now be O(n), no longer log(n). And indeed, both Java's and Python's version have time complexities of O(n) for this remove operation since their underlying implementation is not augmented. - - The trade-off would be that the heap does not support insertion of duplicate objects else the Map would not work - as intended. + - The trade-off would be that the heap does not support insertion of duplicate objects else the Map would not work + as intended. 3. Rather than using Java arrays, where size must be declared upon initializing, we use list here in the implementation. -4. [Good read](https://stackoverflow.com/questions/9755721/how-can-building-a-heap-be-on-time-complexity?) on the - time complexity of heapify and making the correct choice between bubbleUp and bubbleDown. +4. [Good read](https://stackoverflow.com/questions/9755721/how-can-building-a-heap-be-on-time-complexity?) on the + time complexity of heapify and making the correct choice between bubbleUp and bubbleDown. diff --git a/src/main/java/dataStructures/linkedList/LinkedList.java b/src/main/java/dataStructures/linkedList/LinkedList.java index eb82cbd8..d2ad8d21 100644 --- a/src/main/java/dataStructures/linkedList/LinkedList.java +++ b/src/main/java/dataStructures/linkedList/LinkedList.java @@ -11,11 +11,11 @@ * Constructors:
* LinkedList() -- Initialise a link list with a null head; size = 0
* LinkedList(Node head) -- given a node, make node the head of a new linked list; - * size is found by iterating to the end from this - * specified head
+ * size is found by iterating to the end from this + * specified head
* LinkedList(Node head, int size) -- given a head node and size of linked list specified; - * made private to avoid client from constructing a - * linked list with invalid size. + * made private to avoid client from constructing a + * linked list with invalid size. *

* Callable methods are:
* size() -- Gets the size of the linked list
@@ -24,343 +24,336 @@ * insert(T object, int idx) -- inserts the object at the specified index of the linked list
* remove(int idx) -- remove the node at the specified index
* delete(T object) -- delete the 1st encounter of the specified object - * from the linked list
+ * from the linked list
* pop() -- remove the last node from the linked list
* poll() -- remove the first node from the linked list
* search(T object) -- search for the 1st encounter of the node that - * holds the specified object
+ * holds the specified object
* get(int idx) -- get the node at the specified index
* reverse() -- reverse the linked list - * (head of linked list now starts from the back)
+ * (head of linked list now starts from the back)
* sort() -- sorts the linked list by their natural order
* * @param generic type for objects to be stored in the linked list */ public class LinkedList> { - private Node head; - private int size; - - /** - * Constructor for linkedlist. - */ - public LinkedList() { - head = null; - size = 0; - } - /** - * Overloaded constructor for linkedlist. - * - * @param head Head of the linked list. - */ - - public LinkedList(Node head) { - this.head = head; - Node trav = head; - int count = 0; - while (trav != null) { // We count the size of our linked list through iteration. - count++; - trav = trav.next; + private Node head; + private int size; + + /** + * Constructor for linkedlist. + */ + public LinkedList() { + head = null; + size = 0; } - size = count; - } - - private LinkedList(Node head, int size) { - this.head = head; - this.size = size; - } - - /** - * Gets the size of the linked list. - * - * @return int value - */ - public int size() { - return this.size; - } - - /** - * inserts the object at the specified index of the linked list. - * - * @param object to be inserted - * @param idx index which the object is to be inserted into - * - * @return boolean representing whether insertion was successful - */ - public boolean insert(T object, int idx) { - if (idx > size) { - System.out.println("Index out of bounds."); - return false; - } - - Node newNode = new Node<>(object); - if (head == null) { // Linked list empty; We just need to add the head; - head = newNode; - size++; - return true; + /** + * Overloaded constructor for linkedlist. + * + * @param head Head of the linked list. + */ + + public LinkedList(Node head) { + this.head = head; + Node trav = head; + int count = 0; + while (trav != null) { // We count the size of our linked list through iteration. + count++; + trav = trav.next; + } + size = count; } - Node prev = null; - Node trav = head; - for (int i = 0; i < idx; i++) { - prev = trav; - trav = trav.next; + private LinkedList(Node head, int size) { + this.head = head; + this.size = size; } - if (prev != null) { // Reset the pointer at the index. - prev.next = newNode; - newNode.next = trav; - } else { // case when inserting at index 0; need to update head - head = newNode; - newNode.next = trav; - } - size++; - return true; - } - - /** - * Inserts the object at the front of the linked list. - * - * @param object to be inserted - * - * @return boolean representing whether insertion was successful - */ - public boolean insertFront(T object) { - return insert(object, 0); - } - - /** - * Inserts the object at the end of the linked list. - * - * @param object to be inserted - * - * @return boolean representing whether insertion was successful - */ - public boolean insertEnd(T object) { - return insert(object, this.size); - } - - /** - * remove the node at the specified index. - * - * @param idx of the node to be removed - * - * @return node's value - */ - public T remove(int idx) { - if (idx >= size) { - System.out.println("Index out of bounds."); - return null; + /** + * Gets the size of the linked list. + * + * @return int value + */ + public int size() { + return this.size; } - Node prev = null; - Node trav = head; - - for (int i = 0; i < idx; i++) { - prev = trav; - trav = trav.next; - } // This iteration allows us to store a copy of nodes before and after idx. - - if (prev != null) { - prev.next = trav.next; - } else { // case when removing head; need update head - head = head.next; - } - size--; - return trav.val; - } - - /** - * search for the 1st encounter of the node that holds the specified object. - * - * @param object - * - * @return index of the node found - */ - public int search(T object) { - Node trav = head; - int idx = 0; - if (trav == null) { // Empty linked list. - return -1; + /** + * inserts the object at the specified index of the linked list. + * + * @param object to be inserted + * @param idx index which the object is to be inserted into + * @return boolean representing whether insertion was successful + */ + public boolean insert(T object, int idx) { + if (idx > size) { + System.out.println("Index out of bounds."); + return false; + } + + Node newNode = new Node<>(object); + + if (head == null) { // Linked list empty; We just need to add the head; + head = newNode; + size++; + return true; + } + + Node prev = null; + Node trav = head; + for (int i = 0; i < idx; i++) { + prev = trav; + trav = trav.next; + } + + if (prev != null) { // Reset the pointer at the index. + prev.next = newNode; + newNode.next = trav; + } else { // case when inserting at index 0; need to update head + head = newNode; + newNode.next = trav; + } + size++; + return true; } - while (trav != null) { - if (trav.val.equals(object)) { - return idx; - } - idx++; - trav = trav.next; + /** + * Inserts the object at the front of the linked list. + * + * @param object to be inserted + * @return boolean representing whether insertion was successful + */ + public boolean insertFront(T object) { + return insert(object, 0); } - return -1; - } - - /** - * delete the 1st encounter of the specified object from the linked list. - * - * @param object to search and delete - * - * @return boolean whether the delete op was successful - */ - public boolean delete(T object) { - int idx = search(object); // Get index of object to remove. - if (idx != -1) { - remove(idx); // Remove based on that index. - return true; + /** + * Inserts the object at the end of the linked list. + * + * @param object to be inserted + * @return boolean representing whether insertion was successful + */ + public boolean insertEnd(T object) { + return insert(object, this.size); } - return false; - } - - /** - * remove the last node from the linked list. - * - * @return value of popped node - */ - public T pop() { - return remove(size() - 1); - } - - /** - * remove the first node from the linked list. - * - * @return value of polled node - */ - public T poll() { - return remove(0); - } - - - /** - * get the node at the specified index. - * - * @param idx of node to be found - * - * @return node found - */ - public Node get(int idx) { - Node trav = head; - if (idx < this.size && trav != null) { // Check: idx is valid & linked list not empty. - for (int i = 0; i < idx; i++) { - trav = trav.next; - } - return trav; + + /** + * remove the node at the specified index. + * + * @param idx of the node to be removed + * @return node's value + */ + public T remove(int idx) { + if (idx >= size) { + System.out.println("Index out of bounds."); + return null; + } + + Node prev = null; + Node trav = head; + + for (int i = 0; i < idx; i++) { + prev = trav; + trav = trav.next; + } // This iteration allows us to store a copy of nodes before and after idx. + + if (prev != null) { + prev.next = trav.next; + } else { // case when removing head; need update head + head = head.next; + } + size--; + return trav.val; } - return null; - } - - /** - * reverse the linked list. - * A good video to visualise this algorithm can be found - * here. - */ - public void reverse() { - // No need to reverse if list is empty or only 1 element. - if (head == null || head.next == null) { - return; + + /** + * search for the 1st encounter of the node that holds the specified object. + * + * @param object + * @return index of the node found + */ + public int search(T object) { + Node trav = head; + int idx = 0; + if (trav == null) { // Empty linked list. + return -1; + } + + while (trav != null) { + if (trav.val.equals(object)) { + return idx; + } + idx++; + trav = trav.next; + } + + return -1; } - Node prev = head; - Node curr = head.next; - Node newHead = curr; // Store the next head. - prev.next = null; // Reset to null, representing end of list. - while (curr.next != null) { - newHead = curr.next; // We set the next element as the newHead. - curr.next = prev; // Replace our current node as the previous node. - prev = curr; - curr = newHead; // Set our current node as the next element (newHead) to look at. + /** + * delete the 1st encounter of the specified object from the linked list. + * + * @param object to search and delete + * @return boolean whether the delete op was successful + */ + public boolean delete(T object) { + int idx = search(object); // Get index of object to remove. + if (idx != -1) { + remove(idx); // Remove based on that index. + return true; + } + return false; } - newHead.next = prev; - head = newHead; // newHead is last ele from org. list -> First ele of our reversed list. - } - - /** - * Sorts the linked list by the natural order of the elements. - * Generally, merge sort is the most efficient sorting algorithm for linked lists. - * Accessing a random node in the linked list incurs O(n) time complexity. - * This makes sort algorithms like quicksort and heapsort - * inefficient since they rely on the O(1) lookup time, like in an array. - * - * A good video to visualise this algorithm can be found - * here. - */ - public void sort() { - if (this.size <= 1) { - return; + + /** + * remove the last node from the linked list. + * + * @return value of popped node + */ + public T pop() { + return remove(size() - 1); } - int mid = (this.size - 1) / 2; - Node middle = this.get(mid); - Node nextHead = middle.next; - LinkedList next = new LinkedList<>(nextHead, this.size - 1 - mid); // Split the list into 2. - middle.next = null; - this.size = mid + 1; // update size of original list after split - - next.sort(); // Recursively sort the list. - this.sort(); - head = merge(this, next); - } - - /** - * Merge routine helper function for two sorted linked lists. - * - * @param first sorted linked list - * @param second sorted linked list - * - * @return head of merged linked list - */ - private Node merge(LinkedList first, LinkedList second) { - Node headFirst = first.head; - Node headSecond = second.head; - Node dummy = new Node<>(null); - Node trav = dummy; - - while (headFirst != null && headSecond != null) { - if (headFirst.val.compareTo(headSecond.val) < 0) { - // Note that first & second either only have 2 values or are already sorted. - trav.next = headFirst; - headFirst = headFirst.next; - } else { - trav.next = headSecond; - headSecond = headSecond.next; - } - trav = trav.next; + + /** + * remove the first node from the linked list. + * + * @return value of polled node + */ + public T poll() { + return remove(0); } - if (headFirst != null) { // Add any remaining nodes from first. - trav.next = headFirst; - } else { // We know loop terminated because of second; Add any remaining nodes from second. - trav.next = headSecond; + + /** + * get the node at the specified index. + * + * @param idx of node to be found + * @return node found + */ + public Node get(int idx) { + Node trav = head; + if (idx < this.size && trav != null) { // Check: idx is valid & linked list not empty. + for (int i = 0; i < idx; i++) { + trav = trav.next; + } + return trav; + } + return null; } - return dummy.next; - } - - @Override - public String toString() { - Node trav = head; - String ret = ""; - if (trav == null) { - return null; + + /** + * reverses the linked list. + * A good video to visualise this algorithm can be found + * here + */ + public void reverse() { + // No need to reverse if list is empty or only 1 element. + if (head == null || head.next == null) { + return; + } + + Node prev = head; + Node curr = head.next; + Node newHead = curr; // Store the next head. + prev.next = null; // Reset to null, representing end of list. + while (curr.next != null) { + newHead = curr.next; // We set the next element as the newHead. + curr.next = prev; // Replace our current node as the previous node. + prev = curr; + curr = newHead; // Set our current node as the next element (newHead) to look at. + } + newHead.next = prev; + head = newHead; // newHead is last ele from org. list -> First ele of our reversed list. } - while (trav != null) { - ret += trav + " "; - trav = trav.next; + + /** + * Sorts the linked list by the natural order of the elements. + * Generally, merge sort is the most efficient sorting algorithm for linked lists. + * Accessing a random node in the linked list incurs O(n) time complexity. + * This makes sort algorithms like quicksort and heapsort + * inefficient since they rely on the O(1) lookup time, like in an array. + *

+ * A good video to visualise this algorithm can be found + * here + */ + public void sort() { + if (this.size <= 1) { + return; + } + int mid = (this.size - 1) / 2; + Node middle = this.get(mid); + Node nextHead = middle.next; + LinkedList next = new LinkedList<>(nextHead, this.size - 1 - mid); // Split the list into 2. + middle.next = null; + this.size = mid + 1; // update size of original list after split + + next.sort(); // Recursively sort the list. + this.sort(); + head = merge(this, next); } - return ret; - } - - /** - * Node class for linked list. - */ - public static class Node { - T val; - Node next; - - private Node(T val) { - this.val = val; + + /** + * Merge routine helper function for two sorted linked lists. + * + * @param first sorted linked list + * @param second sorted linked list + * @return head of merged linked list + */ + private Node merge(LinkedList first, LinkedList second) { + Node headFirst = first.head; + Node headSecond = second.head; + Node dummy = new Node<>(null); + Node trav = dummy; + + while (headFirst != null && headSecond != null) { + if (headFirst.val.compareTo(headSecond.val) < 0) { + // Note that first & second either only have 2 values or are already sorted. + trav.next = headFirst; + headFirst = headFirst.next; + } else { + trav.next = headSecond; + headSecond = headSecond.next; + } + trav = trav.next; + } + + if (headFirst != null) { // Add any remaining nodes from first. + trav.next = headFirst; + } else { // We know loop terminated because of second; Add any remaining nodes from second. + trav.next = headSecond; + } + return dummy.next; } @Override public String toString() { - return this.val.toString(); + Node trav = head; + String ret = ""; + if (trav == null) { + return null; + } + while (trav != null) { + ret += trav + " "; + trav = trav.next; + } + return ret; + } + + /** + * Node class for linked list. + */ + public static class Node { + private final T val; + private Node next; + + private Node(T val) { + this.val = val; + } + + @Override + public String toString() { + return this.val.toString(); + } } - } } diff --git a/src/main/java/dataStructures/linkedList/README.md b/src/main/java/dataStructures/linkedList/README.md index 9a649c4f..2d78083d 100644 --- a/src/main/java/dataStructures/linkedList/README.md +++ b/src/main/java/dataStructures/linkedList/README.md @@ -1,5 +1,6 @@ # Linked Lists -Linked lists are a linear structure used to store data elements. + +Linked lists are a linear structure used to store data elements. It consists of a collection of objects, used to store our data elements, known as nodes. ![Linked list image](https://media.geeksforgeeks.org/wp-content/cdn-uploads/20230726162542/Linked-List-Data-Structure.png) @@ -7,8 +8,9 @@ It consists of a collection of objects, used to store our data elements, known a *Source: GeeksForGeeks* ### Linked Lists vs Arrays -Linked lists are similar to arrays in -terms of used and purpose, but there are considerations when deciding which structure to use. + +Linked lists are similar to arrays in +terms of used and purpose, but there are considerations when deciding which structure to use. Unlike arrays, which are stored in contiguous locations in memory, linked lists are stored across memory and are connected to each other via pointers. @@ -18,6 +20,7 @@ linked lists are stored across memory and are connected to each other via pointe *Source: BeginnersBook* ## Analysis + **Time Complexity**: Depends on operations, O(n) in general for most operations. Most operations require iterating the linked list. For instance, @@ -30,21 +33,22 @@ element. ## Notes ### Memory Requirements & Flexibility + As a contiguous block of memory must be allocated for an array, its size is fixed. -If we declare a array of size *n*, but only allocate *x* elements, where *x < n*, +If we declare a array of size *n*, but only allocate *x* elements, where *x < n*, we will be left with unused, wasted memory. This waste does not occur with linked lists, which only take up memory as new elements are added. -However, additional space will be used to store the pointers. +However, additional space will be used to store the pointers. -As the declared size of our array is static (done at compile time), we are also given less flexibility if +As the declared size of our array is static (done at compile time), we are also given less flexibility if we end up needing to store more elements at run time. -However, linked list gives us the option of adding new nodes at run time based on our requirements, +However, linked list gives us the option of adding new nodes at run time based on our requirements, allowing us to allocate memory to store items dynamically, giving us more flexibility. - ## Linked List Variants + The lookup time within a linked list is its biggest issue. However, there are variants of linked lists designed to speed up lookup time. @@ -54,11 +58,15 @@ However, there are variants of linked lists designed to speed up lookup time. *Source: GeeksForGeeks* -This is a variant of the linked list with each node containing the pointer to not just the next note, but also the previous node. +This is a variant of the linked list with each node containing the pointer to not just the next note, but also the +previous node. -Unlike the standard linked list, this allows us to traverse the list in the backwards direction too, this makes it a good data structure to use when implementing undo / redo functions. However, when implementing a doubly linked list, it is vital to ensure that you consider and maintain **both** pointers. +Unlike the standard linked list, this allows us to traverse the list in the backwards direction too, this makes it a +good data structure to use when implementing undo / redo functions. However, when implementing a doubly linked list, it +is vital to ensure that you consider and maintain **both** pointers. -It is also worth noting that insertion from the front and back of the linked list is a O(1) operation. (Since we now only need to change the pointers in the node.) +It is also worth noting that insertion from the front and back of the linked list is a O(1) operation. (Since we now +only need to change the pointers in the node.) ### Skip list @@ -66,8 +74,8 @@ It is also worth noting that insertion from the front and back of the linked lis *Source: Brilliant* -This is a variant of a linked list with additional pointer paths that does not lead to the next node -but allow you to skip through nodes in a "express pointer path" which can be user configured. +This is a variant of a linked list with additional pointer paths that does not lead to the next node +but allow you to skip through nodes in a "express pointer path" which can be user configured. If we know which nodes each express pointer path stops at, we would be able to conduct faster lookup. This would also be ideal in situations where we want to store a large amount @@ -79,5 +87,5 @@ of data which we do not need to access regularly that we are not willing to dele *Source: Brilliant* -Unrolled linked lists stores multiple consecutive elements into a single bucket node. +Unrolled linked lists stores multiple consecutive elements into a single bucket node. This allows us to avoid constantly travelling down nodes to get to the element we need. diff --git a/src/main/java/dataStructures/lruCache/LRU.java b/src/main/java/dataStructures/lruCache/LRU.java index b9bc2bab..17090f19 100644 --- a/src/main/java/dataStructures/lruCache/LRU.java +++ b/src/main/java/dataStructures/lruCache/LRU.java @@ -1,32 +1,35 @@ package dataStructures.lruCache; -import java.util.*; +import java.util.HashMap; +import java.util.Map; /** - * Implementation of Least Recently Used (LRU) Cache + * Implementation of Least Recently Used (LRU) Cache + * * @param generic type of key to be stored * @param generic type of associated value corresponding to a given key - * Constructor: - * LRU(int capacity) - * Client methods: - * get(K key) - * put(K key, V value) - * Both methods above run in O(1) average time complexity + * Constructor: + * LRU(int capacity) + * Client methods: + * get(K key) + * put(K key, V value) + * Both methods above run in O(1) average time complexity */ public class LRU { - int cap; - Map> map; - Node left; // dummy left node to point to the left end - Node right; // dummy right node to point to the right end + private final int cap; + private final Map> map; + private final Node left; // dummy left node to point to the left end + private final Node right; // dummy right node to point to the right end /** * Helper node class that encapsulates key-value pair and act as linked list to neighbour nodes. */ private class Node { - K key; - V val; - Node next; - Node prev; + private final K key; + private V val; + private Node next; + private Node prev; + Node(K key, V value) { this.key = key; this.val = value; @@ -35,6 +38,11 @@ private class Node { } } + /** + * Constructs an instance of Least Recently Used Cache. + * + * @param capacity the maximum capacity of the cache. + */ public LRU(int capacity) { this.cap = capacity; this.map = new HashMap<>(); @@ -46,6 +54,7 @@ public LRU(int capacity) { /** * Helper method to remove the specified node from the doubly linked list + * * @param node to be removed from the linked list */ private void remove(Node node) { @@ -57,6 +66,7 @@ private void remove(Node node) { /** * Helper method to insert a node to the right end of the double linked list (Most Recently Used) + * * @param node to be inserted */ private void insert(Node node) { @@ -69,6 +79,7 @@ private void insert(Node node) { /** * return the value of the key if it exists; otherwise null + * * @param key whose value, if exists, to be obtained */ public V get(K key) { @@ -82,9 +93,10 @@ public V get(K key) { } /** - * Update the value of the key if the key exists. - * Otherwise, add the key-value pair to the cache. - * If the number of keys exceeds the capacity from this operation, evict the least recently used key + * Update the value of the key if the key exists. + * Otherwise, add the key-value pair to the cache. + * If the number of keys exceeds the capacity from this operation, evict the least recently used key + * * @param key the key * @param val the associated value */ @@ -123,4 +135,4 @@ public void print() { } System.out.println(); } -} \ No newline at end of file +} diff --git a/src/main/java/dataStructures/queue/Deque/Deque.java b/src/main/java/dataStructures/queue/Deque/Deque.java index 0dedec8e..ea9afd62 100644 --- a/src/main/java/dataStructures/queue/Deque/Deque.java +++ b/src/main/java/dataStructures/queue/Deque/Deque.java @@ -7,172 +7,172 @@ */ public class Deque { - private Node first; - - private Node last; - - private int size; - - /** - * Constructor for empty Deque. - */ - public Deque() { - this.size = 0; - this.first = this.last = null; // implemented with double-linked list - } - - /** - * Check if deque is empty. - * - * @return True if empty. - */ - public boolean isEmpty() { - return this.size == 0; - } - - /** - * Get size of deque. - * - * @return Size of the deque. - */ - public int getSize() { - return this.size; - } - - /** - * Add element into the back of the deque. - * - * @param val Element to be added. - */ - public void addElement(T val) { - Node newNode = new Node<>(val); - if (this.isEmpty()) { - this.last = this.first = newNode; - size++; - return; - } - Node temp = this.last; - temp.next = newNode; - newNode.prev = temp; - this.last = newNode; - this.size++; - } - - /** - * Add a element into the front of the deque. - * - * @param val Element to be added. - */ - public void addFirst(T val) { - Node newNode = new Node<>(val); - if (this.isEmpty()) { - this.last = this.first = newNode; - size++; - return; - } - Node temp = this.first; - temp.prev = newNode; - newNode.next = temp; - this.first = newNode; - size++; - } - - /** - * Peek the first element of the deque. - * - * @return The value in the first node of the deque. - */ - public T peekFirst() { - if (this.isEmpty()) { - return null; - } - return first.val; - } - - /** - * Peek the last element of the deque. - * - * @return The value in the last node of the deque. - */ - public T peekLast() { - if (this.isEmpty()) { - return null; + private Node first; + + private Node last; + + private int size; + + /** + * Constructor for empty Deque. + */ + public Deque() { + this.size = 0; + this.first = this.last = null; // implemented with double-linked list } - return last.val; - } - - /** - * Removes and retrieves the first element of the deque. - * - * @return The value in the first node of the deque. - */ - public T pollFirst() { - if (this.isEmpty()) { - return null; + + /** + * Check if deque is empty. + * + * @return True if empty. + */ + public boolean isEmpty() { + return this.size == 0; } - Node firstNode = this.first; - Node newFirstNode = this.first.next; - if (newFirstNode != null) { - newFirstNode.prev = null; + + /** + * Get size of deque. + * + * @return Size of the deque. + */ + public int getSize() { + return this.size; } - this.first = newFirstNode; - firstNode.next = null; - this.size--; - return firstNode.val; - } - - /** - * Removes and retrieves the last element of the deque. - * - * @return The value in the last node of the deque. - */ - public T pollLast() { - if (this.isEmpty()) { - return null; + + /** + * Add element into the back of the deque. + * + * @param val Element to be added. + */ + public void addElement(T val) { + Node newNode = new Node<>(val); + if (this.isEmpty()) { + this.last = this.first = newNode; + size++; + return; + } + Node temp = this.last; + temp.next = newNode; + newNode.prev = temp; + this.last = newNode; + this.size++; } - Node lastNode = this.last; - Node newLastNode = lastNode.prev; - if (newLastNode != null) { - newLastNode.next = null; + + /** + * Add a element into the front of the deque. + * + * @param val Element to be added. + */ + public void addFirst(T val) { + Node newNode = new Node<>(val); + if (this.isEmpty()) { + this.last = this.first = newNode; + size++; + return; + } + Node temp = this.first; + temp.prev = newNode; + newNode.next = temp; + this.first = newNode; + size++; } - lastNode.prev = null; - this.last = newLastNode; - this.size--; - return lastNode.val; - } - - /** - * Converts Deque into String iteratively. - * - * @return String representation of elements in the deque. - */ - @Override - public String toString() { - if (this.isEmpty()) { - return "[]"; + + /** + * Peek the first element of the deque. + * + * @return The value in the first node of the deque. + */ + public T peekFirst() { + if (this.isEmpty()) { + return null; + } + return first.val; } - String ret = "["; - Node temp = this.first; - while (temp != null) { - ret += " " + temp.toString() + " "; - temp = temp.next; + + /** + * Peek the last element of the deque. + * + * @return The value in the last node of the deque. + */ + public T peekLast() { + if (this.isEmpty()) { + return null; + } + return last.val; } - return ret + "]"; - } - private static class Node { - private T val; - Node next; - Node prev; + /** + * Removes and retrieves the first element of the deque. + * + * @return The value in the first node of the deque. + */ + public T pollFirst() { + if (this.isEmpty()) { + return null; + } + Node firstNode = this.first; + Node newFirstNode = this.first.next; + if (newFirstNode != null) { + newFirstNode.prev = null; + } + this.first = newFirstNode; + firstNode.next = null; + this.size--; + return firstNode.val; + } - public Node(T val) { - this.val = val; - this.next = this.prev = null; + /** + * Removes and retrieves the last element of the deque. + * + * @return The value in the last node of the deque. + */ + public T pollLast() { + if (this.isEmpty()) { + return null; + } + Node lastNode = this.last; + Node newLastNode = lastNode.prev; + if (newLastNode != null) { + newLastNode.next = null; + } + lastNode.prev = null; + this.last = newLastNode; + this.size--; + return lastNode.val; } + /** + * Converts Deque into String iteratively. + * + * @return String representation of elements in the deque. + */ @Override public String toString() { - return this.val.toString(); + if (this.isEmpty()) { + return "[]"; + } + String ret = "["; + Node temp = this.first; + while (temp != null) { + ret += " " + temp + " "; + temp = temp.next; + } + return ret + "]"; + } + + private static class Node { + private final T val; + private Node next; + private Node prev; + + public Node(T val) { + this.val = val; + this.next = this.prev = null; + } + + @Override + public String toString() { + return this.val.toString(); + } } - } } diff --git a/src/main/java/dataStructures/queue/Deque/README.md b/src/main/java/dataStructures/queue/Deque/README.md index 8e557afd..b8e0980f 100644 --- a/src/main/java/dataStructures/queue/Deque/README.md +++ b/src/main/java/dataStructures/queue/Deque/README.md @@ -11,9 +11,11 @@ or LIFO order but rather can utilise either orders flexibly. A deque can be implemented in multiple ways, using doubly linked lists, arrays or two stacks. ## Analysis + Much like a queue, deque operations involves the head / tail, resulting in *O(1)* complexity for most operations. ## Notes + Just like a queue, a monotonic deque could also be created to solve more specific sliding window problems. diff --git a/src/main/java/dataStructures/queue/Queue.java b/src/main/java/dataStructures/queue/Queue.java index 69c8708c..c8fded93 100644 --- a/src/main/java/dataStructures/queue/Queue.java +++ b/src/main/java/dataStructures/queue/Queue.java @@ -12,97 +12,97 @@ * dequeue() - java's poll() / remove() equivalent * peek() *

- * Note: calling dequeue() or peek() on an eqmpty queue - * returns null + * Note: calling dequeue() or peek() on an empty queue + * returns null * * @param generic type of object to be stored in the queue */ public class Queue { - private Node first; - private Node last; - private int size; + private Node first; + private Node last; + private int size; - /** - * Constructor to initialize an empty queue. - */ - public Queue() { - first = last = null; - size = 0; - } + /** + * Constructor to initialize an empty queue. + */ + public Queue() { + first = last = null; + size = 0; + } - /** - * Gets the size of the queue. - * - * @return size of queue - */ - public int size() { - return this.size; - } + /** + * Gets the size of the queue. + * + * @return size of queue + */ + public int size() { + return this.size; + } - /* - * Checks if queue is empty. - * @return boolean value - */ - public boolean isEmpty() { - return this.size == 0; - } + /* + * Checks if queue is empty. + * @return boolean value + */ + public boolean isEmpty() { + return this.size == 0; + } - /** - * Add an element to the end of the queue. - * Note: java's equivalent methods are offer() / add(). - * - * @param item item to be pushed - */ - public void enqueue(T item) { - Node toInsert = new Node<>(item); - if (this.isEmpty()) { - this.first = this.last = toInsert; - } else { - this.last.next = toInsert; - this.last = toInsert; + /** + * Add an element to the end of the queue. + * Note: java's equivalent methods are offer() / add(). + * + * @param item item to be pushed + */ + public void enqueue(T item) { + Node toInsert = new Node<>(item); + if (this.isEmpty()) { + this.first = this.last = toInsert; + } else { + this.last.next = toInsert; + this.last = toInsert; + } + this.size++; } - this.size++; - } - /** - * Remove an element from the start of the queue. - * Note: java's equivalent methods are poll() / remove(). - * - * @return head of the queue; null is empty - */ - public T dequeue() { - if (this.isEmpty()) { - return null; + /** + * Remove an element from the start of the queue. + * Note: java's equivalent methods are poll() / remove(). + * + * @return head of the queue; null is empty + */ + public T dequeue() { + if (this.isEmpty()) { + return null; + } + this.size--; + Node removed = this.first; + this.first = this.first.next; + return removed.val; } - this.size--; - Node removed = this.first; - this.first = this.first.next; - return removed.val; - } - /** - * Display the element at the head of the queue. - * - * @return first item of the queue; null is queue is empty - */ - public T peek() { - if (this.isEmpty()) { - return null; + /** + * Display the element at the head of the queue. + * + * @return first item of the queue; null is queue is empty + */ + public T peek() { + if (this.isEmpty()) { + return null; + } + return this.first.val; } - return this.first.val; - } - /** - * Node class to wrap the object. - * - * @param generic type of object to be stored in the queue - */ - private static class Node { - T val; - Node next; - - private Node(T val) { - this.val = val; + /** + * Node class to wrap the object. + * + * @param generic type of object to be stored in the queue + */ + private static class Node { + private final T val; + private Node next; + + private Node(T val) { + this.val = val; + } } - } } diff --git a/src/main/java/dataStructures/queue/README.md b/src/main/java/dataStructures/queue/README.md index 665088ca..cbbea01f 100644 --- a/src/main/java/dataStructures/queue/README.md +++ b/src/main/java/dataStructures/queue/README.md @@ -32,6 +32,7 @@ On the other hand, a queue allows you to operate on elements that enter first. S this would be useful include Breadth First Search algorithms and task / resource allocation systems. ### Arrays vs Linked List + It is worth noting that queues can be implemented with either a array or with a [linked list](../linkedList/README.md). In the context of ordered operations, the lookup is only restricted to the first element. diff --git a/src/main/java/dataStructures/queue/monotonicQueue/MonotonicQueue.java b/src/main/java/dataStructures/queue/monotonicQueue/MonotonicQueue.java index 76689ff9..790c3896 100644 --- a/src/main/java/dataStructures/queue/monotonicQueue/MonotonicQueue.java +++ b/src/main/java/dataStructures/queue/monotonicQueue/MonotonicQueue.java @@ -5,74 +5,74 @@ import java.util.Deque; /** -* Implementation of a non-increasing (decreasing) monotonic queue -* for certain (but common) use case: -* When larger objects pushed to the queue are able to replace and represent -* smaller objects that come before it. -* Callable methods are: -* isEmpty() -* max() -* pop() -* push() -*

-* Movement within the monotonic-queue: -* index v Increasing queue Decreasing queue -* 1 5 [5] [5] -* 2 3 [3] 3 kick out 5 [5, 3] #3->5 -* 3 1 [1] 1 kick out 3 [5, 3, 1] #1->3 -* 4 2 [1, 2] #2->1 [5, 3, 2] 2 kick out 1 #2->3 -* 5 4 [1, 2, 4] #4->2 [5,4] 4 kick out 2, 3 #4->2 -* -* @param generic type for objects to be stored or queried -*/ + * Implementation of a non-increasing (decreasing) monotonic queue + * for certain (but common) use case: + * When larger objects pushed to the queue are able to replace and represent + * smaller objects that come before it. + * Callable methods are: + * isEmpty() + * max() + * pop() + * push() + *

+ * Movement within the monotonic-queue: + * index v Increasing queue Decreasing queue + * 1 5 [5] [5] + * 2 3 [3] 3 kick out 5 [5, 3] #3->5 + * 3 1 [1] 1 kick out 3 [5, 3, 1] #1->3 + * 4 2 [1, 2] #2->1 [5, 3, 2] 2 kick out 1 #2->3 + * 5 4 [1, 2, 4] #4->2 [5,4] 4 kick out 2, 3 #4->2 + * + * @param generic type for objects to be stored or queried + */ public class MonotonicQueue> { - private Deque dq = new ArrayDeque<>(); // or LinkedList + private final Deque dq = new ArrayDeque<>(); // or LinkedList - /** - * Checks if queue is empty. - * - * @return boolean - */ - public boolean isEmpty() { - return dq.isEmpty(); - } + /** + * Checks if queue is empty. + * + * @return boolean + */ + public boolean isEmpty() { + return dq.isEmpty(); + } - /** - * Push an object into the queue and maintain the non-increasing property. - * - * @param obj to be inserted. - */ - public void push(T obj) { - Integer count = 0; - while (!dq.isEmpty() && obj.compareTo(dq.peekLast()) > 0) { - dq.pollLast(); // Removes elements that do not conform the non-increasing sequence. + /** + * Push an object into the queue and maintain the non-increasing property. + * + * @param obj to be inserted. + */ + public void push(T obj) { + Integer count = 0; + while (!dq.isEmpty() && obj.compareTo(dq.peekLast()) > 0) { + dq.pollLast(); // Removes elements that do not conform the non-increasing sequence. + } + dq.offerLast(obj); } - dq.offerLast(obj); - } - /** - * Returns the highest value stored in the queue. - * - * @return the first value of the queue. - */ - public T max() { - if (isEmpty()) { - return null; + /** + * Returns the highest value stored in the queue. + * + * @return the first value of the queue. + */ + public T max() { + if (isEmpty()) { + return null; + } + return dq.peek(); } - return dq.peek(); - } - /** - * Returns the highest valued object in the queue; i.e the first object; - * Removal will only be done all representation of the object has been accounted for. - * - * @return The first value in the deque. (Highest valued object) - */ - public T pop() { - if (dq.isEmpty()) { - return null; + /** + * Returns the highest valued object in the queue; i.e the first object; + * Removal will only be done all representation of the object has been accounted for. + * + * @return The first value in the deque. (Highest valued object) + */ + public T pop() { + if (dq.isEmpty()) { + return null; + } + return dq.poll(); // Returns & remove head of deque } - return dq.poll(); // Returns & remove head of deque - } } diff --git a/src/main/java/dataStructures/queue/monotonicQueue/README.md b/src/main/java/dataStructures/queue/monotonicQueue/README.md index 25fc4703..c3e114f3 100644 --- a/src/main/java/dataStructures/queue/monotonicQueue/README.md +++ b/src/main/java/dataStructures/queue/monotonicQueue/README.md @@ -11,5 +11,6 @@ increasing / decreasing order, hence, we only need to compare down the monotonic queue from the back when adding new elements. ## Operation Orders -Just like a queue, a monotonic queue mainly has *O(1)* operations, -which is unique given that it ensures a certain order, which usually incurs operations of a higher complexity. \ No newline at end of file + +Just like a queue, a monotonic queue mainly has *O(1)* operations, +which is unique given that it ensures a certain order, which usually incurs operations of a higher complexity. diff --git a/src/main/java/dataStructures/segmentTree/SegmentTree.java b/src/main/java/dataStructures/segmentTree/SegmentTree.java index 21d9ad0c..5b92a469 100644 --- a/src/main/java/dataStructures/segmentTree/SegmentTree.java +++ b/src/main/java/dataStructures/segmentTree/SegmentTree.java @@ -2,27 +2,31 @@ /** * Data structure that is commonly used to solve range sum queries in O(logn) - * */ public class SegmentTree { - int[] tree; - int n; - int[] arr; + private final int[] tree; + private final int n; + private final int[] arr; + /** + * TODO documentation + * + * @param nums + */ public SegmentTree(int[] nums) { this.arr = nums; this.n = nums.length; this.tree = new int[(int) Math.pow(2, this.n)]; - buildTree(0, 0, this.n-1); + buildTree(0, 0, this.n - 1); } private void buildTree(int nodeIdx, int start, int end) { if (start == end) { this.tree[nodeIdx] = this.arr[start]; } else { - int mid = start + (end-start)/2; + int mid = start + (end - start) / 2; buildTree(nodeIdx * 2 + 1, start, mid); - buildTree(nodeIdx * 2 + 2, mid+1, end); + buildTree(nodeIdx * 2 + 2, mid + 1, end); tree[nodeIdx] = tree[nodeIdx * 2 + 1] + tree[nodeIdx * 2 + 2]; } } @@ -32,7 +36,7 @@ private void update(int nodeIdx, int start, int end, int index, int val) { this.tree[nodeIdx] = val; return; } - int mid = start + (end-start)/2; + int mid = start + (end - start) / 2; if (index <= mid) { update(nodeIdx * 2 + 1, start, mid, index, val); } else { @@ -41,12 +45,25 @@ private void update(int nodeIdx, int start, int end, int index, int val) { tree[nodeIdx] = tree[nodeIdx * 2 + 1] + tree[nodeIdx * 2 + 2]; } - public void update(int index, int val){ - update(0, 0, this.n-1, index, val); + /** + * TODO Documentation + * + * @param index + * @param val + */ + public void update(int index, int val) { + update(0, 0, this.n - 1, index, val); } + /** + * TODO documentation + * + * @param left + * @param right + * @return + */ public int sumRange(int left, int right) { - return query(0, 0, this.n-1, left, right); + return query(0, 0, this.n - 1, left, right); } private int query(int nodeIdx, int start, int end, int left, int right) { @@ -56,10 +73,8 @@ private int query(int nodeIdx, int start, int end, int left, int right) { if (left <= start && right >= end) { return this.tree[nodeIdx]; } else { - int m = start + (end-start)/2; - return query(nodeIdx * 2 + 1, start, m, left, right) + query(nodeIdx * 2 + 2, m+1, end, left, right); + int m = start + (end - start) / 2; + return query(nodeIdx * 2 + 1, start, m, left, right) + query(nodeIdx * 2 + 2, m + 1, end, left, right); } } - - -} \ No newline at end of file +} diff --git a/src/main/java/dataStructures/segmentTree/improvedSegmentTree/ImprovedSegmentTree.java b/src/main/java/dataStructures/segmentTree/improvedSegmentTree/ImprovedSegmentTree.java index e8811ff9..91ee0928 100644 --- a/src/main/java/dataStructures/segmentTree/improvedSegmentTree/ImprovedSegmentTree.java +++ b/src/main/java/dataStructures/segmentTree/improvedSegmentTree/ImprovedSegmentTree.java @@ -1,71 +1,84 @@ package dataStructures.segmentTree.improvedSegmentTree; + /** * Improved segment tree that does not require O(2^n) space complexity * Uses TreeNode to store relevant information for range sum queries */ +public class ImprovedSegmentTree { + private final TreeNode tree; - public class ImprovedSegmentTree { - TreeNode tree; + public ImprovedSegmentTree(int[] nums) { + this.tree = buildTree(nums, 0, nums.length - 1); + } private TreeNode buildTree(int[] nums, int start, int end) { TreeNode curr = new TreeNode(start, end); if (start == end) { - curr.sum = nums[start]; + curr.setSum(nums[start]); } else { - int m = start + (end-start)/2; + int m = start + (end - start) / 2; TreeNode left = buildTree(nums, start, m); - TreeNode right = buildTree(nums, m+1, end); - curr.left = left; - curr.right = right; - curr.sum = left.sum + right.sum; + TreeNode right = buildTree(nums, m + 1, end); + curr.setLeft(left); + curr.setRight(right); + curr.setSum(left.getSum() + right.getSum()); } return curr; } - public ImprovedSegmentTree(int[] nums) { - this.tree = buildTree(nums, 0, nums.length-1); - } - private void update(TreeNode root, int idx, int val) { - if (root.start == root.end) { - root.sum = val; + if (root.getStart() == root.getEnd()) { + root.setSum(val); } else { - if (idx <= root.left.end) { - update(root.left, idx, val); + if (idx <= root.getLeft().getEnd()) { + update(root.getLeft(), idx, val); } else { - update(root.right, idx, val); + update(root.getRight(), idx, val); } - root.sum = root.left.sum + root.right.sum; + root.setSum(root.getLeft().getSum() + root.getRight().getSum()); } } + /** + * Updates the values of the idx node to the given val. + * + * @param idx the idx of the intended node to be updated. + * @param val the new value. + */ public void update(int idx, int val) { - if (idx > this.tree.end) { + if (idx > this.tree.getEnd()) { return; } update(this.tree, idx, val); } private int query(TreeNode root, int left, int right) { - if (root.start == root.end) { - return root.sum; + if (root.getStart() == root.getEnd()) { + return root.getSum(); } else { - if (left <= root.start && root.end <= right) { - return root.sum; + if (left <= root.getStart() && root.getEnd() <= right) { + return root.getSum(); } int ans = 0; - int m = root.start + (root.end - root.start)/2; + int m = root.getStart() + (root.getEnd() - root.getStart()) / 2; if (left <= m) { - ans += query(root.left, left, Math.min(right, root.left.end)); + ans += query(root.getLeft(), left, Math.min(right, root.getLeft().getEnd())); } - if (m+1 <= right) { - ans += query(root.right, Math.max(left, root.right.start), right); + if (m + 1 <= right) { + ans += query(root.getRight(), Math.max(left, root.getRight().getStart()), right); } return ans; } } + /** + * Returns the sum of values in the range. + * + * @param left the lower range. + * @param right the upper range. + * @return the total sum of values in the range + */ public int sumRange(int left, int right) { return query(this.tree, left, right); } - } \ No newline at end of file +} diff --git a/src/main/java/dataStructures/segmentTree/improvedSegmentTree/TreeNode.java b/src/main/java/dataStructures/segmentTree/improvedSegmentTree/TreeNode.java index e9d7fc70..658f5cb0 100644 --- a/src/main/java/dataStructures/segmentTree/improvedSegmentTree/TreeNode.java +++ b/src/main/java/dataStructures/segmentTree/improvedSegmentTree/TreeNode.java @@ -2,15 +2,63 @@ /** * helper TreeNode class to store relevant information - * of binary segment tree + * of binary segment tree. Data class. */ public class TreeNode { - TreeNode left, right; - int sum; - int start, end; + private TreeNode left; + private TreeNode right; + private int sum; + private int start; + private int end; + /** + * Constructs a tree node. + * + * @param s start of the segment. + * @param e end of the segment + */ public TreeNode(int s, int e) { this.start = s; this.end = e; } -} \ No newline at end of file + + public TreeNode getLeft() { + return left; + } + + public void setLeft(TreeNode left) { + this.left = left; + } + + public TreeNode getRight() { + return right; + } + + public void setRight(TreeNode right) { + this.right = right; + } + + public int getSum() { + return sum; + } + + public void setSum(int sum) { + this.sum = sum; + } + + public int getStart() { + return start; + } + + public void setStart(int start) { + this.start = start; + } + + public int getEnd() { + return end; + } + + public void setEnd(int end) { + this.end = end; + } +} diff --git a/src/main/java/dataStructures/stack/README.md b/src/main/java/dataStructures/stack/README.md index a7e7c3c8..3c81c969 100644 --- a/src/main/java/dataStructures/stack/README.md +++ b/src/main/java/dataStructures/stack/README.md @@ -1,5 +1,6 @@ # Stack -Stack is a linear data structure that restricts + +Stack is a linear data structure that restricts the order in which operations can be performed on its elements. ![Stack data structure](https://cdn.programiz.com/sites/tutorial2program/files/stack.png) @@ -8,8 +9,8 @@ the order in which operations can be performed on its elements. ### Operation Orders -Stack follows a LIFO, last in first out order. -This means the most recent element +Stack follows a LIFO, last in first out order. +This means the most recent element added to the stack is the one operations are conducted on first. A [queue](../queue/README.md) conducts operations in the opposite order. @@ -17,7 +18,7 @@ A [queue](../queue/README.md) conducts operations in the opposite order. ## Analysis As a stack only interacts with the most recent element regardless of operation, -it keeps the pointer of the most recent element at hand, which is constantly updated as more +it keeps the pointer of the most recent element at hand, which is constantly updated as more elements are removed / added. This allows stack operations to only incur a *O(1)* time complexity. ## Notes @@ -25,16 +26,17 @@ elements are removed / added. This allows stack operations to only incur a *O(1) ### Stack vs Queues Stack and queues only differ in terms of operation order, you should aim to use a stack when -you want the most recent elements to be operated on. +you want the most recent elements to be operated on. Some situations where a stack would work well include build redo / undo systems and backtracking problems. On the other hand, a queue allows you to operate on elements that enter first. Some situations where this would be useful include Breadth First Search algorithms and task / resource allocation systems. ### Arrays vs Linked List + It is worth noting that stacks can be implemented with either a array or with a [linked list](../linkedList/README.md). In the context of ordered operations, the lookup is only restricted to the first element. -Hence, there is not much advantage in using a array, which only has a better lookup speed (*O(1)* time complexity) +Hence, there is not much advantage in using a array, which only has a better lookup speed (*O(1)* time complexity) to implement a stack. Especially when using a linked list to construct your stack -would allow you to grow or shrink the stack as you wish. \ No newline at end of file +would allow you to grow or shrink the stack as you wish. diff --git a/src/main/java/dataStructures/stack/Stack.java b/src/main/java/dataStructures/stack/Stack.java index bc5337cc..0ae15b31 100644 --- a/src/main/java/dataStructures/stack/Stack.java +++ b/src/main/java/dataStructures/stack/Stack.java @@ -1,84 +1,87 @@ package dataStructures.stack; -import java.util.List; import java.util.ArrayList; +import java.util.Collections; +import java.util.List; /** * Simple implementation of a stack using ArrayList. * Note: ArrayList can be implemented using LinkedList - * and hence the code below can be replicated with - * LinkedList. + * and hence the code below can be replicated with + * LinkedList. * Callable methods: - * push(T item) - * pop() - * peek() - * isEmpty() + * push(T item) + * pop() + * peek() + * isEmpty() * Note: Calling pop() or peek() on an empty stack returns null + * * @param generic type of objects to be stored in the stack */ public class Stack { - List stack; + private final List stack; - /** - * Constructor to create an empty stack - */ - public Stack() { - stack = new ArrayList(); - } + /** + * Constructor to create an empty stack + */ + public Stack() { + stack = new ArrayList<>(); + } - /** - * Constructor to create a stack with a given list holding the same type - */ - public Stack(List lst) { - stack = lst; - } + /** + * Constructor to create a stack with a given list holding the same type + */ + public Stack(List lst) { + stack = lst; + } - /** - * Constructor to create a stack given a sequence of objects of the same generic type - */ - public Stack(T ...lst) { - stack = new ArrayList(); - for (T obj : lst) { - stack.add(obj); + /** + * Constructor to create a stack given a sequence of objects of the same generic type + */ + public Stack(T... lst) { + stack = new ArrayList(); + Collections.addAll(stack, lst); } - } - /** - * @param item element to be pushed as the last element into the stack - */ - public void push(T item) { - stack.add(item); - } + /** + * @param item element to be pushed as the last element into the stack + */ + public void push(T item) { + stack.add(item); + } - /** - * Removes the last element from the stack. - * @return last element of the stack; null if the stack is empty - */ - public T pop() { - if (this.isEmpty()) { - return null; + /** + * Removes the last element from the stack. + * + * @return last element of the stack; null if the stack is empty + */ + public T pop() { + if (this.isEmpty()) { + return null; + } + // note that the operation below is O(1) when called on the last index + return stack.remove(stack.size() - 1); } - // note that the operation below is O(1) when called on the last index - return stack.remove(stack.size() - 1); - } - /** - * Displays the last element of the stack. - * @return last element of the stack; null if the stack is empty - */ - public T peek() { - if (this.isEmpty()) { - return null; + /** + * Displays the last element of the stack. + * + * @return last element of the stack; null if the stack is empty + */ + public T peek() { + if (this.isEmpty()) { + return null; + } + // similarly, below is an O(1) operation + return stack.get(stack.size() - 1); } - // similarly, below is an O(1) operation - return stack.get(stack.size() - 1); - } - /** - * Checks if the stack is empty. - * @return boolean value representing whether the stack is empty - */ - public boolean isEmpty() { - return stack.size() == 0; - } -} + /** + * Checks if the stack is empty. + * + * @return boolean value representing whether the stack is empty + */ + public boolean isEmpty() { + return stack.isEmpty(); + } +} diff --git a/src/main/java/dataStructures/trie/Trie.java b/src/main/java/dataStructures/trie/Trie.java index 385ae242..4e4b65d5 100644 --- a/src/main/java/dataStructures/trie/Trie.java +++ b/src/main/java/dataStructures/trie/Trie.java @@ -9,102 +9,105 @@ * prune(String word) */ public class Trie { - private TrieNode root; + private final TrieNode root; - public Trie() { - root = new TrieNode(); - } - - /** - * Insert a word into the trie; converts word to - * to lower-case characters before insertion. - * @param word the string to be inserted - */ - public void insert(String word) { - word = word.toLowerCase(); - System.out.println(String.format("~~~~~~~Inserting '%s'~~~~~~~", word)); - TrieNode node = root; - for (int i = 0; i < word.length(); i++) { - char curr = word.charAt(i); - if (!node.containsKey(curr)) { - node.insertKey(curr); - } - node = node.getNext(curr); // go to the subsequent node! + public Trie() { + root = new TrieNode(); } - node.makeEnd(); - } - /** - * Search for a word (converted to lower-case) in the trie. - * @param word the string to look for - * @return boolean representing whether the word was found - */ - public boolean search(String word) { - word.toLowerCase(); - System.out.println(String.format("~~~~~~~Searching '%s'~~~~~~~", word)); - TrieNode node = root; - for (int i = 0; i < word.length(); i++) { - char curr = word.charAt(i); - if (node.containsKey(curr)) { - node = node.getNext(curr); - } else { - return false; - } + /** + * Insert a word into the trie; converts word to + * to lower-case characters before insertion. + * + * @param word the string to be inserted + */ + public void insert(String word) { + word = word.toLowerCase(); + System.out.printf("~~~~~~~Inserting '%s'~~~~~~~%n", word); + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char curr = word.charAt(i); + if (!node.containsKey(curr)) { + node.insertKey(curr); + } + node = node.getNext(curr); // go to the subsequent node! + } + node.makeEnd(); } - return node.isEnd(); - } - /** - * Search for a prefix (converted to lower-case) in the trie. - * Note: very similar in implementation to search method - * except the search here does not need to look for end flag - * @param prefix the string to look for - * @return boolean representing whether the prefix exists - */ - public boolean startsWith(String prefix) { - prefix = prefix.toLowerCase(); - System.out.println(String.format("~~~~~~~Looking for prefix '%s'~~~~~~~", prefix)); - TrieNode node = root; - for (int i = 0; i < prefix.length(); i++) { - char curr = prefix.charAt(i); - if (node.containsKey(curr)) { - node = node.getNext(curr); - } else { - return false; - } + /** + * Search for a word (converted to lower-case) in the trie. + * + * @param word the string to look for + * @return boolean representing whether the word was found + */ + public boolean search(String word) { + word.toLowerCase(); + System.out.printf("~~~~~~~Searching '%s'~~~~~~~%n", word); + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char curr = word.charAt(i); + if (node.containsKey(curr)) { + node = node.getNext(curr); + } else { + return false; + } + } + return node.isEnd(); } - return true; - } - - /** - * Removes a word from the trie by toggling the end flag; - * if any of the end nodes (next nodes relative to current) - * do not hold further characters, repetitively prune the trie - * by removing these nodes from the hashmap of the current node. - * Note: This method is useful in optimizing searching for a set of known words - * especially when the data to be traversed has words that are similar in spelling/ - * repeated words which might have been previously found. - * @param word the word to be removed - */ - public void prune(String word) { - word = word.toLowerCase(); - System.out.println(String.format("~~~~~~~Removing '%s'~~~~~~~", word)); - TrieNode node = root; - TrieNode[] track = new TrieNode[word.length()]; - for (int i = 0; i < word.length(); i++) { - char curr = word.charAt(i); - track[i] = node; - node = node.getNext(curr); + + /** + * Search for a prefix (converted to lower-case) in the trie. + * Note: very similar in implementation to search method + * except the search here does not need to look for end flag + * + * @param prefix the string to look for + * @return boolean representing whether the prefix exists + */ + public boolean startsWith(String prefix) { + prefix = prefix.toLowerCase(); + System.out.printf("~~~~~~~Looking for prefix '%s'~~~~~~~%n", prefix); + TrieNode node = root; + for (int i = 0; i < prefix.length(); i++) { + char curr = prefix.charAt(i); + if (node.containsKey(curr)) { + node = node.getNext(curr); + } else { + return false; + } + } + return true; } - node.removeEnd(); - for (int i = word.length() - 1; i >= 0; i--) { - char curr = word.charAt(i); - if (track[i].getNext(curr).getCharacters().size() > 0) { - break; // done further nodes are required - } else { - track[i].getCharacters().remove(curr); - } + + /** + * Removes a word from the trie by toggling the end flag; + * if any of the end nodes (next nodes relative to current) + * do not hold further characters, repetitively prune the trie + * by removing these nodes from the hashmap of the current node. + * Note: This method is useful in optimizing searching for a set of known words + * especially when the data to be traversed has words that are similar in spelling/ + * repeated words which might have been previously found. + * + * @param word the word to be removed + */ + public void prune(String word) { + word = word.toLowerCase(); + System.out.printf("~~~~~~~Removing '%s'~~~~~~~%n", word); + TrieNode node = root; + TrieNode[] track = new TrieNode[word.length()]; + for (int i = 0; i < word.length(); i++) { + char curr = word.charAt(i); + track[i] = node; + node = node.getNext(curr); + } + node.removeEnd(); + for (int i = word.length() - 1; i >= 0; i--) { + char curr = word.charAt(i); + if (track[i].getNext(curr).getCharacters().size() > 0) { + break; // done further nodes are required + } else { + track[i].getCharacters().remove(curr); + } + } } - return; - } } diff --git a/src/main/java/dataStructures/trie/TrieNode.java b/src/main/java/dataStructures/trie/TrieNode.java index 5ae0fe37..1f5ae389 100644 --- a/src/main/java/dataStructures/trie/TrieNode.java +++ b/src/main/java/dataStructures/trie/TrieNode.java @@ -1,79 +1,85 @@ package dataStructures.trie; -import java.util.*; +import java.util.HashMap; +import java.util.Map; /** * Implementation of TrieNode for Trie structure (not restricited to alphabets) * Each TrieNode has the following attributes: - * characters: HashMap whose keys represent the available characters and - * values represent the nodes to select the next characters - * NOTE: Presence of a character in the hashmap means that this character - * is part of the trie structure and can be used to continue forming a searched word - * or return the accumulation of letters as the word if the end flag is toggled to true + * characters: HashMap whose keys represent the available characters and + * values represent the nodes to select the next characters + * NOTE: Presence of a character in the hashmap means that this character + * is part of the trie structure and can be used to continue forming a searched word + * or return the accumulation of letters as the word if the end flag is toggled to true * end : Tells us whether the current TrieNode is actually a terminating flag */ public class TrieNode { - private Map characters; - private boolean end; + private final Map characters; + private boolean end; - public TrieNode() { - characters = new HashMap<>(); - } + public TrieNode() { + characters = new HashMap<>(); + } - /** - * Checks if node has a character. - * @param c character to check for presence - * @return boolean representing if the character exists - */ - public boolean containsKey(char c) { - return characters.containsKey(c); - } + /** + * Checks if node has a character. + * + * @param c character to check for presence + * @return boolean representing if the character exists + */ + public boolean containsKey(char c) { + return characters.containsKey(c); + } - /** - * Get the next node at the index represented by the character. - * @param c character whose index holds the desired next node - * @return the desired node at that index - */ - public TrieNode getNext(char c) { - return characters.get(c); - } + /** + * Get the next node at the index represented by the character. + * + * @param c character whose index holds the desired next node + * @return the desired node at that index + */ + public TrieNode getNext(char c) { + return characters.get(c); + } - /** - * Get the avaialble characters can be used as the next letter from the current node. - * @return hashmap of characters and next nodes - */ - public Map getCharacters() { - return characters; - } + /** + * Get the avaialble characters can be used as the next letter from the current node. + * + * @return hashmap of characters and next nodes + */ + public Map getCharacters() { + return characters; + } - /** - * Inserts a character to the current TrieNode. - * @param c character whose index represents where to insert the node - */ - public void insertKey(char c) { - characters.put(c, new TrieNode()); - } + /** + * Inserts a character to the current TrieNode. + * + * @param c character whose index represents where to insert the node + */ + public void insertKey(char c) { + characters.put(c, new TrieNode()); + } - /** - * Checks if the current TrieNode is a terminating flag. - * @return boolean value - */ - public boolean isEnd() { - return end; - } + /** + * Checks if the current TrieNode is a terminating flag. + * + * @return boolean value + */ + public boolean isEnd() { + return end; + } - /** - * Make the current TrieNode a terminating flag/node. - */ - public void makeEnd() { - end = true; - } + /** + * Make the current TrieNode a terminating flag/node. + */ + public void makeEnd() { + end = true; + } - /** - * Remove terminating flag from current TrieNode. - */ - public void removeEnd() { - end = false; - } + /** + * Remove terminating flag from current TrieNode. + */ + public void removeEnd() { + end = false; + } } diff --git a/src/main/java/dataStructures/trie/legacy/Trie.java b/src/main/java/dataStructures/trie/legacy/Trie.java index 60c8f675..3f47e008 100644 --- a/src/main/java/dataStructures/trie/legacy/Trie.java +++ b/src/main/java/dataStructures/trie/legacy/Trie.java @@ -8,66 +8,69 @@ * startsWith(String prefix) */ public class Trie { - private TrieNode root; + private final TrieNode root; - public Trie() { - root = new TrieNode(); - } + public Trie() { + root = new TrieNode(); + } - /** - * Insert a word into the trie. - * @param word the string to be inserted - */ - public void insert(String word) { - System.out.println(String.format("~~~~~~~Inserting %s~~~~~~~", word)); - TrieNode node = root; - for (int i = 0; i < word.length(); i++) { - char curr = word.charAt(i); - if (!node.containsKey(curr)) { - node.insertKey(curr); - } - node = node.getNext(curr); // go to the subsequent node! + /** + * Insert a word into the trie. + * + * @param word the string to be inserted + */ + public void insert(String word) { + System.out.printf("~~~~~~~Inserting %s~~~~~~~%n", word); + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char curr = word.charAt(i); + if (!node.containsKey(curr)) { + node.insertKey(curr); + } + node = node.getNext(curr); // go to the subsequent node! + } + node.makeEnd(); } - node.makeEnd(); - } - /** - * Search for a word in the trie. - * @param word the string to look for - * @return boolean representing whether the word was found - */ - public boolean search(String word) { - System.out.println(String.format("~~~~~~~Searching %s~~~~~~~", word)); - TrieNode node = root; - for (int i = 0; i < word.length(); i++) { - char curr = word.charAt(i); - if (node.containsKey(curr)) { - node = node.getNext(curr); - } else { - return false; - } + /** + * Search for a word in the trie. + * + * @param word the string to look for + * @return boolean representing whether the word was found + */ + public boolean search(String word) { + System.out.printf("~~~~~~~Searching %s~~~~~~~%n", word); + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char curr = word.charAt(i); + if (node.containsKey(curr)) { + node = node.getNext(curr); + } else { + return false; + } + } + return node.isEnd(); } - return node.isEnd(); - } - /** - * Search for a prefix in the trie. - * Note: very similar in implementation to search method - * except the search here does not need to look for end flag - * @param prefix the string to look for - * @return boolean representing whether the prefix exists - */ - public boolean startsWith(String prefix) { - System.out.println(String.format("~~~~~~~Looking for prefix %s~~~~~~~", prefix)); - TrieNode node = root; - for (int i = 0; i < prefix.length(); i++) { - char curr = prefix.charAt(i); - if (node.containsKey(curr)) { - node = node.getNext(curr); - } else { - return false; - } + /** + * Search for a prefix in the trie. + * Note: very similar in implementation to search method + * except the search here does not need to look for end flag + * + * @param prefix the string to look for + * @return boolean representing whether the prefix exists + */ + public boolean startsWith(String prefix) { + System.out.printf("~~~~~~~Looking for prefix %s~~~~~~~%n", prefix); + TrieNode node = root; + for (int i = 0; i < prefix.length(); i++) { + char curr = prefix.charAt(i); + if (node.containsKey(curr)) { + node = node.getNext(curr); + } else { + return false; + } + } + return true; } - return true; - } } diff --git a/src/main/java/dataStructures/trie/legacy/TrieNode.java b/src/main/java/dataStructures/trie/legacy/TrieNode.java index 7986f6a1..ac30cedc 100644 --- a/src/main/java/dataStructures/trie/legacy/TrieNode.java +++ b/src/main/java/dataStructures/trie/legacy/TrieNode.java @@ -4,60 +4,64 @@ * Implementation of TrieNode for Trie structure (assumes chracters are lower-cased alphabets). * Each TrieNode has the following attributes: * TrieNode Array: Indices of the array represent characters, with a starting from 0 and z ending at 26 - * NOTE: Presence of a node at a given index means that character represented by that - * index is present / is part of the trie structure - * (regardless whether the node has end flag toggled to True) + * NOTE: Presence of a node at a given index means that character represented by that + * index is present / is part of the trie structure + * (regardless whether the node has end flag toggled to True) * Capacity : The size of the TrieNode array * end flag : Tells us whether the current TrieNode is actually a terminating flag */ public class TrieNode { - private TrieNode[] next; - private final int CAPACITY = 26; // implementation assumes lower-cased alphabets - private boolean end; - - public TrieNode() { - next = new TrieNode[CAPACITY]; - } - - /** - * Checks if node has a character. - * @param c character to check for presence - * @return boolean representing if the character exists - */ - public boolean containsKey(char c) { - return next[c-'a'] != null; - } - - /** - * Get the next node at the index represented by the character. - * @param c character whose index holds the desired next node - * @return the desired node at that index - */ - public TrieNode getNext(char c) { - return next[c-'a']; - } - - /** - * Inserts a character to the current TrieNode. - * @param c character whose index represents where to insert the node - */ - public void insertKey(char c) { - next[c-'a'] = new TrieNode(); - } - - /** - * Checks if the current TrieNode is a terminating flag. - * @return boolean value - */ - public boolean isEnd() { - return end; - } - - /** - * Make the current TrieNode a terminating flag/node. - */ - public void makeEnd() { - end = true; - } + private static final int CAPACITY = 26; // implementation assumes lower-cased alphabets + private final TrieNode[] next; + private boolean end; + + public TrieNode() { + next = new TrieNode[CAPACITY]; + } + + /** + * Checks if node has a character. + * + * @param c character to check for presence + * @return boolean representing if the character exists + */ + public boolean containsKey(char c) { + return next[c - 'a'] != null; + } + + /** + * Get the next node at the index represented by the character. + * + * @param c character whose index holds the desired next node + * @return the desired node at that index + */ + public TrieNode getNext(char c) { + return next[c - 'a']; + } + + /** + * Inserts a character to the current TrieNode. + * + * @param c character whose index represents where to insert the node + */ + public void insertKey(char c) { + next[c - 'a'] = new TrieNode(); + } + + /** + * Checks if the current TrieNode is a terminating flag. + * + * @return boolean value + */ + public boolean isEnd() { + return end; + } + + /** + * Make the current TrieNode a terminating flag/node. + */ + public void makeEnd() { + end = true; + } } diff --git a/src/test/README.md b/src/test/README.md index ac834a2a..dd548eaf 100644 --- a/src/test/README.md +++ b/src/test/README.md @@ -2,8 +2,11 @@ This folder contains the test code for the corresponding algorithms and data structures in **main/** folder. -Note to contributors: +Note to contributors: + - Naming convention for test methods: featureUnderTest_testScenario_expectedBehaviour() -The above is just a guide, slight deviation is fine as long as its clear. eg. no need to specifically mention "ordinary array" if testing some sorting algor. - - camelCase - - eg. sortList_emptyList_shouldReturnNoChange(), test_mergeSort_shouldReturnSortedArray, test{algor}_emptyList_shouldReturnEmptyResult + The above is just a guide, slight deviation is fine as long as its clear. eg. no need to specifically mention " + ordinary array" if testing some sorting algor. + - camelCase + - eg. sortList_emptyList_shouldReturnNoChange(), test_mergeSort_shouldReturnSortedArray, test{algor}_ + emptyList_shouldReturnEmptyResult diff --git a/src/test/java/algorithms/binarySearch/BinarySearchTest.java b/src/test/java/algorithms/binarySearch/BinarySearchTest.java index 0aeb9737..3d09ee9a 100644 --- a/src/test/java/algorithms/binarySearch/BinarySearchTest.java +++ b/src/test/java/algorithms/binarySearch/BinarySearchTest.java @@ -8,41 +8,41 @@ * Test cases for {@link BinarySearch}. */ public class BinarySearchTest { - @Test - public void test_binarySearch() { - // Test 1: even number of elements - int[] firstArray = {1, 5, 10, 12}; - int firstResult = BinarySearch.search(firstArray, 1); - - // Test 2: odd number of elements - int[] secondArray = {1, 5, 10, 11, 12}; - int secondResult = BinarySearch.search(secondArray, 11); - - // Test 3: target not in array - int[] thirdArray = {1, 5, 10, 11, 12}; - int thirdResult = BinarySearch.search(thirdArray, 3); - - assertEquals(0, firstResult); - assertEquals(3, secondResult); - assertEquals(-1, thirdResult); - } - - @Test - public void test_binarySearchTemplated() { - // Test 1: even number of elements - int[] firstArray = {1, 5, 10, 12}; - int firstResult = BinarySearchTemplated.search(firstArray, 1); - - // Test 2: odd number of elements - int[] secondArray = {1, 5, 10, 11, 12}; - int secondResult = BinarySearchTemplated.search(secondArray, 11); - - // Test 3: target not in array - int[] thirdArray = {1, 5, 10, 11, 12}; - int thirdResult = BinarySearchTemplated.search(thirdArray, 3); - - assertEquals(0, firstResult); - assertEquals(3, secondResult); - assertEquals(-1, thirdResult); - } + @Test + public void test_binarySearch() { + // Test 1: even number of elements + int[] firstArray = {1, 5, 10, 12}; + int firstResult = BinarySearch.search(firstArray, 1); + + // Test 2: odd number of elements + int[] secondArray = {1, 5, 10, 11, 12}; + int secondResult = BinarySearch.search(secondArray, 11); + + // Test 3: target not in array + int[] thirdArray = {1, 5, 10, 11, 12}; + int thirdResult = BinarySearch.search(thirdArray, 3); + + assertEquals(0, firstResult); + assertEquals(3, secondResult); + assertEquals(-1, thirdResult); + } + + @Test + public void test_binarySearchTemplated() { + // Test 1: even number of elements + int[] firstArray = {1, 5, 10, 12}; + int firstResult = BinarySearchTemplated.search(firstArray, 1); + + // Test 2: odd number of elements + int[] secondArray = {1, 5, 10, 11, 12}; + int secondResult = BinarySearchTemplated.search(secondArray, 11); + + // Test 3: target not in array + int[] thirdArray = {1, 5, 10, 11, 12}; + int thirdResult = BinarySearchTemplated.search(thirdArray, 3); + + assertEquals(0, firstResult); + assertEquals(3, secondResult); + assertEquals(-1, thirdResult); + } } diff --git a/src/test/java/algorithms/graphs/BreadthFirstSearchTest.java b/src/test/java/algorithms/graphs/BreadthFirstSearchTest.java new file mode 100644 index 00000000..33712253 --- /dev/null +++ b/src/test/java/algorithms/graphs/BreadthFirstSearchTest.java @@ -0,0 +1,90 @@ +package algorithms.graphs; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +import algorithms.graphs.util.BinaryTreeNode; +import algorithms.graphs.util.GraphNode; + +/** + * Test cases for {@link BreadthFirstSearch}. + */ +public class BreadthFirstSearchTest { + + @Test + public void bfs_levelOrderTraversal_shouldReturnAccurate() { + // empty tree + List firstList = new ArrayList<>(); + BinaryTreeNode root1 = null; + List firstResult = BreadthFirstSearch.levelOrder(root1); + + //standard tree + // 1 + // / \ + // 2 3 + // / \ + // 4 5 + List secondList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); + BinaryTreeNode rootRight2 = new BinaryTreeNode(3, new BinaryTreeNode(4), new BinaryTreeNode(5)); + BinaryTreeNode root2 = new BinaryTreeNode(1, new BinaryTreeNode(2), rootRight2); + List secondResult = BreadthFirstSearch.levelOrder(root2); + + //standard tree 2 + // 1 + // / \ + // 2 7 + // / \ + // 3 5 + // / / + // 4 6 + List thirdList = new ArrayList<>(Arrays.asList(1, 2, 7, 3, 5, 4, 6)); + BinaryTreeNode rootLeft3 = + new BinaryTreeNode(2, new BinaryTreeNode(3, new BinaryTreeNode(4), null), + new BinaryTreeNode(5, new BinaryTreeNode(6), null) + ); + BinaryTreeNode root3 = new BinaryTreeNode(1, rootLeft3, new BinaryTreeNode(7)); + List thirdResult = BreadthFirstSearch.levelOrder(root3); + + assert firstResult.equals(firstList); + assert secondResult.equals(secondList); + assert thirdResult.equals(thirdList); + } + + @Test + public void bfs_friendHops_shouldReturnAccurate() { + + // Tests based on the following friend graph: + // Andre ------ Ben ------- Diana ------- Evelyn ------- Gerald + // | | | + // Cathy Felix-------------------------- + // | + // Harold ------ Iris Anonymous + GraphNode andre = new GraphNode<>("andre"); + GraphNode ben = new GraphNode<>("ben"); + GraphNode cathy = new GraphNode<>("cathy"); + GraphNode diana = new GraphNode<>("diana"); + GraphNode evelyn = new GraphNode<>("evelyn"); + GraphNode felix = new GraphNode<>("felix"); + GraphNode gerald = new GraphNode<>("gerald"); + GraphNode harold = new GraphNode<>("harold"); + GraphNode iris = new GraphNode<>("iris"); + GraphNode anonymous = new GraphNode<>("anonymous"); + GraphNode.connect(andre, ben); + GraphNode.connect(andre, cathy); + GraphNode.connect(cathy, harold); + GraphNode.connect(harold, iris); + GraphNode.connect(ben, felix); + GraphNode.connect(ben, diana); + GraphNode.connect(diana, evelyn); + GraphNode.connect(evelyn, felix); + GraphNode.connect(evelyn, gerald); + + assert BreadthFirstSearch.friendHops(anonymous, diana) == -1; + assert BreadthFirstSearch.friendHops(iris, gerald) == 7; + assert BreadthFirstSearch.friendHops(andre, gerald) == 4; + assert BreadthFirstSearch.friendHops(felix, harold) == 4; + } +} diff --git a/src/test/java/algorithms/graphs/DepthFirstSearchTest.java b/src/test/java/algorithms/graphs/DepthFirstSearchTest.java new file mode 100644 index 00000000..8ac80aec --- /dev/null +++ b/src/test/java/algorithms/graphs/DepthFirstSearchTest.java @@ -0,0 +1,132 @@ +package algorithms.graphs; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +import algorithms.graphs.util.BinaryTreeNode; + +/** + * Test cases for {@link DepthFirstSearch}. + */ +public class DepthFirstSearchTest { + + @Test + public void dfs_preOrderTraversal_shouldReturnAccurate() { + // empty tree + List firstList = new ArrayList<>(); + BinaryTreeNode root1 = null; + List firstResult = DepthFirstSearch.preOrder(root1); + + //standard tree + // 1 + // / \ + // 2 3 + // / \ + // 4 5 + List secondList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); + BinaryTreeNode rootRight2 = new BinaryTreeNode(3, new BinaryTreeNode(4), new BinaryTreeNode(5)); + BinaryTreeNode root2 = new BinaryTreeNode(1, new BinaryTreeNode(2), rootRight2); + List secondResult = DepthFirstSearch.preOrder(root2); + + //standard tree 2 + // 1 + // / \ + // 2 7 + // / \ + // 3 5 + // / / + // 4 6 + List thirdList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7)); + BinaryTreeNode rootLeft3 = + new BinaryTreeNode(2, new BinaryTreeNode(3, new BinaryTreeNode(4), null), + new BinaryTreeNode(5, new BinaryTreeNode(6), null) + ); + BinaryTreeNode root3 = new BinaryTreeNode(1, rootLeft3, new BinaryTreeNode(7)); + List thirdResult = DepthFirstSearch.preOrder(root3); + + assert firstResult.equals(firstList); + assert secondResult.equals(secondList); + assert thirdResult.equals(thirdList); + } + + @Test + public void dfs_inOrderTraversal_shouldReturnAccurate() { + // empty tree + List firstList = new ArrayList<>(); + BinaryTreeNode root1 = null; + List firstResult = DepthFirstSearch.inOrder(root1); + + //standard tree + // 1 + // / \ + // 2 3 + // / \ + // 4 5 + List secondList = new ArrayList<>(Arrays.asList(2, 1, 4, 3, 5)); + BinaryTreeNode rootRight2 = new BinaryTreeNode(3, new BinaryTreeNode(4), new BinaryTreeNode(5)); + BinaryTreeNode root2 = new BinaryTreeNode(1, new BinaryTreeNode(2), rootRight2); + List secondResult = DepthFirstSearch.inOrder(root2); + + //standard tree 2 + // 1 + // / \ + // 2 7 + // / \ + // 3 5 + // / / + // 4 6 + List thirdList = new ArrayList<>(Arrays.asList(4, 3, 2, 6, 5, 1, 7)); + BinaryTreeNode rootLeft3 = + new BinaryTreeNode(2, new BinaryTreeNode(3, new BinaryTreeNode(4), null), + new BinaryTreeNode(5, new BinaryTreeNode(6), null) + ); + BinaryTreeNode root3 = new BinaryTreeNode(1, rootLeft3, new BinaryTreeNode(7)); + List thirdResult = DepthFirstSearch.inOrder(root3); + + assert firstResult.equals(firstList); + assert secondResult.equals(secondList); + assert thirdResult.equals(thirdList); + } + + @Test + public void dfs_postOrderTraversal_shouldReturnAccurate() { + // empty tree + List firstList = new ArrayList<>(); + BinaryTreeNode root1 = null; + List firstResult = DepthFirstSearch.inOrder(root1); + + //standard tree + // 1 + // / \ + // 2 3 + // / \ + // 4 5 + List secondList = new ArrayList<>(Arrays.asList(2, 4, 5, 3, 1)); + BinaryTreeNode rootRight2 = new BinaryTreeNode(3, new BinaryTreeNode(4), new BinaryTreeNode(5)); + BinaryTreeNode root2 = new BinaryTreeNode(1, new BinaryTreeNode(2), rootRight2); + List secondResult = DepthFirstSearch.postOrder(root2); + + //standard tree 2 + // 1 + // / \ + // 2 7 + // / \ + // 3 5 + // / / + // 4 6 + List thirdList = new ArrayList<>(Arrays.asList(4, 3, 6, 5, 2, 7, 1)); + BinaryTreeNode rootLeft3 = + new BinaryTreeNode(2, new BinaryTreeNode(3, new BinaryTreeNode(4), null), + new BinaryTreeNode(5, new BinaryTreeNode(6), null) + ); + BinaryTreeNode root3 = new BinaryTreeNode(1, rootLeft3, new BinaryTreeNode(7)); + List thirdResult = DepthFirstSearch.postOrder(root3); + + assert firstResult.equals(firstList); + assert secondResult.equals(secondList); + assert thirdResult.equals(thirdList); + } +} diff --git a/src/test/java/algorithms/graphs/breadthFirstSearchTest.java b/src/test/java/algorithms/graphs/breadthFirstSearchTest.java deleted file mode 100644 index 7edc427c..00000000 --- a/src/test/java/algorithms/graphs/breadthFirstSearchTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package algorithms.graphs; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.junit.Test; - -import algorithms.graphs.util.BinaryTreeNode; -import algorithms.graphs.util.GraphNode; - -/** - * Test cases for {@link breadthFirstSearch}. - */ -public class BreadthFirstSearchTest { - - @Test - public void bfs_levelOrderTraversal_shouldReturnAccurate() { - // empty tree - List firstList = new ArrayList<>(); - BinaryTreeNode root1 = null; - List firstResult = breadthFirstSearch.levelOrder(root1); - - //standard tree - // 1 - // / \ - // 2 3 - // / \ - // 4 5 - List secondList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); - BinaryTreeNode rootRight2 = new BinaryTreeNode(3, new BinaryTreeNode(4), new BinaryTreeNode(5)); - BinaryTreeNode root2 = new BinaryTreeNode(1, new BinaryTreeNode(2), rootRight2); - List secondResult = breadthFirstSearch.levelOrder(root2); - - //standard tree 2 - // 1 - // / \ - // 2 7 - // / \ - // 3 5 - // / / - // 4 6 - List thirdList = new ArrayList<>(Arrays.asList(1, 2, 7, 3, 5, 4, 6)); - BinaryTreeNode rootLeft3 = - new BinaryTreeNode(2, new BinaryTreeNode(3, new BinaryTreeNode(4), null), - new BinaryTreeNode(5, new BinaryTreeNode(6), null)); - BinaryTreeNode root3 = new BinaryTreeNode(1, rootLeft3, new BinaryTreeNode(7)); - List thirdResult = breadthFirstSearch.levelOrder(root3); - - assert firstResult.equals(firstList); - assert secondResult.equals(secondList); - assert thirdResult.equals(thirdList); - } - - @Test - public void bfs_friendHops_shouldReturnAccurate() { - - // Tests based on the following friend graph: - // Andre ------ Ben ------- Diana ------- Evelyn ------- Gerald - // | | | - // Cathy Felix-------------------------- - // | - // Harold ------ Iris Anonymous - GraphNode andre = new GraphNode<>("andre"); - GraphNode ben = new GraphNode<>("ben"); - GraphNode cathy = new GraphNode<>("cathy"); - GraphNode diana = new GraphNode<>("diana"); - GraphNode evelyn = new GraphNode<>("evelyn"); - GraphNode felix = new GraphNode<>("felix"); - GraphNode gerald = new GraphNode<>("gerald"); - GraphNode harold = new GraphNode<>("harold"); - GraphNode iris = new GraphNode<>("iris"); - GraphNode anonymous = new GraphNode<>("anonymous"); - GraphNode.connect(andre, ben); - GraphNode.connect(andre, cathy); - GraphNode.connect(cathy, harold); - GraphNode.connect(harold, iris); - GraphNode.connect(ben, felix); - GraphNode.connect(ben, diana); - GraphNode.connect(diana, evelyn); - GraphNode.connect(evelyn, felix); - GraphNode.connect(evelyn, gerald); - - assert breadthFirstSearch.friendHops(anonymous, diana) == -1; - assert breadthFirstSearch.friendHops(iris, gerald) == 7; - assert breadthFirstSearch.friendHops(andre, gerald) == 4; - assert breadthFirstSearch.friendHops(felix, harold) == 4; - } -} diff --git a/src/test/java/algorithms/graphs/depthFirstSearchTest.java b/src/test/java/algorithms/graphs/depthFirstSearchTest.java deleted file mode 100644 index df95f5b4..00000000 --- a/src/test/java/algorithms/graphs/depthFirstSearchTest.java +++ /dev/null @@ -1,129 +0,0 @@ -package algorithms.graphs; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.junit.Test; - -import algorithms.graphs.util.BinaryTreeNode; - -/** - * Test cases for {@link depthFirstSearch}. - */ -public class DepthFirstSearchTest { - - @Test - public void dfs_preOrderTraversal_shouldReturnAccurate() { - // empty tree - List firstList = new ArrayList<>(); - BinaryTreeNode root1 = null; - List firstResult = depthFirstSearch.preOrder(root1); - - //standard tree - // 1 - // / \ - // 2 3 - // / \ - // 4 5 - List secondList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); - BinaryTreeNode rootRight2 = new BinaryTreeNode(3, new BinaryTreeNode(4), new BinaryTreeNode(5)); - BinaryTreeNode root2 = new BinaryTreeNode(1, new BinaryTreeNode(2), rootRight2); - List secondResult = depthFirstSearch.preOrder(root2); - - //standard tree 2 - // 1 - // / \ - // 2 7 - // / \ - // 3 5 - // / / - // 4 6 - List thirdList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7)); - BinaryTreeNode rootLeft3 = - new BinaryTreeNode(2, new BinaryTreeNode(3, new BinaryTreeNode(4), null), - new BinaryTreeNode(5, new BinaryTreeNode(6), null)); - BinaryTreeNode root3 = new BinaryTreeNode(1, rootLeft3, new BinaryTreeNode(7)); - List thirdResult = depthFirstSearch.preOrder(root3); - - assert firstResult.equals(firstList); - assert secondResult.equals(secondList); - assert thirdResult.equals(thirdList); - } - - @Test - public void dfs_inOrderTraversal_shouldReturnAccurate() { - // empty tree - List firstList = new ArrayList<>(); - BinaryTreeNode root1 = null; - List firstResult = depthFirstSearch.inOrder(root1); - - //standard tree - // 1 - // / \ - // 2 3 - // / \ - // 4 5 - List secondList = new ArrayList<>(Arrays.asList(2, 1, 4, 3, 5)); - BinaryTreeNode rootRight2 = new BinaryTreeNode(3, new BinaryTreeNode(4), new BinaryTreeNode(5)); - BinaryTreeNode root2 = new BinaryTreeNode(1, new BinaryTreeNode(2), rootRight2); - List secondResult = depthFirstSearch.inOrder(root2); - - //standard tree 2 - // 1 - // / \ - // 2 7 - // / \ - // 3 5 - // / / - // 4 6 - List thirdList = new ArrayList<>(Arrays.asList(4, 3, 2, 6, 5, 1, 7)); - BinaryTreeNode rootLeft3 = - new BinaryTreeNode(2, new BinaryTreeNode(3, new BinaryTreeNode(4), null), - new BinaryTreeNode(5, new BinaryTreeNode(6), null)); - BinaryTreeNode root3 = new BinaryTreeNode(1, rootLeft3, new BinaryTreeNode(7)); - List thirdResult = depthFirstSearch.inOrder(root3); - - assert firstResult.equals(firstList); - assert secondResult.equals(secondList); - assert thirdResult.equals(thirdList); - } - - @Test - public void dfs_postOrderTraversal_shouldReturnAccurate() { - // empty tree - List firstList = new ArrayList<>(); - BinaryTreeNode root1 = null; - List firstResult = depthFirstSearch.inOrder(root1); - - //standard tree - // 1 - // / \ - // 2 3 - // / \ - // 4 5 - List secondList = new ArrayList<>(Arrays.asList(2, 4, 5, 3, 1)); - BinaryTreeNode rootRight2 = new BinaryTreeNode(3, new BinaryTreeNode(4), new BinaryTreeNode(5)); - BinaryTreeNode root2 = new BinaryTreeNode(1, new BinaryTreeNode(2), rootRight2); - List secondResult = depthFirstSearch.postOrder(root2); - - //standard tree 2 - // 1 - // / \ - // 2 7 - // / \ - // 3 5 - // / / - // 4 6 - List thirdList = new ArrayList<>(Arrays.asList(4, 3, 6, 5, 2, 7, 1)); - BinaryTreeNode rootLeft3 = - new BinaryTreeNode(2, new BinaryTreeNode(3, new BinaryTreeNode(4), null), - new BinaryTreeNode(5, new BinaryTreeNode(6), null)); - BinaryTreeNode root3 = new BinaryTreeNode(1, rootLeft3, new BinaryTreeNode(7)); - List thirdResult = depthFirstSearch.postOrder(root3); - - assert firstResult.equals(firstList); - assert secondResult.equals(secondList); - assert thirdResult.equals(thirdList); - } -} diff --git a/src/test/java/algorithms/patternFinding/KmpTest.java b/src/test/java/algorithms/patternFinding/KmpTest.java index 14b8d301..1d795d64 100644 --- a/src/test/java/algorithms/patternFinding/KmpTest.java +++ b/src/test/java/algorithms/patternFinding/KmpTest.java @@ -11,53 +11,53 @@ * Test cases for {@link KMP}. */ public class KmpTest { - @Test - public void test_findOccurrences_shouldReturnStartIndices() { - String seq = "abclaabcabcabc"; - String pattern = "abc"; - - List indices = KMP.findOccurrences(seq, pattern); - List expected = Arrays.asList(0, 5, 8, 11); - Assert.assertEquals(expected, indices); - } - - @Test - public void testEmptySequence_findOccurrences_shouldReturnStartIndices() { - String seq = ""; - String pattern = "a"; - - List indices = KMP.findOccurrences(seq, pattern); - List expected = new ArrayList<>(); - Assert.assertEquals(expected, indices); - } - - @Test - public void testNoOccurence_findOccurrences_shouldReturnStartIndices() { - String seq = "abcabcabc"; - String pattern = "noway"; - - List indices = KMP.findOccurrences(seq, pattern); - List expected = new ArrayList<>(); - Assert.assertEquals(expected, indices); - } - - @Test - public void testRepeatPatternOnly_findOccurrences_shouldReturnStartIndices() { - String seq = "abcabcabcabcabc"; - String pattern = "abc"; - - List indices = KMP.findOccurrences(seq, pattern); - List expected = Arrays.asList(0, 3, 6, 9, 12); - Assert.assertEquals(expected, indices); - } - - @Test - public void testAllSame_findOccurrences_shouldReturnStartIndices() { - String seq = "aaaaaaaaaaaaa"; - String pattern = "aa"; - - List indices = KMP.findOccurrences(seq, pattern); - List expected = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); - Assert.assertEquals(expected, indices); - } + @Test + public void test_findOccurrences_shouldReturnStartIndices() { + String seq = "abclaabcabcabc"; + String pattern = "abc"; + + List indices = KMP.findOccurrences(seq, pattern); + List expected = Arrays.asList(0, 5, 8, 11); + Assert.assertEquals(expected, indices); + } + + @Test + public void testEmptySequence_findOccurrences_shouldReturnStartIndices() { + String seq = ""; + String pattern = "a"; + + List indices = KMP.findOccurrences(seq, pattern); + List expected = new ArrayList<>(); + Assert.assertEquals(expected, indices); + } + + @Test + public void testNoOccurence_findOccurrences_shouldReturnStartIndices() { + String seq = "abcabcabc"; + String pattern = "noway"; + + List indices = KMP.findOccurrences(seq, pattern); + List expected = new ArrayList<>(); + Assert.assertEquals(expected, indices); + } + + @Test + public void testRepeatPatternOnly_findOccurrences_shouldReturnStartIndices() { + String seq = "abcabcabcabcabc"; + String pattern = "abc"; + + List indices = KMP.findOccurrences(seq, pattern); + List expected = Arrays.asList(0, 3, 6, 9, 12); + Assert.assertEquals(expected, indices); + } + + @Test + public void testAllSame_findOccurrences_shouldReturnStartIndices() { + String seq = "aaaaaaaaaaaaa"; + String pattern = "aa"; + + List indices = KMP.findOccurrences(seq, pattern); + List expected = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11); + Assert.assertEquals(expected, indices); + } } diff --git a/src/test/java/algorithms/sorting/bubbleSort/BubbleSortTest.java b/src/test/java/algorithms/sorting/bubbleSort/BubbleSortTest.java index 828fba30..1ee78c78 100644 --- a/src/test/java/algorithms/sorting/bubbleSort/BubbleSortTest.java +++ b/src/test/java/algorithms/sorting/bubbleSort/BubbleSortTest.java @@ -10,32 +10,36 @@ * Test cases for {@link BubbleSort}. */ public class BubbleSortTest { - @Test - public void test_bubbleSort_shouldReturnSortedArray() { - int[] firstArray = - new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, - 85, 30}; - int[] firstResult = BubbleSort.sort(Arrays.copyOf(firstArray, firstArray.length)); - - int[] secondArray = - new int[] {9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10}; - int[] secondResult = BubbleSort.sort(Arrays.copyOf(secondArray, secondArray.length)); - - int[] thirdArray = new int[] {}; - int[] thirdResult = BubbleSort.sort(Arrays.copyOf(thirdArray, thirdArray.length)); - - int[] fourthArray = new int[] {1}; - int[] fourthResult = BubbleSort.sort(Arrays.copyOf(fourthArray, fourthArray.length)); - - Arrays.sort(firstArray); - Arrays.sort(secondArray); - Arrays.sort(thirdArray); - Arrays.sort(fourthArray); - - assertArrayEquals(firstResult, firstArray); - assertArrayEquals(secondResult, secondArray); - assertArrayEquals(thirdResult, thirdArray); - assertArrayEquals(fourthResult, fourthArray); - } + @Test + public void test_bubbleSort_shouldReturnSortedArray() { + int[] firstArray = + new int[] { + 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, + 85, 30 + }; + int[] firstResult = BubbleSort.sort(Arrays.copyOf(firstArray, firstArray.length)); + + int[] secondArray = + new int[] { + 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10 + }; + int[] secondResult = BubbleSort.sort(Arrays.copyOf(secondArray, secondArray.length)); + + int[] thirdArray = new int[] {}; + int[] thirdResult = BubbleSort.sort(Arrays.copyOf(thirdArray, thirdArray.length)); + + int[] fourthArray = new int[] {1}; + int[] fourthResult = BubbleSort.sort(Arrays.copyOf(fourthArray, fourthArray.length)); + + Arrays.sort(firstArray); + Arrays.sort(secondArray); + Arrays.sort(thirdArray); + Arrays.sort(fourthArray); + + assertArrayEquals(firstResult, firstArray); + assertArrayEquals(secondResult, secondArray); + assertArrayEquals(thirdResult, thirdArray); + assertArrayEquals(fourthResult, fourthArray); + } } diff --git a/src/test/java/algorithms/sorting/countingSort/CountingSortTest.java b/src/test/java/algorithms/sorting/countingSort/CountingSortTest.java index adf1bff9..1f3343da 100644 --- a/src/test/java/algorithms/sorting/countingSort/CountingSortTest.java +++ b/src/test/java/algorithms/sorting/countingSort/CountingSortTest.java @@ -11,34 +11,38 @@ */ public class CountingSortTest { - @Test - public void countingSort_emptyArray_shouldReturnEmptyArray() { - int[] emptyArray = new int[10]; - int[] result = CountingSort.sort(emptyArray); - assertArrayEquals(emptyArray, result); - } - - @Test - public void test_countingSort_shouldReturnSortedArray() { - int[] firstArray = - new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, - 85, 30}; - int[] firstResult = CountingSort.sort(firstArray); - - int[] secondArray = - new int[] {9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10}; - int[] secondResult = CountingSort.sort(secondArray); - - int[] thirdArray = new int[] {}; - int[] thirdResult = CountingSort.sort(thirdArray); - - Arrays.sort(firstArray); // get expected result - Arrays.sort(secondArray); // get expected result - Arrays.sort(thirdArray); // get expected result - - assertArrayEquals(firstResult, firstArray); - assertArrayEquals(secondResult, secondArray); - assertArrayEquals(thirdResult, thirdArray); - } + @Test + public void countingSort_emptyArray_shouldReturnEmptyArray() { + int[] emptyArray = new int[10]; + int[] result = CountingSort.sort(emptyArray); + assertArrayEquals(emptyArray, result); + } + + @Test + public void test_countingSort_shouldReturnSortedArray() { + int[] firstArray = + new int[] { + 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, + 85, 30 + }; + int[] firstResult = CountingSort.sort(firstArray); + + int[] secondArray = + new int[] { + 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10 + }; + int[] secondResult = CountingSort.sort(secondArray); + + int[] thirdArray = new int[] {}; + int[] thirdResult = CountingSort.sort(thirdArray); + + Arrays.sort(firstArray); // get expected result + Arrays.sort(secondArray); // get expected result + Arrays.sort(thirdArray); // get expected result + + assertArrayEquals(firstResult, firstArray); + assertArrayEquals(secondResult, secondArray); + assertArrayEquals(thirdResult, thirdArray); + } } diff --git a/src/test/java/algorithms/sorting/cyclicSort/generalised/CyclicSortTest.java b/src/test/java/algorithms/sorting/cyclicSort/generalised/CyclicSortTest.java index 93df2b01..e94b512c 100644 --- a/src/test/java/algorithms/sorting/cyclicSort/generalised/CyclicSortTest.java +++ b/src/test/java/algorithms/sorting/cyclicSort/generalised/CyclicSortTest.java @@ -10,49 +10,51 @@ * Test cases for generalized {@link CyclicSort}. */ public class CyclicSortTest { - @Test - public void cyclicSort_emptyArray_shouldReturnEmptyArray() { - int[] emptyArray = new int[10]; - int[] expected = new int[10]; - CyclicSort.sort(emptyArray); - assertArrayEquals(emptyArray, expected); - } - - @Test - public void cyclicSort_positiveInputs_shouldReturnSortedArray() { - int[] array = - new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, - 85, 30}; - CyclicSort.sort(array); - - int[] expected = Arrays.copyOf(array, array.length); - Arrays.sort(expected); - - assertArrayEquals(array, expected); - } - - @Test - public void cyclicSort_someNegativeInputs_shouldReturnSortedArray() { - int[] array = - new int[] {2, 3, 4, 1, 2, -5, 6, 7, 10, -15, 20, 13, 15, 1, 2, 15, -12, 20, 21, 120, 11, 5, - 7, -85, 30}; - CyclicSort.sort(array); - - int[] expected = Arrays.copyOf(array, array.length); - Arrays.sort(expected); - - assertArrayEquals(array, expected); - } - - @Test - public void cyclicSort_alreadySorted_shouldReturnSortedArray() { - int[] array = - new int[] {1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15}; - CyclicSort.sort(array); - - int[] expected = Arrays.copyOf(array, array.length); - Arrays.sort(expected); - - assertArrayEquals(array, expected); - } + @Test + public void cyclicSort_emptyArray_shouldReturnEmptyArray() { + int[] emptyArray = new int[10]; + int[] expected = new int[10]; + + CyclicSort.sort(emptyArray); + assertArrayEquals(emptyArray, expected); + } + + @Test + public void cyclicSort_positiveInputs_shouldReturnSortedArray() { + int[] array = + new int[] { + 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, + 85, 30 + }; + int[] expected = Arrays.copyOf(array, array.length); + Arrays.sort(expected); + + CyclicSort.sort(array); + assertArrayEquals(array, expected); + } + + @Test + public void cyclicSort_someNegativeInputs_shouldReturnSortedArray() { + int[] array = + new int[] { + 2, 3, 4, 1, 2, -5, 6, 7, 10, -15, 20, 13, 15, 1, 2, 15, -12, 20, 21, 120, 11, 5, + 7, -85, 30 + }; + int[] expected = Arrays.copyOf(array, array.length); + Arrays.sort(expected); + + CyclicSort.sort(array); + assertArrayEquals(array, expected); + } + + @Test + public void cyclicSort_alreadySorted_shouldReturnSortedArray() { + int[] array = + new int[] {1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15}; + int[] expected = Arrays.copyOf(array, array.length); + Arrays.sort(expected); + + CyclicSort.sort(array); + assertArrayEquals(array, expected); + } } diff --git a/src/test/java/algorithms/sorting/cyclicSort/simple/FindFirstMissingNonNegativeTest.java b/src/test/java/algorithms/sorting/cyclicSort/simple/FindFirstMissingNonNegativeTest.java index 0cdf8c00..e10af613 100644 --- a/src/test/java/algorithms/sorting/cyclicSort/simple/FindFirstMissingNonNegativeTest.java +++ b/src/test/java/algorithms/sorting/cyclicSort/simple/FindFirstMissingNonNegativeTest.java @@ -8,39 +8,39 @@ * Test cases for simple {@link FindFirstMissingNonNegative}. */ public class FindFirstMissingNonNegativeTest { - @Test - public void findMissing_randomArray_shouldReturnFirstMissing() { - int[] arrayMissing3 = {0, 7, 1, 2, 4, 5, 6, 8}; // 3 - assertEquals(FindFirstMissingNonNegative.findMissing(arrayMissing3), 3); - } - - @Test - public void findMissing_duplicatesPresent_shouldReturnFirstMissing() { - int[] arrayMissing5 = {1, 3, 2, 0, 2, 7, 4}; // 5 - assertEquals(FindFirstMissingNonNegative.findMissing(arrayMissing5), 5); - } - - @Test - public void findMissing_firstNthPresent_shouldReturnFirstMissing() { - int[] arrayMissing7 = {0, 1, 2, 3, 4, 5, 6}; // 7 - assertEquals(FindFirstMissingNonNegative.findMissing(arrayMissing7), 7); - } - - @Test - public void findMissing_allNegatives_shouldReturnZero() { - int[] arrayAllNegatives = {-5, -10, -25, -500, -1}; // 0 - assertEquals(FindFirstMissingNonNegative.findMissing(arrayAllNegatives), 0); - } - - @Test - public void findMissing_allPositives_shouldReturnZero() { - int[] arrayAllPositives = {5, 10, 25, 500, 1}; // 0 - assertEquals(FindFirstMissingNonNegative.findMissing(arrayAllPositives), 0); - } - - @Test - public void findMissing_empty_shouldReturnZero() { - int[] empty = {}; - assertEquals(FindFirstMissingNonNegative.findMissing(empty), 0); - } + @Test + public void findMissing_randomArray_shouldReturnFirstMissing() { + int[] arrayMissing3 = {0, 7, 1, 2, 4, 5, 6, 8}; // 3 + assertEquals(FindFirstMissingNonNegative.findMissing(arrayMissing3), 3); + } + + @Test + public void findMissing_duplicatesPresent_shouldReturnFirstMissing() { + int[] arrayMissing5 = {1, 3, 2, 0, 2, 7, 4}; // 5 + assertEquals(FindFirstMissingNonNegative.findMissing(arrayMissing5), 5); + } + + @Test + public void findMissing_firstNthPresent_shouldReturnFirstMissing() { + int[] arrayMissing7 = {0, 1, 2, 3, 4, 5, 6}; // 7 + assertEquals(FindFirstMissingNonNegative.findMissing(arrayMissing7), 7); + } + + @Test + public void findMissing_allNegatives_shouldReturnZero() { + int[] arrayAllNegatives = {-5, -10, -25, -500, -1}; // 0 + assertEquals(FindFirstMissingNonNegative.findMissing(arrayAllNegatives), 0); + } + + @Test + public void findMissing_allPositives_shouldReturnZero() { + int[] arrayAllPositives = {5, 10, 25, 500, 1}; // 0 + assertEquals(FindFirstMissingNonNegative.findMissing(arrayAllPositives), 0); + } + + @Test + public void findMissing_empty_shouldReturnZero() { + int[] empty = {}; + assertEquals(FindFirstMissingNonNegative.findMissing(empty), 0); + } } diff --git a/src/test/java/algorithms/sorting/cyclicSort/simple/SimpleCyclicSortTest.java b/src/test/java/algorithms/sorting/cyclicSort/simple/SimpleCyclicSortTest.java index fbb211b0..9a7ddaec 100644 --- a/src/test/java/algorithms/sorting/cyclicSort/simple/SimpleCyclicSortTest.java +++ b/src/test/java/algorithms/sorting/cyclicSort/simple/SimpleCyclicSortTest.java @@ -8,30 +8,30 @@ * Test cases for Simple {@link CyclicSort}. */ public class SimpleCyclicSortTest { - @Test - public void test_cyclicSort_shouldReturnSortedArray() { - // unsorted array of 16 integers, from 0 to 15 - int[] firstArray = - new int[] {2, 5, 6, 4, 8, 15, 3, 1, 7, 12, 14, 0, 9, 13, 11, 10}; - int[] firstExpected = - new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + @Test + public void test_cyclicSort_shouldReturnSortedArray() { + // unsorted array of 16 integers, from 0 to 15 + int[] firstArray = + new int[] {2, 5, 6, 4, 8, 15, 3, 1, 7, 12, 14, 0, 9, 13, 11, 10}; + int[] firstExpected = + new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - // already sorted, do nothing - int[] secondArray = - new int[] {0, 1, 2, 3, 4, 5, 6}; - int[] secondExpected = - new int[] {0, 1, 2, 3, 4, 5, 6}; + // already sorted, do nothing + int[] secondArray = + new int[] {0, 1, 2, 3, 4, 5, 6}; + int[] secondExpected = + new int[] {0, 1, 2, 3, 4, 5, 6}; - // empty, do nothing - int[] thirdArray = new int[] {}; - int[] thirdExpected = new int[] {}; + // empty, do nothing + int[] thirdArray = new int[] {}; + int[] thirdExpected = new int[] {}; - CyclicSort.sort(firstArray); - CyclicSort.sort(secondArray); - CyclicSort.sort(thirdArray); + CyclicSort.sort(firstArray); + CyclicSort.sort(secondArray); + CyclicSort.sort(thirdArray); - assertArrayEquals(firstArray, firstExpected); - assertArrayEquals(secondArray, secondExpected); - assertArrayEquals(thirdArray, thirdExpected); - } + assertArrayEquals(firstArray, firstExpected); + assertArrayEquals(secondArray, secondExpected); + assertArrayEquals(thirdArray, thirdExpected); + } } diff --git a/src/test/java/algorithms/sorting/insertionSort/InsertionSortTest.java b/src/test/java/algorithms/sorting/insertionSort/InsertionSortTest.java index 5d777843..817b09bc 100644 --- a/src/test/java/algorithms/sorting/insertionSort/InsertionSortTest.java +++ b/src/test/java/algorithms/sorting/insertionSort/InsertionSortTest.java @@ -11,37 +11,41 @@ */ public class InsertionSortTest { - @Test - public void test_insertionSort_shouldReturnSortedArray() { - int[] firstArray = - new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, - 85, 30}; - int[] firstResult = InsertionSort.sort(Arrays.copyOf(firstArray, firstArray.length)); - - int[] secondArray = - new int[] {9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10}; - int[] secondResult = InsertionSort.sort(Arrays.copyOf(secondArray, secondArray.length)); - - int[] thirdArray = new int[] {}; - int[] thirdResult = InsertionSort.sort(Arrays.copyOf(thirdArray, thirdArray.length)); - - int[] fourthArray = new int[] {1}; - int[] fourthResult = InsertionSort.sort(Arrays.copyOf(fourthArray, fourthArray.length)); - - int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; - int[] fifthResult = InsertionSort.sort(Arrays.copyOf(fifthArray, fifthArray.length)); - - Arrays.sort(firstArray); // get expected result - Arrays.sort(secondArray); // get expected result - Arrays.sort(thirdArray); // get expected result - Arrays.sort(fourthArray); // get expected result - Arrays.sort(fifthArray); // get expected result - - assertArrayEquals(firstResult, firstArray); - assertArrayEquals(secondResult, secondArray); - assertArrayEquals(thirdResult, thirdArray); - assertArrayEquals(fourthResult, fourthArray); - assertArrayEquals(fifthResult, fifthArray); - } + @Test + public void test_insertionSort_shouldReturnSortedArray() { + int[] firstArray = + new int[] { + 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, + 85, 30 + }; + int[] firstResult = InsertionSort.sort(Arrays.copyOf(firstArray, firstArray.length)); + + int[] secondArray = + new int[] { + 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10 + }; + int[] secondResult = InsertionSort.sort(Arrays.copyOf(secondArray, secondArray.length)); + + int[] thirdArray = new int[] {}; + int[] thirdResult = InsertionSort.sort(Arrays.copyOf(thirdArray, thirdArray.length)); + + int[] fourthArray = new int[] {1}; + int[] fourthResult = InsertionSort.sort(Arrays.copyOf(fourthArray, fourthArray.length)); + + int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; + int[] fifthResult = InsertionSort.sort(Arrays.copyOf(fifthArray, fifthArray.length)); + + Arrays.sort(firstArray); // get expected result + Arrays.sort(secondArray); // get expected result + Arrays.sort(thirdArray); // get expected result + Arrays.sort(fourthArray); // get expected result + Arrays.sort(fifthArray); // get expected result + + assertArrayEquals(firstResult, firstArray); + assertArrayEquals(secondResult, secondArray); + assertArrayEquals(thirdResult, thirdArray); + assertArrayEquals(fourthResult, fourthArray); + assertArrayEquals(fifthResult, fifthArray); + } } diff --git a/src/test/java/algorithms/sorting/mergeSort/iterative/MergeSortTest.java b/src/test/java/algorithms/sorting/mergeSort/iterative/MergeSortTest.java index 99965c52..7aa924a1 100644 --- a/src/test/java/algorithms/sorting/mergeSort/iterative/MergeSortTest.java +++ b/src/test/java/algorithms/sorting/mergeSort/iterative/MergeSortTest.java @@ -11,43 +11,46 @@ */ public class MergeSortTest { - @Test - public void test_insertionSort_shouldReturnSortedArray() { - int[] firstArray = - new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, - 85, 30}; - int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); - MergeSort.sort(firstResult); - - int[] secondArray - = - new int[] {9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10}; - int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); - MergeSort.sort(secondResult); - - int[] thirdArray = new int[] {}; - int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); - MergeSort.sort(thirdResult); - - int[] fourthArray = new int[] {1}; - int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); - MergeSort.sort(fourthResult); - - int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; - int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); - MergeSort.sort(fifthResult); - - Arrays.sort(firstArray); // get expected result - Arrays.sort(secondArray); // get expected result - Arrays.sort(thirdArray); // get expected result - Arrays.sort(fourthArray); // get expected result - Arrays.sort(fifthArray); // get expected result - - assertArrayEquals(firstResult, firstArray); - assertArrayEquals(secondResult, secondArray); - assertArrayEquals(thirdResult, thirdArray); - assertArrayEquals(fourthResult, fourthArray); - assertArrayEquals(fifthResult, fifthArray); - } + @Test + public void test_insertionSort_shouldReturnSortedArray() { + int[] firstArray = + new int[] { + 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, + 85, 30 + }; + int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); + MergeSort.sort(firstResult); + + int[] secondArray = + new int[] { + 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10 + }; + int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); + MergeSort.sort(secondResult); + + int[] thirdArray = new int[] {}; + int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); + MergeSort.sort(thirdResult); + + int[] fourthArray = new int[] {1}; + int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); + MergeSort.sort(fourthResult); + + int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; + int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); + MergeSort.sort(fifthResult); + + Arrays.sort(firstArray); // get expected result + Arrays.sort(secondArray); // get expected result + Arrays.sort(thirdArray); // get expected result + Arrays.sort(fourthArray); // get expected result + Arrays.sort(fifthArray); // get expected result + + assertArrayEquals(firstResult, firstArray); + assertArrayEquals(secondResult, secondArray); + assertArrayEquals(thirdResult, thirdArray); + assertArrayEquals(fourthResult, fourthArray); + assertArrayEquals(fifthResult, fifthArray); + } } diff --git a/src/test/java/algorithms/sorting/mergeSort/recursive/MergeSortTest.java b/src/test/java/algorithms/sorting/mergeSort/recursive/MergeSortTest.java index 5680de94..c785a2dc 100644 --- a/src/test/java/algorithms/sorting/mergeSort/recursive/MergeSortTest.java +++ b/src/test/java/algorithms/sorting/mergeSort/recursive/MergeSortTest.java @@ -12,43 +12,46 @@ */ public class MergeSortTest { - @Test - public void test_insertionSort_shouldReturnSortedArray() { - int[] firstArray = - new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, - 85, 30}; - int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); - MergeSort.sort(firstResult); - - int[] secondArray - = - new int[] {9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10}; - int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); - MergeSort.sort(secondResult); - - int[] thirdArray = new int[] {}; - int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); - MergeSort.sort(thirdResult); - - int[] fourthArray = new int[] {1}; - int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); - MergeSort.sort(fourthResult); - - int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; - int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); - MergeSort.sort(fifthResult); - - Arrays.sort(firstArray); // get expected result - Arrays.sort(secondArray); // get expected result - Arrays.sort(thirdArray); // get expected result - Arrays.sort(fourthArray); // get expected result - Arrays.sort(fifthArray); // get expected result - - assertArrayEquals(firstResult, firstArray); - assertArrayEquals(secondResult, secondArray); - assertArrayEquals(thirdResult, thirdArray); - assertArrayEquals(fourthResult, fourthArray); - assertArrayEquals(fifthResult, fifthArray); - } + @Test + public void test_insertionSort_shouldReturnSortedArray() { + int[] firstArray = + new int[] { + 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, + 85, 30 + }; + int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); + MergeSort.sort(firstResult); + + int[] secondArray = + new int[] { + 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10 + }; + int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); + MergeSort.sort(secondResult); + + int[] thirdArray = new int[] {}; + int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); + MergeSort.sort(thirdResult); + + int[] fourthArray = new int[] {1}; + int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); + MergeSort.sort(fourthResult); + + int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; + int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); + MergeSort.sort(fifthResult); + + Arrays.sort(firstArray); // get expected result + Arrays.sort(secondArray); // get expected result + Arrays.sort(thirdArray); // get expected result + Arrays.sort(fourthArray); // get expected result + Arrays.sort(fifthArray); // get expected result + + assertArrayEquals(firstResult, firstArray); + assertArrayEquals(secondResult, secondArray); + assertArrayEquals(thirdResult, thirdArray); + assertArrayEquals(fourthResult, fourthArray); + assertArrayEquals(fifthResult, fifthArray); + } } diff --git a/src/test/java/algorithms/sorting/quickSort/hoares/QuickSortTest.java b/src/test/java/algorithms/sorting/quickSort/hoares/QuickSortTest.java index b4135ad6..cbc61d6c 100644 --- a/src/test/java/algorithms/sorting/quickSort/hoares/QuickSortTest.java +++ b/src/test/java/algorithms/sorting/quickSort/hoares/QuickSortTest.java @@ -11,62 +11,65 @@ */ public class QuickSortTest { - @Test - public void test_selectionSort_shouldReturnSortedArray() { - int[] firstArray = - new int[] {22, 1, 6, 40, 32, 10, 18, 4, 50}; - int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); - QuickSort.sort(firstResult); + @Test + public void test_selectionSort_shouldReturnSortedArray() { + int[] firstArray = + new int[] {22, 1, 6, 40, 32, 10, 18, 4, 50}; + int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); + QuickSort.sort(firstResult); - int[] secondArray - = - new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, - 85, 30}; - int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); - QuickSort.sort(secondResult); + int[] secondArray = + new int[] { + 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, + 85, 30 + }; + int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); + QuickSort.sort(secondResult); - int[] thirdArray = - new int[] {9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10}; - int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); - QuickSort.sort(thirdResult); + int[] thirdArray = + new int[] { + 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10 + }; + int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); + QuickSort.sort(thirdResult); - int[] fourthArray = new int[] {}; - int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); - QuickSort.sort(fourthResult); + int[] fourthArray = new int[] {}; + int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); + QuickSort.sort(fourthResult); - int[] fifthArray = new int[] {1}; - int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); - QuickSort.sort(fifthResult); + int[] fifthArray = new int[] {1}; + int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); + QuickSort.sort(fifthResult); - int[] sixthArray = new int[] {5, 1, 1, 2, 0, 0}; - int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length); - QuickSort.sort(sixthResult); + int[] sixthArray = new int[] {5, 1, 1, 2, 0, 0}; + int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length); + QuickSort.sort(sixthResult); - int[] seventhArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - int[] seventhResult = Arrays.copyOf(seventhArray, seventhArray.length); - QuickSort.sort(seventhResult); + int[] seventhArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + int[] seventhResult = Arrays.copyOf(seventhArray, seventhArray.length); + QuickSort.sort(seventhResult); - int[] eighthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - int[] eighthResult = Arrays.copyOf(eighthArray, eighthArray.length); - QuickSort.sort(eighthResult); + int[] eighthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + int[] eighthResult = Arrays.copyOf(eighthArray, eighthArray.length); + QuickSort.sort(eighthResult); - Arrays.sort(firstArray); // get expected result - Arrays.sort(secondArray); // get expected result - Arrays.sort(thirdArray); // get expected result - Arrays.sort(fourthArray); // get expected result - Arrays.sort(fifthArray); // get expected result - Arrays.sort(sixthArray); // get expected result - Arrays.sort(seventhArray); // get expected result - Arrays.sort(eighthArray); // get expected result + Arrays.sort(firstArray); // get expected result + Arrays.sort(secondArray); // get expected result + Arrays.sort(thirdArray); // get expected result + Arrays.sort(fourthArray); // get expected result + Arrays.sort(fifthArray); // get expected result + Arrays.sort(sixthArray); // get expected result + Arrays.sort(seventhArray); // get expected result + Arrays.sort(eighthArray); // get expected result - assertArrayEquals(firstResult, firstArray); - assertArrayEquals(secondResult, secondArray); - assertArrayEquals(thirdResult, thirdArray); - assertArrayEquals(fourthResult, fourthArray); - assertArrayEquals(fifthResult, fifthArray); - assertArrayEquals(sixthResult, sixthArray); - assertArrayEquals(seventhResult, seventhArray); - assertArrayEquals(eighthResult, eighthArray); - } + assertArrayEquals(firstResult, firstArray); + assertArrayEquals(secondResult, secondArray); + assertArrayEquals(thirdResult, thirdArray); + assertArrayEquals(fourthResult, fourthArray); + assertArrayEquals(fifthResult, fifthArray); + assertArrayEquals(sixthResult, sixthArray); + assertArrayEquals(seventhResult, seventhArray); + assertArrayEquals(eighthResult, eighthArray); + } } diff --git a/src/test/java/algorithms/sorting/quickSort/lomuto/QuickSortTest.java b/src/test/java/algorithms/sorting/quickSort/lomuto/QuickSortTest.java index 5b61efa8..c9868601 100644 --- a/src/test/java/algorithms/sorting/quickSort/lomuto/QuickSortTest.java +++ b/src/test/java/algorithms/sorting/quickSort/lomuto/QuickSortTest.java @@ -11,49 +11,52 @@ */ public class QuickSortTest { - @Test - public void test_selectionSort_shouldReturnSortedArray() { - int[] firstArray = - new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, - 85, 30}; - int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); - QuickSort.sort(firstResult); - - int[] secondArray - = - new int[] {9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10}; - int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); - QuickSort.sort(secondResult); - - int[] thirdArray = new int[] {}; - int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); - QuickSort.sort(thirdResult); - - int[] fourthArray = new int[] {1}; - int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); - QuickSort.sort(fourthResult); - - int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; - int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); - QuickSort.sort(fifthResult); - - int[] sixthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length); - QuickSort.sort(sixthResult); - - Arrays.sort(firstArray); // get expected result - Arrays.sort(secondArray); // get expected result - Arrays.sort(thirdArray); // get expected result - Arrays.sort(fourthArray); // get expected result - Arrays.sort(fifthArray); // get expected result - Arrays.sort(sixthArray); // get expected result - - assertArrayEquals(firstResult, firstArray); - assertArrayEquals(secondResult, secondArray); - assertArrayEquals(thirdResult, thirdArray); - assertArrayEquals(fourthResult, fourthArray); - assertArrayEquals(fifthResult, fifthArray); - assertArrayEquals(sixthResult, sixthArray); - } + @Test + public void test_selectionSort_shouldReturnSortedArray() { + int[] firstArray = + new int[] { + 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, + 85, 30 + }; + int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); + QuickSort.sort(firstResult); + + int[] secondArray = + new int[] { + 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10 + }; + int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); + QuickSort.sort(secondResult); + + int[] thirdArray = new int[] {}; + int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); + QuickSort.sort(thirdResult); + + int[] fourthArray = new int[] {1}; + int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); + QuickSort.sort(fourthResult); + + int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; + int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); + QuickSort.sort(fifthResult); + + int[] sixthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length); + QuickSort.sort(sixthResult); + + Arrays.sort(firstArray); // get expected result + Arrays.sort(secondArray); // get expected result + Arrays.sort(thirdArray); // get expected result + Arrays.sort(fourthArray); // get expected result + Arrays.sort(fifthArray); // get expected result + Arrays.sort(sixthArray); // get expected result + + assertArrayEquals(firstResult, firstArray); + assertArrayEquals(secondResult, secondArray); + assertArrayEquals(thirdResult, thirdArray); + assertArrayEquals(fourthResult, fourthArray); + assertArrayEquals(fifthResult, fifthArray); + assertArrayEquals(sixthResult, sixthArray); + } } diff --git a/src/test/java/algorithms/sorting/quickSort/paranoid/QuickSortTest.java b/src/test/java/algorithms/sorting/quickSort/paranoid/QuickSortTest.java index 576c19dd..4708abe2 100644 --- a/src/test/java/algorithms/sorting/quickSort/paranoid/QuickSortTest.java +++ b/src/test/java/algorithms/sorting/quickSort/paranoid/QuickSortTest.java @@ -11,52 +11,55 @@ */ public class QuickSortTest { - @Test - public void test_selectionSort_shouldReturnSortedArray() { - int[] firstArray = - new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, - 85, 30}; - int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); - QuickSort.sort(firstResult); - - int[] secondArray - = - new int[] {9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10}; - int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); - QuickSort.sort(secondResult); - - int[] thirdArray = new int[] {}; - int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); - QuickSort.sort(thirdResult); - - int[] fourthArray = new int[] {1}; - int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); - QuickSort.sort(fourthResult); - - int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; - int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); - QuickSort.sort(fifthResult); - - int[] sixthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length); - // testing for duplicate arrays of length >= 10, stack overflow is expected to happen - try { - QuickSort.sort(sixthResult); - } catch (StackOverflowError e) { - System.out.println("Stack overflow occurred for sixthResult"); - } + @Test + public void test_selectionSort_shouldReturnSortedArray() { + int[] firstArray = + new int[] { + 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, + 85, 30 + }; + int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); + QuickSort.sort(firstResult); + + int[] secondArray = + new int[] { + 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10 + }; + int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); + QuickSort.sort(secondResult); + + int[] thirdArray = new int[] {}; + int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); + QuickSort.sort(thirdResult); + + int[] fourthArray = new int[] {1}; + int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); + QuickSort.sort(fourthResult); - Arrays.sort(firstArray); // get expected result - Arrays.sort(secondArray); // get expected result - Arrays.sort(thirdArray); // get expected result - Arrays.sort(fourthArray); // get expected result - Arrays.sort(fifthArray); // get expected result - - assertArrayEquals(firstResult, firstArray); - assertArrayEquals(secondResult, secondArray); - assertArrayEquals(thirdResult, thirdArray); - assertArrayEquals(fourthResult, fourthArray); - assertArrayEquals(fifthResult, fifthArray); - } + int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; + int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); + QuickSort.sort(fifthResult); + + int[] sixthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length); + // testing for duplicate arrays of length >= 10, stack overflow is expected to happen + try { + QuickSort.sort(sixthResult); + } catch (StackOverflowError e) { + System.out.println("Stack overflow occurred for sixthResult"); + } + + Arrays.sort(firstArray); // get expected result + Arrays.sort(secondArray); // get expected result + Arrays.sort(thirdArray); // get expected result + Arrays.sort(fourthArray); // get expected result + Arrays.sort(fifthArray); // get expected result + + assertArrayEquals(firstResult, firstArray); + assertArrayEquals(secondResult, secondArray); + assertArrayEquals(thirdResult, thirdArray); + assertArrayEquals(fourthResult, fourthArray); + assertArrayEquals(fifthResult, fifthArray); + } } diff --git a/src/test/java/algorithms/sorting/quickSort/threeWayPartitioning/QuickSortTest.java b/src/test/java/algorithms/sorting/quickSort/threeWayPartitioning/QuickSortTest.java index 2bfd9b19..40b911b5 100644 --- a/src/test/java/algorithms/sorting/quickSort/threeWayPartitioning/QuickSortTest.java +++ b/src/test/java/algorithms/sorting/quickSort/threeWayPartitioning/QuickSortTest.java @@ -11,48 +11,52 @@ */ public class QuickSortTest { - @Test - public void test_selectionSort_shouldReturnSortedArray() { - int[] firstArray = - new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, - 85, 30}; - int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); - QuickSort.sort(firstResult); - - int[] secondArray = - new int[] {9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10}; - int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); - QuickSort.sort(secondResult); - - int[] thirdArray = new int[] {}; - int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); - QuickSort.sort(thirdResult); - - int[] fourthArray = new int[] {1}; - int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); - QuickSort.sort(fourthResult); - - int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; - int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); - QuickSort.sort(fifthResult); - - int[] sixthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length); - QuickSort.sort(sixthResult); - - Arrays.sort(firstArray); // get expected result - Arrays.sort(secondArray); // get expected result - Arrays.sort(thirdArray); // get expected result - Arrays.sort(fourthArray); // get expected result - Arrays.sort(fifthArray); // get expected result - Arrays.sort(sixthArray); // get expected result - - assertArrayEquals(firstResult, firstArray); - assertArrayEquals(secondResult, secondArray); - assertArrayEquals(thirdResult, thirdArray); - assertArrayEquals(fourthResult, fourthArray); - assertArrayEquals(fifthResult, fifthArray); - assertArrayEquals(sixthResult, sixthArray); - } + @Test + public void test_selectionSort_shouldReturnSortedArray() { + int[] firstArray = + new int[] { + 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, + 85, 30 + }; + int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); + QuickSort.sort(firstResult); + + int[] secondArray = + new int[] { + 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10 + }; + int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); + QuickSort.sort(secondResult); + + int[] thirdArray = new int[] {}; + int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); + QuickSort.sort(thirdResult); + + int[] fourthArray = new int[] {1}; + int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); + QuickSort.sort(fourthResult); + + int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; + int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length); + QuickSort.sort(fifthResult); + + int[] sixthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; + int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length); + QuickSort.sort(sixthResult); + + Arrays.sort(firstArray); // get expected result + Arrays.sort(secondArray); // get expected result + Arrays.sort(thirdArray); // get expected result + Arrays.sort(fourthArray); // get expected result + Arrays.sort(fifthArray); // get expected result + Arrays.sort(sixthArray); // get expected result + + assertArrayEquals(firstResult, firstArray); + assertArrayEquals(secondResult, secondArray); + assertArrayEquals(thirdResult, thirdArray); + assertArrayEquals(fourthResult, fourthArray); + assertArrayEquals(fifthResult, fifthArray); + assertArrayEquals(sixthResult, sixthArray); + } } diff --git a/src/test/java/algorithms/sorting/radixSort/RadixSortTest.java b/src/test/java/algorithms/sorting/radixSort/RadixSortTest.java index c64466ad..7bef49be 100644 --- a/src/test/java/algorithms/sorting/radixSort/RadixSortTest.java +++ b/src/test/java/algorithms/sorting/radixSort/RadixSortTest.java @@ -3,37 +3,42 @@ import static org.junit.Assert.assertArrayEquals; import java.util.Arrays; + import org.junit.Test; public class RadixSortTest { - @Test - public void test_radixSort_shouldReturnSortedArray() { - int[] firstArray = new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, - 15, 12, 20, 21, 120, 11, 5, 7, 85, 30}; - int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); - RadixSort.radixSort(firstResult); - - int[] secondArray = new int[] {9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, - 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); - RadixSort.radixSort(secondResult); - - int[] thirdArray = new int[] {}; - int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); - RadixSort.radixSort(thirdResult); - - int[] fourthArray = new int[] {1}; - int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); - RadixSort.radixSort(fourthResult); - - Arrays.sort(firstArray); - Arrays.sort(secondArray); - Arrays.sort(thirdArray); - Arrays.sort(fourthArray); - - assertArrayEquals(firstResult, firstArray); - assertArrayEquals(secondResult, secondArray); - assertArrayEquals(thirdResult, thirdArray); - assertArrayEquals(fourthResult, fourthArray); - } + @Test + public void test_radixSort_shouldReturnSortedArray() { + int[] firstArray = new int[] { + 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, + 15, 12, 20, 21, 120, 11, 5, 7, 85, 30 + }; + int[] firstResult = Arrays.copyOf(firstArray, firstArray.length); + RadixSort.radixSort(firstResult); + + int[] secondArray = new int[] { + 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, + 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 + }; + int[] secondResult = Arrays.copyOf(secondArray, secondArray.length); + RadixSort.radixSort(secondResult); + + int[] thirdArray = new int[] {}; + int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length); + RadixSort.radixSort(thirdResult); + + int[] fourthArray = new int[] {1}; + int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length); + RadixSort.radixSort(fourthResult); + + Arrays.sort(firstArray); + Arrays.sort(secondArray); + Arrays.sort(thirdArray); + Arrays.sort(fourthArray); + + assertArrayEquals(firstResult, firstArray); + assertArrayEquals(secondResult, secondArray); + assertArrayEquals(thirdResult, thirdArray); + assertArrayEquals(fourthResult, fourthArray); + } } diff --git a/src/test/java/algorithms/sorting/selectionSort/SelectionSortTest.java b/src/test/java/algorithms/sorting/selectionSort/SelectionSortTest.java index 378754db..8eca493d 100644 --- a/src/test/java/algorithms/sorting/selectionSort/SelectionSortTest.java +++ b/src/test/java/algorithms/sorting/selectionSort/SelectionSortTest.java @@ -11,37 +11,41 @@ */ public class SelectionSortTest { - @Test - public void test_selectionSort_shouldReturnSortedArray() { - int[] firstArray = - new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, - 85, 30}; - int[] firstResult = SelectionSort.sort(Arrays.copyOf(firstArray, firstArray.length)); - - int[] secondArray = - new int[] {9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10}; - int[] secondResult = SelectionSort.sort(Arrays.copyOf(secondArray, secondArray.length)); - - int[] thirdArray = new int[] {}; - int[] thirdResult = SelectionSort.sort(Arrays.copyOf(thirdArray, thirdArray.length)); - - int[] fourthArray = new int[] {1}; - int[] fourthResult = SelectionSort.sort(Arrays.copyOf(fourthArray, fourthArray.length)); - - int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; - int[] fifthResult = SelectionSort.sort(Arrays.copyOf(fifthArray, fifthArray.length)); - - Arrays.sort(firstArray); // get expected result - Arrays.sort(secondArray); // get expected result - Arrays.sort(thirdArray); // get expected result - Arrays.sort(fourthArray); // get expected result - Arrays.sort(fifthArray); // get expected result - - assertArrayEquals(firstResult, firstArray); - assertArrayEquals(secondResult, secondArray); - assertArrayEquals(thirdResult, thirdArray); - assertArrayEquals(fourthResult, fourthArray); - assertArrayEquals(fifthResult, fifthArray); - } + @Test + public void test_selectionSort_shouldReturnSortedArray() { + int[] firstArray = + new int[] { + 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7, + 85, 30 + }; + int[] firstResult = SelectionSort.sort(Arrays.copyOf(firstArray, firstArray.length)); + + int[] secondArray = + new int[] { + 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10 + }; + int[] secondResult = SelectionSort.sort(Arrays.copyOf(secondArray, secondArray.length)); + + int[] thirdArray = new int[] {}; + int[] thirdResult = SelectionSort.sort(Arrays.copyOf(thirdArray, thirdArray.length)); + + int[] fourthArray = new int[] {1}; + int[] fourthResult = SelectionSort.sort(Arrays.copyOf(fourthArray, fourthArray.length)); + + int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0}; + int[] fifthResult = SelectionSort.sort(Arrays.copyOf(fifthArray, fifthArray.length)); + + Arrays.sort(firstArray); // get expected result + Arrays.sort(secondArray); // get expected result + Arrays.sort(thirdArray); // get expected result + Arrays.sort(fourthArray); // get expected result + Arrays.sort(fifthArray); // get expected result + + assertArrayEquals(firstResult, firstArray); + assertArrayEquals(secondResult, secondArray); + assertArrayEquals(thirdResult, thirdArray); + assertArrayEquals(fourthResult, fourthArray); + assertArrayEquals(fifthResult, fifthArray); + } } diff --git a/src/test/java/dataStructures/binarySearchTree/BinarySearchTreeTest.java b/src/test/java/dataStructures/binarySearchTree/BinarySearchTreeTest.java index 7ea37cce..fcad20bb 100644 --- a/src/test/java/dataStructures/binarySearchTree/BinarySearchTreeTest.java +++ b/src/test/java/dataStructures/binarySearchTree/BinarySearchTreeTest.java @@ -1,10 +1,10 @@ package dataStructures.binarySearchTree; -import org.junit.Test; - import java.util.Arrays; import java.util.List; +import org.junit.Test; + public class BinarySearchTreeTest { @Test public void insert_shouldCorrectlyInsertNodes() { @@ -19,7 +19,10 @@ public void insert_shouldCorrectlyInsertNodes() { List result = bst.getInorder(); // Expected in-order traversal result - List expected = Arrays.asList("Key: 2, Value: Two", "Key: 3, Value: Three", "Key: 4, Value: Four", "Key: 5, Value: Five", "Key: 7, Value: Seven"); + List expected = + Arrays.asList("Key: 2, Value: Two", "Key: 3, Value: Three", "Key: 4, Value: Four", "Key: 5, Value: Five", + "Key: 7, Value: Seven" + ); assert result.equals(expected); } @@ -76,7 +79,8 @@ public void delete_shouldCorrectlyHandleNodeWithTwoChildren() { List result = bst.getInorder(); // Expected in-order traversal result after deletion - List expected = Arrays.asList("Key: 2, Value: Two", "Key: 4, Value: Four", "Key: 5, Value: Five", "Key: 7, Value: Seven"); + List expected = + Arrays.asList("Key: 2, Value: Two", "Key: 4, Value: Four", "Key: 5, Value: Five", "Key: 7, Value: Seven"); assert result.equals(expected); } diff --git a/src/test/java/dataStructures/hashSet/chaining/HashSetTest.java b/src/test/java/dataStructures/hashSet/chaining/HashSetTest.java index 8fe2d16b..63b2ad3b 100644 --- a/src/test/java/dataStructures/hashSet/chaining/HashSetTest.java +++ b/src/test/java/dataStructures/hashSet/chaining/HashSetTest.java @@ -14,73 +14,73 @@ * Test cases for {@link HashSet} implemented using Chaining to resolve collisions. */ public class HashSetTest { - @Test - public void testAdd_noDuplicates_shouldReturnTrue() { - HashSet hashSet = new HashSet<>(); - assertTrue(hashSet.add("Hello")); - assertTrue(hashSet.add("World")); - } + @Test + public void testAdd_noDuplicates_shouldReturnTrue() { + HashSet hashSet = new HashSet<>(); + assertTrue(hashSet.add("Hello")); + assertTrue(hashSet.add("World")); + } - @Test - public void testAdd_withDuplicates_shouldReturnFalse() { - HashSet hashSet = new HashSet<>(); - assertTrue(hashSet.add("Hello")); - assertTrue(hashSet.add("World")); - assertFalse(hashSet.add("Hello")); // Adding duplicate element should return false - } + @Test + public void testAdd_withDuplicates_shouldReturnFalse() { + HashSet hashSet = new HashSet<>(); + assertTrue(hashSet.add("Hello")); + assertTrue(hashSet.add("World")); + assertFalse(hashSet.add("Hello")); // Adding duplicate element should return false + } - @Test - public void testContains() { - HashSet hashSet = new HashSet<>(); - hashSet.add("Hello"); - hashSet.add("World"); - assertTrue(hashSet.contains("Hello")); - assertTrue(hashSet.contains("World")); - assertFalse(hashSet.contains("Universe")); // Element not in set - } + @Test + public void testContains() { + HashSet hashSet = new HashSet<>(); + hashSet.add("Hello"); + hashSet.add("World"); + assertTrue(hashSet.contains("Hello")); + assertTrue(hashSet.contains("World")); + assertFalse(hashSet.contains("Universe")); // Element not in set + } - @Test - public void testRemove() { - HashSet hashSet = new HashSet<>(); - hashSet.add("Hello"); - hashSet.add("World"); - assertTrue(hashSet.remove("Hello")); - assertFalse(hashSet.contains("Hello")); // Element should be removed - assertFalse(hashSet.remove("Universe")); // Removing non-existent element should return false - } + @Test + public void testRemove() { + HashSet hashSet = new HashSet<>(); + hashSet.add("Hello"); + hashSet.add("World"); + assertTrue(hashSet.remove("Hello")); + assertFalse(hashSet.contains("Hello")); // Element should be removed + assertFalse(hashSet.remove("Universe")); // Removing non-existent element should return false + } - @Test - public void testSize() { - HashSet hashSet = new HashSet<>(); - assertEquals(0, hashSet.size()); // Initial size should be 0 - hashSet.add("Hello"); - assertEquals(1, hashSet.size()); // Size after adding one element - hashSet.add("World"); - assertEquals(2, hashSet.size()); // Size after adding two elements - hashSet.remove("Hello"); - assertEquals(1, hashSet.size()); // Size after removing one element - } + @Test + public void testSize() { + HashSet hashSet = new HashSet<>(); + assertEquals(0, hashSet.size()); // Initial size should be 0 + hashSet.add("Hello"); + assertEquals(1, hashSet.size()); // Size after adding one element + hashSet.add("World"); + assertEquals(2, hashSet.size()); // Size after adding two elements + hashSet.remove("Hello"); + assertEquals(1, hashSet.size()); // Size after removing one element + } - @Test - public void testIsEmpty() { - HashSet hashSet = new HashSet<>(); - assertTrue(hashSet.isEmpty()); // Initial set should be empty - hashSet.add("Hello"); - assertFalse(hashSet.isEmpty()); // Set should not be empty after adding an element - hashSet.remove("Hello"); - assertTrue(hashSet.isEmpty()); // Set should be empty after removing the only element - } + @Test + public void testIsEmpty() { + HashSet hashSet = new HashSet<>(); + assertTrue(hashSet.isEmpty()); // Initial set should be empty + hashSet.add("Hello"); + assertFalse(hashSet.isEmpty()); // Set should not be empty after adding an element + hashSet.remove("Hello"); + assertTrue(hashSet.isEmpty()); // Set should be empty after removing the only element + } - @Test - public void test_hashSet_shouldNotHaveDuplicates() { - HashSet hashSet = new HashSet<>(); - hashSet.add("Hello"); - hashSet.add("World"); - hashSet.add("Hello"); - hashSet.add("World"); - List firstList = Stream.of("Hello", "World").sorted().collect(Collectors.toList()); - List firstResult = hashSet.toList().stream().sorted().collect(Collectors.toList()); + @Test + public void test_hashSet_shouldNotHaveDuplicates() { + HashSet hashSet = new HashSet<>(); + hashSet.add("Hello"); + hashSet.add("World"); + hashSet.add("Hello"); + hashSet.add("World"); + List firstList = Stream.of("Hello", "World").sorted().collect(Collectors.toList()); + List firstResult = hashSet.toList().stream().sorted().collect(Collectors.toList()); - assertEquals(firstList, firstResult); - } + assertEquals(firstList, firstResult); + } } diff --git a/src/test/java/dataStructures/hashSet/openAddressing/HashSetTest.java b/src/test/java/dataStructures/hashSet/openAddressing/HashSetTest.java index c9661326..6af466f4 100644 --- a/src/test/java/dataStructures/hashSet/openAddressing/HashSetTest.java +++ b/src/test/java/dataStructures/hashSet/openAddressing/HashSetTest.java @@ -14,127 +14,127 @@ * Test cases for {@link HashSet} implemented using Open Addressing to resolve collisions. */ public class HashSetTest { - @Test - public void testAdd_noDuplicates_shouldReturnTrue() { - HashSet hashSet = new HashSet<>(); - assertTrue(hashSet.add("Hello")); - assertTrue(hashSet.add("World")); - } - - @Test - public void testAdd_withDuplicates_shouldReturnFalse() { - HashSet hashSet = new HashSet<>(); - assertTrue(hashSet.add("Hello")); - assertTrue(hashSet.add("World")); - assertFalse(hashSet.add("Hello")); // Adding duplicate element should return false - } - - @Test - public void testContains() { - HashSet hashSet = new HashSet<>(); - hashSet.add("Hello"); - hashSet.add("World"); - assertTrue(hashSet.contains("Hello")); - assertTrue(hashSet.contains("World")); - assertFalse(hashSet.contains("Universe")); // Element not in set - } - - @Test - public void testRemove() { - HashSet hashSet = new HashSet<>(); - hashSet.add("Hello"); - hashSet.add("World"); - assertTrue(hashSet.remove("Hello")); - assertFalse(hashSet.contains("Hello")); // Element should be removed - assertFalse(hashSet.remove("Universe")); // Removing non-existent element should return false - } - - @Test - public void testSize() { - HashSet hashSet = new HashSet<>(); - assertEquals(0, hashSet.size()); // Initial size should be 0 - hashSet.add("Hello"); - assertEquals(1, hashSet.size()); // Size after adding one element - hashSet.add("World"); - assertEquals(2, hashSet.size()); // Size after adding two elements - hashSet.remove("Hello"); - assertEquals(1, hashSet.size()); // Size after removing one element - } - - @Test - public void testIsEmpty() { - HashSet hashSet = new HashSet<>(); - assertTrue(hashSet.isEmpty()); // Initial set should be empty - hashSet.add("Hello"); - assertFalse(hashSet.isEmpty()); // Set should not be empty after adding an element - hashSet.remove("Hello"); - assertTrue(hashSet.isEmpty()); // Set should be empty after removing the only element - } - - @Test - public void testContains_afterRemove() { - HashSet hashSet = new HashSet<>(); - Stream.iterate(0, i -> i + 1) // Populates the hashSet. - .limit(16) - .forEach(hashSet::add); - hashSet.remove(4); - assertTrue(hashSet.add(25)); // add should insert 25 at where 4 was at previously. - hashSet.remove(10); // Introduce a tombstone in the probe sequence for 25. - - assertTrue(hashSet.contains(25)); // contains should still find 25. - } - - @Test - public void testResize() { - // Create a HashSet with an initial capacity of 16 and load factor of 0.75 for testing. - HashSet set = new HashSet<>(); - for (int i = 1; i <= 12; i++) { - set.add(i); + @Test + public void testAdd_noDuplicates_shouldReturnTrue() { + HashSet hashSet = new HashSet<>(); + assertTrue(hashSet.add("Hello")); + assertTrue(hashSet.add("World")); } - // Verify that the HashSet has the initial capacity of 16. - assertEquals(16, set.capacity()); + @Test + public void testAdd_withDuplicates_shouldReturnFalse() { + HashSet hashSet = new HashSet<>(); + assertTrue(hashSet.add("Hello")); + assertTrue(hashSet.add("World")); + assertFalse(hashSet.add("Hello")); // Adding duplicate element should return false + } + + @Test + public void testContains() { + HashSet hashSet = new HashSet<>(); + hashSet.add("Hello"); + hashSet.add("World"); + assertTrue(hashSet.contains("Hello")); + assertTrue(hashSet.contains("World")); + assertFalse(hashSet.contains("Universe")); // Element not in set + } - // Adding one more element should trigger a resize operation to double the capacity to 32. - set.add(13); + @Test + public void testRemove() { + HashSet hashSet = new HashSet<>(); + hashSet.add("Hello"); + hashSet.add("World"); + assertTrue(hashSet.remove("Hello")); + assertFalse(hashSet.contains("Hello")); // Element should be removed + assertFalse(hashSet.remove("Universe")); // Removing non-existent element should return false + } - // Verify that the HashSet has resized and doubled its capacity to 32. - assertEquals(32, set.capacity()); + @Test + public void testSize() { + HashSet hashSet = new HashSet<>(); + assertEquals(0, hashSet.size()); // Initial size should be 0 + hashSet.add("Hello"); + assertEquals(1, hashSet.size()); // Size after adding one element + hashSet.add("World"); + assertEquals(2, hashSet.size()); // Size after adding two elements + hashSet.remove("Hello"); + assertEquals(1, hashSet.size()); // Size after removing one element + } + + @Test + public void testIsEmpty() { + HashSet hashSet = new HashSet<>(); + assertTrue(hashSet.isEmpty()); // Initial set should be empty + hashSet.add("Hello"); + assertFalse(hashSet.isEmpty()); // Set should not be empty after adding an element + hashSet.remove("Hello"); + assertTrue(hashSet.isEmpty()); // Set should be empty after removing the only element + } - // Removing elements until it triggers a resize operation to shrink the capacity. - // Currently size is 13. Therefore, remove 5 elements. - for (int i = 1; i <= 5; i++) { - set.remove(i); + @Test + public void testContains_afterRemove() { + HashSet hashSet = new HashSet<>(); + Stream.iterate(0, i -> i + 1) // Populates the hashSet. + .limit(16) + .forEach(hashSet::add); + hashSet.remove(4); + assertTrue(hashSet.add(25)); // add should insert 25 at where 4 was at previously. + hashSet.remove(10); // Introduce a tombstone in the probe sequence for 25. + + assertTrue(hashSet.contains(25)); // contains should still find 25. } - // Verify that the HashSet has not resized. - assertEquals(32, set.capacity()); + @Test + public void testResize() { + // Create a HashSet with an initial capacity of 16 and load factor of 0.75 for testing. + HashSet set = new HashSet<>(); + for (int i = 1; i <= 12; i++) { + set.add(i); + } - // Removing one more element should trigger a resize operation to halve the capacity back to 16. - set.remove(10); + // Verify that the HashSet has the initial capacity of 16. + assertEquals(16, set.capacity()); - // Verify that the HashSet has resized and halved its capacity back to 16. - assertEquals(16, set.capacity()); - } + // Adding one more element should trigger a resize operation to double the capacity to 32. + set.add(13); - @Test - public void testAdd_afterRemove() { - HashSet hashSet = new HashSet<>(); - // these elements all map to the same initial bucket, resulting in collisions. - hashSet.add(1); - hashSet.add(17); - hashSet.add(33); - // the hashSet will look like {1, 17, 33, ...} after the series of adds + // Verify that the HashSet has resized and doubled its capacity to 32. + assertEquals(32, set.capacity()); - hashSet.remove(17); - // hashSet now looks like {1, X, 33, ...} where X denotes a tombstone. + // Removing elements until it triggers a resize operation to shrink the capacity. + // Currently size is 13. Therefore, remove 5 elements. + for (int i = 1; i <= 5; i++) { + set.remove(i); + } - boolean isAdded = hashSet.add(33); // this should not be added into the hashSet. - assertFalse(isAdded); + // Verify that the HashSet has not resized. + assertEquals(32, set.capacity()); - List expectedList = List.of(1, 33); - List actualList = hashSet.toList(); + // Removing one more element should trigger a resize operation to halve the capacity back to 16. + set.remove(10); - assertEquals(expectedList, actualList); - } + // Verify that the HashSet has resized and halved its capacity back to 16. + assertEquals(16, set.capacity()); + } + + @Test + public void testAdd_afterRemove() { + HashSet hashSet = new HashSet<>(); + // these elements all map to the same initial bucket, resulting in collisions. + hashSet.add(1); + hashSet.add(17); + hashSet.add(33); + // the hashSet will look like {1, 17, 33, ...} after the series of adds + + hashSet.remove(17); + // hashSet now looks like {1, X, 33, ...} where X denotes a tombstone. + + boolean isAdded = hashSet.add(33); // this should not be added into the hashSet. + assertFalse(isAdded); + + List expectedList = List.of(1, 33); + List actualList = hashSet.toList(); + + assertEquals(expectedList, actualList); + } } diff --git a/src/test/java/dataStructures/heap/MaxHeapTest.java b/src/test/java/dataStructures/heap/MaxHeapTest.java index adb2f551..4c28bf2a 100644 --- a/src/test/java/dataStructures/heap/MaxHeapTest.java +++ b/src/test/java/dataStructures/heap/MaxHeapTest.java @@ -8,7 +8,7 @@ /** * Test cases for {@link MaxHeap}. * We will illustrate with the following elements: - * + *

* 13 13 * / \ INSERT 10 / \ REMOVE 10 * 9 11 -----------> 10 11 -----------> ORIGINAL @@ -16,7 +16,7 @@ * 4 7 6 3 4 9 6 3 * / \ / \ / * 2 1 2 1 7 - * + *

* 13 11 * / \ DEC 13 to 5 / \ INC 5 to 13 * 9 11 -----------> 9 6 ------------> ORIGINAL @@ -26,94 +26,95 @@ * 2 1 2 1 */ public class MaxHeapTest { - @Test - public void test_offer_shouldConstructArray() { - MaxHeap heap = new MaxHeap<>(); - heap.offer(13); - heap.offer(9); - heap.offer(11); - heap.offer(4); - heap.offer(7); - heap.offer(6); - heap.offer(3); - heap.offer(2); - heap.offer(1); - - String expected = "[13, 9, 11, 4, 7, 6, 3, 2, 1]"; - Assert.assertEquals(expected, heap.toString()); - - // insert 10 - heap.offer(10); - expected = "[13, 10, 11, 4, 9, 6, 3, 2, 1, 7]"; - Assert.assertEquals(expected, heap.toString()); - } - - @Test - public void test_bothHeapifyMethods_shouldConstructArray() { - MaxHeap heap = new MaxHeap<>(); - // 1st heapify, takes in a list of values - heap.heapify(Arrays.asList(1, 9, 11, 4, 7, 6, 3, 2, 13)); - String firstHeap = heap.toString(); - - // 2nd heapify, take sin sequence of values - heap.heapify(2, 9, 11, 4, 7, 6, 3, 13, 1); - String secondHeap = heap.toString(); - - Assert.assertEquals(firstHeap, secondHeap); // should be the same - String expected = "[13, 9, 11, 4, 7, 6, 3, 2, 1]"; - Assert.assertEquals(expected, firstHeap); - } - - @Test - public void test_size_shouldReturnSizeOfHeap() { - MaxHeap heap = new MaxHeap<>(); - heap.heapify(2, 9, 11, 4, 7, 6, 3, 13, 1); - - Assert.assertEquals(9, heap.size()); - } - - @Test - public void test_peek_shouldReturnHighestKey() { - MaxHeap heap = new MaxHeap<>(); - heap.heapify(2, 9, 11, 4, 7, 6, 3, 13, 1); - - Assert.assertEquals((Integer) 13, - (Integer) heap.peek()); // no method definition for comparing primitive int - } - - @Test - public void test_poll_shouldRemoveAndReturnHighestKey() { - MaxHeap heap = new MaxHeap<>(); - heap.heapify(2, 9, 11, 4, 7, 6, 3, 13, 1); - Integer removed = (Integer) heap.poll(); - - Assert.assertEquals((Integer) 13, removed); - Assert.assertEquals("[11, 9, 6, 4, 7, 1, 3, 2]", heap.toString()); - } - - @Test - public void test_remove_shouldRemoveSpecifiedKey() { - // As illustrated in the example above, we will insert and remove 10 - MaxHeap heap = new MaxHeap<>(); - heap.heapify(2, 9, 11, 4, 7, 6, 3, 13, 1); - heap.offer(10); - Assert.assertEquals("[13, 10, 11, 4, 9, 6, 3, 2, 1, 7]", heap.toString()); // check added - - heap.remove(10); - // Note that element 10 was specifically chosen to simulate the example shown (bubble up/down - // operations) - Assert.assertEquals("[13, 9, 11, 4, 7, 6, 3, 2, 1]", heap.toString()); - } - - @Test - public void test_decreaseKey_increaseKey_shouldUpdateHeapToMaintainProperty() { - // As illustrated in the example above, we will insert and remove 10 - MaxHeap heap = new MaxHeap<>(); - heap.heapify(2, 9, 11, 4, 7, 6, 3, 13, 1); - heap.decreaseKey(13, 5); - Assert.assertEquals("[11, 9, 6, 4, 7, 5, 3, 2, 1]", heap.toString()); // check updated - - heap.increaseKey(5, 13); - Assert.assertEquals("[13, 9, 11, 4, 7, 6, 3, 2, 1]", heap.toString()); - } + @Test + public void test_offer_shouldConstructArray() { + MaxHeap heap = new MaxHeap<>(); + heap.offer(13); + heap.offer(9); + heap.offer(11); + heap.offer(4); + heap.offer(7); + heap.offer(6); + heap.offer(3); + heap.offer(2); + heap.offer(1); + + String expected = "[13, 9, 11, 4, 7, 6, 3, 2, 1]"; + Assert.assertEquals(expected, heap.toString()); + + // insert 10 + heap.offer(10); + expected = "[13, 10, 11, 4, 9, 6, 3, 2, 1, 7]"; + Assert.assertEquals(expected, heap.toString()); + } + + @Test + public void test_bothHeapifyMethods_shouldConstructArray() { + MaxHeap heap = new MaxHeap<>(); + // 1st heapify, takes in a list of values + heap.heapify(Arrays.asList(1, 9, 11, 4, 7, 6, 3, 2, 13)); + String firstHeap = heap.toString(); + + // 2nd heapify, take sin sequence of values + heap.heapify(2, 9, 11, 4, 7, 6, 3, 13, 1); + String secondHeap = heap.toString(); + + Assert.assertEquals(firstHeap, secondHeap); // should be the same + String expected = "[13, 9, 11, 4, 7, 6, 3, 2, 1]"; + Assert.assertEquals(expected, firstHeap); + } + + @Test + public void test_size_shouldReturnSizeOfHeap() { + MaxHeap heap = new MaxHeap<>(); + heap.heapify(2, 9, 11, 4, 7, 6, 3, 13, 1); + + Assert.assertEquals(9, heap.size()); + } + + @Test + public void test_peek_shouldReturnHighestKey() { + MaxHeap heap = new MaxHeap<>(); + heap.heapify(2, 9, 11, 4, 7, 6, 3, 13, 1); + + Assert.assertEquals((Integer) 13, + heap.peek() + ); // no method definition for comparing primitive int + } + + @Test + public void test_poll_shouldRemoveAndReturnHighestKey() { + MaxHeap heap = new MaxHeap<>(); + heap.heapify(2, 9, 11, 4, 7, 6, 3, 13, 1); + Integer removed = heap.poll(); + + Assert.assertEquals((Integer) 13, removed); + Assert.assertEquals("[11, 9, 6, 4, 7, 1, 3, 2]", heap.toString()); + } + + @Test + public void test_remove_shouldRemoveSpecifiedKey() { + // As illustrated in the example above, we will insert and remove 10 + MaxHeap heap = new MaxHeap<>(); + heap.heapify(2, 9, 11, 4, 7, 6, 3, 13, 1); + heap.offer(10); + Assert.assertEquals("[13, 10, 11, 4, 9, 6, 3, 2, 1, 7]", heap.toString()); // check added + + heap.remove(10); + // Note that element 10 was specifically chosen to simulate the example shown (bubble up/down + // operations) + Assert.assertEquals("[13, 9, 11, 4, 7, 6, 3, 2, 1]", heap.toString()); + } + + @Test + public void test_decreaseKeyThenIncreaseKey_shouldUpdateHeapToMaintainProperty() { + // As illustrated in the example above, we will insert and remove 10 + MaxHeap heap = new MaxHeap<>(); + heap.heapify(2, 9, 11, 4, 7, 6, 3, 13, 1); + heap.decreaseKey(13, 5); + Assert.assertEquals("[11, 9, 6, 4, 7, 5, 3, 2, 1]", heap.toString()); // check updated + + heap.increaseKey(5, 13); + Assert.assertEquals("[13, 9, 11, 4, 7, 6, 3, 2, 1]", heap.toString()); + } } diff --git a/src/test/java/dataStructures/linkedList/LinkedListTest.java b/src/test/java/dataStructures/linkedList/LinkedListTest.java index e17e5816..98f7cfa5 100644 --- a/src/test/java/dataStructures/linkedList/LinkedListTest.java +++ b/src/test/java/dataStructures/linkedList/LinkedListTest.java @@ -9,101 +9,101 @@ * Test cases for {@link LinkedList}. */ public class LinkedListTest { - @Test - public void testInsert() { - LinkedList ll = new LinkedList<>(); - Assert.assertEquals(null, ll.toString()); - - ll.insertFront(2); - ll.insertEnd(3); - ll.insertEnd(5); - ll.insertFront(1); - Assert.assertEquals("1 2 3 5 ", ll.toString()); - - ll.insert(0, 0); - ll.insert(4, 4); - ll.insert(7, 6); - ll.insert(6, 6); - Assert.assertEquals("0 1 2 3 4 5 6 7 ", ll.toString()); - } - - @Test - public void testSearchAndGet() { - LinkedList ll = new LinkedList<>(); - ll.insertFront(2); - ll.insertEnd(3); - ll.insertEnd(5); - ll.insertFront(1); - ll.insert(0, 0); - ll.insert(4, 4); - ll.insert(7, 6); - ll.insert(6, 6); - - Node test1 = ll.get(4); - Assert.assertEquals("4", test1.toString()); - - Node test2 = ll.get(3); - Assert.assertEquals("3", test2.toString()); - - Integer test3 = ll.search(4); - Assert.assertEquals("4", test3.toString()); - - Integer test4 = ll.search(3); - Assert.assertEquals("3", test4.toString()); - - Assert.assertEquals("0 1 2 3 4 5 6 7 ", ll.toString()); - } - - @Test - public void testRemove() { - - LinkedList ll = new LinkedList<>(); - ll.insertFront(2); - ll.insertEnd(3); - ll.insertEnd(5); - ll.insertFront(1); - ll.insert(0, 0); - ll.insert(4, 4); - ll.insert(7, 6); - ll.insert(6, 6); - - ll.remove(5); - ll.delete(6); - ll.pop(); - ll.poll(); - - String expected = "1 2 3 4 "; - Assert.assertEquals(expected, ll.toString()); - } - - @Test - public void testReverse() { - LinkedList ll = new LinkedList<>(); - ll.reverse(); - Assert.assertEquals(null, ll.toString()); - - ll.insertFront(3); - ll.reverse(); - Assert.assertEquals("3 ", ll.toString()); - - ll.insertFront(2); - ll.insertFront(1); - ll.insertEnd(4); - ll.reverse(); - Assert.assertEquals("4 3 2 1 ", ll.toString()); - } - - @Test - public void testSort() { - LinkedList ll = new LinkedList<>(); - ll.insertFront(5); - ll.insertEnd(2); - ll.insertFront(7); - ll.insertEnd(3); - ll.insertEnd(4); - ll.insertEnd(3); - ll.sort(); - - Assert.assertEquals("2 3 3 4 5 7 ", ll.toString()); - } + @Test + public void testInsert() { + LinkedList ll = new LinkedList<>(); + Assert.assertNull(ll.toString()); + + ll.insertFront(2); + ll.insertEnd(3); + ll.insertEnd(5); + ll.insertFront(1); + Assert.assertEquals("1 2 3 5 ", ll.toString()); + + ll.insert(0, 0); + ll.insert(4, 4); + ll.insert(7, 6); + ll.insert(6, 6); + Assert.assertEquals("0 1 2 3 4 5 6 7 ", ll.toString()); + } + + @Test + public void testSearchAndGet() { + LinkedList ll = new LinkedList<>(); + ll.insertFront(2); + ll.insertEnd(3); + ll.insertEnd(5); + ll.insertFront(1); + ll.insert(0, 0); + ll.insert(4, 4); + ll.insert(7, 6); + ll.insert(6, 6); + + Node test1 = ll.get(4); + Assert.assertEquals("4", test1.toString()); + + Node test2 = ll.get(3); + Assert.assertEquals("3", test2.toString()); + + Integer test3 = ll.search(4); + Assert.assertEquals("4", test3.toString()); + + Integer test4 = ll.search(3); + Assert.assertEquals("3", test4.toString()); + + Assert.assertEquals("0 1 2 3 4 5 6 7 ", ll.toString()); + } + + @Test + public void testRemove() { + + LinkedList ll = new LinkedList<>(); + ll.insertFront(2); + ll.insertEnd(3); + ll.insertEnd(5); + ll.insertFront(1); + ll.insert(0, 0); + ll.insert(4, 4); + ll.insert(7, 6); + ll.insert(6, 6); + + ll.remove(5); + ll.delete(6); + ll.pop(); + ll.poll(); + + String expected = "1 2 3 4 "; + Assert.assertEquals(expected, ll.toString()); + } + + @Test + public void testReverse() { + LinkedList ll = new LinkedList<>(); + ll.reverse(); + Assert.assertNull(ll.toString()); + + ll.insertFront(3); + ll.reverse(); + Assert.assertEquals("3 ", ll.toString()); + + ll.insertFront(2); + ll.insertFront(1); + ll.insertEnd(4); + ll.reverse(); + Assert.assertEquals("4 3 2 1 ", ll.toString()); + } + + @Test + public void testSort() { + LinkedList ll = new LinkedList<>(); + ll.insertFront(5); + ll.insertEnd(2); + ll.insertFront(7); + ll.insertEnd(3); + ll.insertEnd(4); + ll.insertEnd(3); + ll.sort(); + + Assert.assertEquals("2 3 3 4 5 7 ", ll.toString()); + } } diff --git a/src/test/java/dataStructures/queue/DequeTest.java b/src/test/java/dataStructures/queue/DequeTest.java new file mode 100644 index 00000000..0ef262c1 --- /dev/null +++ b/src/test/java/dataStructures/queue/DequeTest.java @@ -0,0 +1,63 @@ +package dataStructures.queue; + +import java.util.Optional; + +import org.junit.Assert; +import org.junit.Test; + +import dataStructures.queue.Deque.Deque; + +/** + * This class implements tests for the deque. + */ +public class DequeTest { + @Test + public void testEmpty() { + Deque d = new Deque<>(); + Assert.assertTrue(d.isEmpty()); + Assert.assertEquals(0, d.getSize()); + Assert.assertNull(d.peekFirst()); + Assert.assertNull(d.peekLast()); + } + + @Test + public void testInsertion() { + Deque d = new Deque<>(); + Assert.assertEquals("[]", d.toString()); + d.addElement(2); + d.addElement(3); + d.addFirst(1); + Assert.assertEquals("[ 1 2 3 ]", d.toString()); + Assert.assertEquals(3, d.getSize()); + } + + @Test + public void testPeek() { + Deque d = new Deque<>(); + Assert.assertNull(d.peekFirst()); + Assert.assertNull(d.peekLast()); + d.addElement(1); + d.addElement(2); + d.addElement(3); + d.peekLast(); + Assert.assertEquals(3, d.getSize()); + Assert.assertEquals(Optional.of(1).get(), d.peekFirst()); + Assert.assertEquals(Optional.of(3).get(), d.peekLast()); + } + + @Test + public void testPoll() { + Deque d = new Deque<>(); + Assert.assertNull(d.pollFirst()); + Assert.assertNull(d.pollLast()); + d.addElement(1); + d.addElement(2); + d.addElement(3); + Assert.assertEquals(Optional.of(1).get(), d.pollFirst()); + Assert.assertEquals(Optional.of(3).get(), d.pollLast()); + Assert.assertEquals(1, d.getSize()); + Assert.assertEquals("[ 2 ]", d.toString()); + Assert.assertEquals(Optional.of(2).get(), d.pollLast()); + Assert.assertEquals(0, d.getSize()); + } +} diff --git a/src/test/java/dataStructures/queue/MonotonicQueueTest.java b/src/test/java/dataStructures/queue/MonotonicQueueTest.java index c91c4885..8bca1530 100644 --- a/src/test/java/dataStructures/queue/MonotonicQueueTest.java +++ b/src/test/java/dataStructures/queue/MonotonicQueueTest.java @@ -7,54 +7,54 @@ * This class implements tests for the monotonic queue. */ public class MonotonicQueueTest { - @Test - public void testEmpty() { - MonotonicQueue q = new MonotonicQueue<>(); - Assert.assertEquals(true, q.isEmpty()); - Assert.assertEquals(null, q.max()); - Assert.assertEquals(null, q.pop()); - } + @Test + public void testEmpty() { + MonotonicQueue q = new MonotonicQueue<>(); + Assert.assertTrue(q.isEmpty()); + Assert.assertNull(q.max()); + Assert.assertNull(q.pop()); + } - @Test - public void testMax() { - MonotonicQueue q = new MonotonicQueue<>(); - q.push(2); - Assert.assertEquals("2", q.max().toString()); - q.push(7); - Assert.assertEquals("7", q.max().toString()); - q.push(1); - Assert.assertEquals("7", q.max().toString()); - q.push(7); - Assert.assertEquals("7", q.max().toString()); - q.push(5); - Assert.assertEquals("7", q.max().toString()); - q.push(4); - Assert.assertEquals("7", q.max().toString()); - q.push(3); - q.push(2); - q.push(5); - Assert.assertEquals("7", q.max().toString()); - } + @Test + public void testMax() { + MonotonicQueue q = new MonotonicQueue<>(); + q.push(2); + Assert.assertEquals("2", q.max().toString()); + q.push(7); + Assert.assertEquals("7", q.max().toString()); + q.push(1); + Assert.assertEquals("7", q.max().toString()); + q.push(7); + Assert.assertEquals("7", q.max().toString()); + q.push(5); + Assert.assertEquals("7", q.max().toString()); + q.push(4); + Assert.assertEquals("7", q.max().toString()); + q.push(3); + q.push(2); + q.push(5); + Assert.assertEquals("7", q.max().toString()); + } - @Test - public void testPop() { - MonotonicQueue q = new MonotonicQueue<>(); - q.push(2); - q.push(7); - q.push(1); - q.push(7); - q.push(5); - q.push(4); - q.push(3); - q.push(2); - q.push(5); - q.push(2); + @Test + public void testPop() { + MonotonicQueue q = new MonotonicQueue<>(); + q.push(2); + q.push(7); + q.push(1); + q.push(7); + q.push(5); + q.push(4); + q.push(3); + q.push(2); + q.push(5); + q.push(2); - Assert.assertEquals("7", q.pop().toString()); - Assert.assertEquals("7", q.pop().toString()); - Assert.assertEquals("5", q.pop().toString()); - q.pop(); - Assert.assertEquals("2", q.pop().toString()); - Assert.assertEquals(null, q.pop()); - } + Assert.assertEquals("7", q.pop().toString()); + Assert.assertEquals("7", q.pop().toString()); + Assert.assertEquals("5", q.pop().toString()); + q.pop(); + Assert.assertEquals("2", q.pop().toString()); + Assert.assertNull(q.pop()); + } } diff --git a/src/test/java/dataStructures/queue/QueueTest.java b/src/test/java/dataStructures/queue/QueueTest.java new file mode 100644 index 00000000..132d2410 --- /dev/null +++ b/src/test/java/dataStructures/queue/QueueTest.java @@ -0,0 +1,52 @@ +package dataStructures.queue; + +import org.junit.Assert; +import org.junit.Test; + +/** + * This class implements the test cases for the queue. + */ +public class QueueTest { + @Test + public void testEmptyQueue() { + Queue q = new Queue<>(); + Assert.assertEquals(0, q.size()); + Assert.assertTrue(q.isEmpty()); + Assert.assertNull(q.dequeue()); + } + + @Test + public void testEnqueue() { + Queue q = new Queue<>(); + q.enqueue(1); + q.enqueue(2); + q.enqueue(3); + Assert.assertEquals(3, q.size()); + } + + @Test + public void testPeek() { + Queue q = new Queue<>(); + q.enqueue(1); + Assert.assertEquals("1", q.peek().toString()); + q.enqueue(2); + q.enqueue(3); + q.peek(); + Assert.assertEquals("1", q.peek().toString()); + } + + @Test + public void testDequeue() { + Queue q = new Queue<>(); + q.enqueue(1); + q.enqueue(2); + q.enqueue(3); + Assert.assertEquals("1", q.dequeue().toString()); + Assert.assertEquals(2, q.size()); + q.dequeue(); + Assert.assertEquals(1, q.size()); + Assert.assertEquals("3", q.dequeue().toString()); + Assert.assertEquals(0, q.size()); + } + +} diff --git a/src/test/java/dataStructures/queue/dequeTest.java b/src/test/java/dataStructures/queue/dequeTest.java deleted file mode 100644 index d2612712..00000000 --- a/src/test/java/dataStructures/queue/dequeTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package dataStructures.queue; - -import java.util.Optional; - -import org.junit.Assert; -import org.junit.Test; - -import dataStructures.queue.Deque.Deque; - -/** - * This class implements tests for the deque. - */ -public class DequeTest { - @Test - public void testEmpty() { - Deque d = new Deque<>(); - Assert.assertEquals(true, d.isEmpty()); - Assert.assertEquals(0, d.getSize()); - Assert.assertEquals(d.peekFirst(), null); - Assert.assertEquals(d.peekLast(), null); - } - - @Test - public void testInsertion() { - Deque d = new Deque<>(); - Assert.assertEquals("[]", d.toString()); - d.addElement(2); - d.addElement(3); - d.addFirst(1); - Assert.assertEquals("[ 1 2 3 ]", d.toString()); - Assert.assertEquals(3, d.getSize()); - } - - @Test - public void testPeek() { - Deque d = new Deque<>(); - Assert.assertEquals(null, d.peekFirst()); - Assert.assertEquals(null, d.peekLast()); - d.addElement(1); - d.addElement(2); - d.addElement(3); - d.peekLast(); - Assert.assertEquals(3, d.getSize()); - Assert.assertEquals(Optional.of(1).get(), d.peekFirst()); - Assert.assertEquals(Optional.of(3).get(), d.peekLast()); - } - - @Test - public void testPoll() { - Deque d = new Deque<>(); - Assert.assertEquals(null, d.pollFirst()); - Assert.assertEquals(null, d.pollLast()); - d.addElement(1); - d.addElement(2); - d.addElement(3); - Assert.assertEquals(Optional.of(1).get(), d.pollFirst()); - Assert.assertEquals(Optional.of(3).get(), d.pollLast()); - Assert.assertEquals(1, d.getSize()); - Assert.assertEquals("[ 2 ]", d.toString()); - Assert.assertEquals(Optional.of(2).get(), d.pollLast()); - Assert.assertEquals(0, d.getSize()); - } -} diff --git a/src/test/java/dataStructures/queue/queueTest.java b/src/test/java/dataStructures/queue/queueTest.java deleted file mode 100644 index 7d04fa38..00000000 --- a/src/test/java/dataStructures/queue/queueTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package dataStructures.queue; - -import org.junit.Assert; -import org.junit.Test; - -/** - * This class implements the test cases for the queue. - */ -public class QueueTest { - @Test - public void testEmptyQueue() { - Queue q = new Queue<>(); - Assert.assertEquals(0, q.size()); - Assert.assertEquals(true, q.isEmpty()); - Assert.assertEquals(null, q.dequeue()); - } - - @Test - public void testEnqueue() { - Queue q = new Queue<>(); - q.enqueue(1); - q.enqueue(2); - q.enqueue(3); - Assert.assertEquals(3, q.size()); - } - - @Test - public void testPeek() { - Queue q = new Queue<>(); - q.enqueue(1); - Assert.assertEquals("1", q.peek().toString()); - q.enqueue(2); - q.enqueue(3); - q.peek(); - Assert.assertEquals("1", q.peek().toString()); - } - - @Test - public void testDequeue() { - Queue q = new Queue<>(); - q.enqueue(1); - q.enqueue(2); - q.enqueue(3); - Assert.assertEquals("1", q.dequeue().toString()); - Assert.assertEquals(2, q.size()); - q.dequeue(); - Assert.assertEquals(1, q.size()); - Assert.assertEquals("3", q.dequeue().toString()); - Assert.assertEquals(0, q.size()); - } - -} diff --git a/src/test/java/dataStructures/stack/StackTest.java b/src/test/java/dataStructures/stack/StackTest.java index 62b24956..02aa199c 100644 --- a/src/test/java/dataStructures/stack/StackTest.java +++ b/src/test/java/dataStructures/stack/StackTest.java @@ -7,28 +7,28 @@ * This class implements the test for the stack. */ public class StackTest { - @Test - public void testEmpty() { - Stack stk = new Stack<>(); - Assert.assertEquals(null, stk.pop()); - Assert.assertEquals(null, stk.peek()); - } + @Test + public void testEmpty() { + Stack stk = new Stack<>(); + Assert.assertNull(stk.pop()); + Assert.assertNull(stk.peek()); + } - @Test - public void testPopAndPeek() { - Stack stk = new Stack<>(1, 2, 3); - Assert.assertEquals("3", stk.peek().toString()); - Assert.assertEquals("3", stk.pop().toString()); - Assert.assertEquals("2", stk.peek().toString()); - } + @Test + public void testPopAndPeek() { + Stack stk = new Stack<>(1, 2, 3); + Assert.assertEquals("3", stk.peek().toString()); + Assert.assertEquals("3", stk.pop().toString()); + Assert.assertEquals("2", stk.peek().toString()); + } - @Test - public void testPush() { - Stack stk = new Stack<>(); - stk.push(1); - Assert.assertEquals("1", stk.peek().toString()); - stk.push(2); - stk.push(3); - Assert.assertEquals("3", stk.peek().toString()); - } + @Test + public void testPush() { + Stack stk = new Stack<>(); + stk.push(1); + Assert.assertEquals("1", stk.peek().toString()); + stk.push(2); + stk.push(3); + Assert.assertEquals("3", stk.peek().toString()); + } } diff --git a/src/test/java/random/andre/Test.java b/src/test/java/random/andre/Test.java new file mode 100644 index 00000000..be56064a --- /dev/null +++ b/src/test/java/random/andre/Test.java @@ -0,0 +1,44 @@ +package random.andre; + +import java.util.ArrayList; +import java.util.Arrays; + +import dataStructures.heap.MaxHeap; + +/** + * Basic testing of MaxHeap structure. + */ +public class Test { + /** + * Runs custom test. + * + * @param args unused. + */ + public static void main(String[] args) { + MaxHeap heap = new MaxHeap<>(); + heap.offer(1); + heap.offer(2); + heap.offer(9); + heap.offer(4); + heap.offer(5); + System.out.println(heap); + + System.out.println("Peek: " + heap.peek()); + System.out.println("Poll: " + heap.poll()); + System.out.println("Peek: " + heap.peek()); + + heap.offer(6); + + System.out.println("Peek: " + heap.peek()); + System.out.println("Poll: " + heap.poll()); + System.out.println("Poll: " + heap.poll()); + System.out.println(heap); + + heap.heapify(new ArrayList<>(Arrays.asList(5, 4, 6, 7, 2, 1, 9, 8, 0, 3))); + System.out.println(heap); + heap.remove(10); + heap.remove(5); + System.out.println(heap); + } +} + diff --git a/src/test/java/random/andre/lruCache/Test.java b/src/test/java/random/andre/lruCache/Test.java new file mode 100644 index 00000000..8e55c58f --- /dev/null +++ b/src/test/java/random/andre/lruCache/Test.java @@ -0,0 +1,27 @@ +package random.andre.lruCache; + +import dataStructures.lruCache.LRU; + +/** + * Basic testing. + */ +public class Test { + /** + * Runs the custom test. + * + * @param args unused. + */ + public static void main(String[] args) { + LRU lru = new LRU(4); + lru.update(5, "abc"); + lru.update(2, "def"); + lru.update(3, "ghi"); + lru.update(1, "jkl"); + lru.print(); // 5->2->3->1 + lru.update(2, "lmn"); + lru.print(); // 5->3->1->2 + lru.update(7, "opq"); // 5 should be evicted + lru.update(8, "rst"); // 3 should be evicted + lru.print(); // 1->2->7->8 + } +} diff --git a/src/test/java/random/andre/radixSort/Test.java b/src/test/java/random/andre/radixSort/Test.java new file mode 100644 index 00000000..2569c242 --- /dev/null +++ b/src/test/java/random/andre/radixSort/Test.java @@ -0,0 +1,62 @@ +package random.andre.radixSort; + +import algorithms.sorting.radixSort.RadixSort; + +/** + * Custom Test class for radixSort. To be phased out to JUnit tests. + */ +public class Test { + /** + * Helper method to print int elements in an array. + * + * @param arr The given array. + */ + private static void print(int[] arr) { + for (int num : arr) { + System.out.printf("%d ", num); + } + System.out.println(); + } + + /** + * Runs custom test cases. + * + * @param args unused. + */ + public static void main(String[] args) { + int[] test1 = + new int[] { + 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 1, 2, 15, 12, 20, 21, 12, 11, 5, 7, 23, + 30 + }; + RadixSort.radixSort(test1); + print(test1); + // Arrays.sort(test1); + // print(test1); + + int[] test2 = + new int[] { + 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10 + }; + RadixSort.radixSort(test2); + print(test2); + // Arrays.sort(test2); + // print(test2); + + int[] test3 = new int[] {9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100}; + RadixSort.radixSort(test3); + print(test3); + // Arrays.sort(test3); + // print(test3); + + int[] test4 = + new int[] { + 1 << 25 + 123, 1 << 28 + 340230, 34954043, 5678, 987654, 121234, 1, 0, 20493943, + 1 << 20 + 543298 + }; + // System.out.println(1 << 25 + 123); + RadixSort.radixSort(test4); + print(test4); + } +} diff --git a/src/test/java/random/andre/segmentTree/Test.java b/src/test/java/random/andre/segmentTree/Test.java new file mode 100644 index 00000000..5f1e06bc --- /dev/null +++ b/src/test/java/random/andre/segmentTree/Test.java @@ -0,0 +1,21 @@ +package random.andre.segmentTree; + +import dataStructures.segmentTree.SegmentTree; + +/** + * Basic testing. + */ +public class Test { + /** + * Custom test case for SegmentTree. + * + * @param args unused. + */ + public static void main(String[] args) { + int[] t = new int[] {1, 2, 3, 4, 5, 6, 7}; + SegmentTree tree = new SegmentTree(t); + System.out.println(tree.sumRange(2, 5)); + tree.update(3, 10); + System.out.println(tree.sumRange(2, 5)); + } +} diff --git a/src/test/java/random/andre/segmentTree/improved/Test.java b/src/test/java/random/andre/segmentTree/improved/Test.java new file mode 100644 index 00000000..53af119c --- /dev/null +++ b/src/test/java/random/andre/segmentTree/improved/Test.java @@ -0,0 +1,21 @@ +package random.andre.segmentTree.improved; + +import dataStructures.segmentTree.improvedSegmentTree.ImprovedSegmentTree; + +/** + * Basic testing. + */ +public class Test { + /** + * Custom test case for ImprovedSegmentTree. + * + * @param args unused. + */ + public static void main(String[] args) { + int[] t = new int[] {1, 2, 3, 4, 5, 6, 7}; + ImprovedSegmentTree tree = new ImprovedSegmentTree(t); + System.out.println(tree.sumRange(2, 5)); + tree.update(3, 10); + System.out.println(tree.sumRange(2, 5)); + } +} diff --git a/src/test/java/random/changxian/linkedList/Test.java b/src/test/java/random/changxian/linkedList/Test.java new file mode 100644 index 00000000..4045995f --- /dev/null +++ b/src/test/java/random/changxian/linkedList/Test.java @@ -0,0 +1,89 @@ +package random.changxian.linkedList; + +import dataStructures.linkedList.LinkedList; + +/** + * Basic Testing. + */ +public class Test { + /** + * Run custom test script. + * + * @param args unused. + */ + public static void main(String[] args) { + /* + * Testing insert methods + */ + System.out.println("Testing insert methods: "); + LinkedList linkedList = new LinkedList<>(); + System.out.println(linkedList); + linkedList.insertFront(2); + linkedList.insertEnd(3); + linkedList.insertEnd(5); + linkedList.insertFront(1); + System.out.println(linkedList); + linkedList.insert(0, 0); + linkedList.insert(4, 4); + linkedList.insert(6, 7); + linkedList.insert(7, 6); + linkedList.insert(6, 6); + System.out.println(linkedList); + System.out.println(); + + /* + * Testing search and get methods + */ + System.out.println("Testing search & get methods: "); + System.out.println(linkedList.search(4)); + System.out.println(linkedList.search(3)); + System.out.println(linkedList.get(3)); + System.out.println(linkedList.get(10)); + System.out.println(linkedList); + System.out.println(); + + /* + * Testing remove methods + */ + System.out.println("Testing remove methods: "); + linkedList.remove(5); + linkedList.delete(6); + linkedList.pop(); + linkedList.poll(); + System.out.println(linkedList); + System.out.println(); + + /* + * Testing reverse of Linked Lists + */ + System.out.println("Testing reverse method: "); + LinkedList newLinkedList = new LinkedList<>(); + newLinkedList.reverse(); + System.out.println(newLinkedList); + newLinkedList.insertFront(3); + System.out.println(newLinkedList); + linkedList.reverse(); + System.out.println(linkedList); + linkedList.reverse(); + System.out.println(linkedList); + System.out.println(); + + /* + * Test sorting + * Note: testing with entirely new nodes and linked list to avoid mutating linkedList + */ + LinkedList sortLinkedList = new LinkedList<>(); + sortLinkedList.insertFront(5); + sortLinkedList.insertEnd(2); + sortLinkedList.insertFront(7); + sortLinkedList.insertEnd(3); + sortLinkedList.insertEnd(4); + sortLinkedList.insertEnd(3); + System.out.println(sortLinkedList); + sortLinkedList.sort(); + System.out.println(sortLinkedList); + sortLinkedList.insertFront(8); + sortLinkedList.sort(); + System.out.println(sortLinkedList); + } +} diff --git a/src/test/java/random/changxian/minimumSpanningTree/kruskals/Test.java b/src/test/java/random/changxian/minimumSpanningTree/kruskals/Test.java new file mode 100644 index 00000000..5b325a34 --- /dev/null +++ b/src/test/java/random/changxian/minimumSpanningTree/kruskals/Test.java @@ -0,0 +1,48 @@ +package random.changxian.minimumSpanningTree.kruskals; + +import algorithms.minimumSpanningTree.kruskals.Kruskal; + +/** + * Basic testing. + * Motivating Example: Minimum Cost to Connect All Points + * A -9- C -2- E + * / / \ \ + * 3 4 7 2 + * / / \ / + * F -1- B --5-- D + */ +public class Test { + /** + * Runs the custom test. + * + * @param args unused. + */ + public static void main(String[] args) { + int[][] adjM = new int[6][6]; + adjM[0][2] = 9; + adjM[2][0] = 9; + + adjM[0][5] = 3; + adjM[5][0] = 3; + + adjM[1][5] = 1; + adjM[5][1] = 1; + + adjM[1][3] = 5; + adjM[3][1] = 5; + + adjM[1][2] = 4; + adjM[2][1] = 4; + + adjM[2][3] = 7; + adjM[3][2] = 7; + + adjM[2][4] = 2; + adjM[4][2] = 2; + + adjM[3][4] = 2; + adjM[4][3] = 2; + Kruskal kruskalAlgor = new Kruskal(); + System.out.println(kruskalAlgor.minCostConnectPoints(adjM)); + } +} diff --git a/src/test/java/random/changxian/minimumSpanningTree/prims/Test.java b/src/test/java/random/changxian/minimumSpanningTree/prims/Test.java new file mode 100644 index 00000000..73f9d116 --- /dev/null +++ b/src/test/java/random/changxian/minimumSpanningTree/prims/Test.java @@ -0,0 +1,49 @@ +package random.changxian.minimumSpanningTree.prims; + +import algorithms.minimumSpanningTree.prims.Prim; + +/** + * Basic testing. + * Motivating Example: Minimum Cost to Connect All Points + * A -9- C -2- E + * / / \ \ + * 3 4 7 2 + * / / \ / + * F -1- B --5-- D + */ +public class Test { + /** + * Runs the custom test. + * + * @param args unused. + */ + public static void main(String[] args) { + int[][] adjM = new int[6][6]; + adjM[0][2] = 9; + adjM[2][0] = 9; + + adjM[0][5] = 3; + adjM[5][0] = 3; + + adjM[1][5] = 1; + adjM[5][1] = 1; + + adjM[1][3] = 5; + adjM[3][1] = 5; + + adjM[1][2] = 4; + adjM[2][1] = 4; + + adjM[2][3] = 7; + adjM[3][2] = 7; + + adjM[2][4] = 2; + adjM[4][2] = 2; + + adjM[3][4] = 2; + adjM[4][3] = 2; + Prim primsAlgor = new Prim(); + System.out.println(primsAlgor.minCostConnectPoints(adjM)); + System.out.println(primsAlgor.minCostConnectPoints2(adjM)); + } +} diff --git a/src/test/java/random/changxian/stackAndQueue/monotonicQueue/Test.java b/src/test/java/random/changxian/stackAndQueue/monotonicQueue/Test.java new file mode 100644 index 00000000..2851a1db --- /dev/null +++ b/src/test/java/random/changxian/stackAndQueue/monotonicQueue/Test.java @@ -0,0 +1,37 @@ +package random.changxian.stackAndQueue.monotonicQueue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import dataStructures.queue.MonotonicQueue; + +/** + * Basic Testing. + * Maintains a non-increasing queue structure; useful for maximum/minimum sliding window fixed + * length problem + * Expected behaviour: Numbers that come before a larger number will be displayed as that larger + * number + */ +public class Test { + /** + * Runs the custom test. + * + * @param args unused. + */ + public static void main(String[] args) { + List toInsert = + new ArrayList<>(Arrays.asList(2, 7, 1, 3, 7, 5, 4, 3, 2, 5)); + MonotonicQueue mq = new MonotonicQueue<>(); + for (int num : toInsert) { + System.out.printf("Pushing %d%n", num); + mq.push(num); + System.out.printf("Max in queue now is %d%n", mq.max()); + } + + System.out.println("\nPopping thrice: "); + for (int i = 0; i < 7; i++) { + System.out.println(mq.pop()); + } + } +} diff --git a/src/test/java/random/changxian/stackAndQueue/queue/Test.java b/src/test/java/random/changxian/stackAndQueue/queue/Test.java new file mode 100644 index 00000000..f49eb6a3 --- /dev/null +++ b/src/test/java/random/changxian/stackAndQueue/queue/Test.java @@ -0,0 +1,26 @@ +package random.changxian.stackAndQueue.queue; + +import dataStructures.queue.Queue; + +/** + * Basic Testing. + */ +public class Test { + /** + * Runs the custom test. + * + * @param args unused. + */ + public static void main(String[] args) { + Queue myQueue = new Queue<>(); + System.out.println("Adding 7, 17, 27"); + myQueue.enqueue(7); + myQueue.enqueue(17); + myQueue.enqueue(27); + System.out.println("size: " + myQueue.size()); + System.out.println("peek: " + myQueue.peek()); + System.out.println("dequeue: " + myQueue.dequeue()); + System.out.println("peek: " + myQueue.peek()); + System.out.println("size: " + myQueue.size()); + } +} diff --git a/src/test/java/random/changxian/stackAndQueue/stack/Test.java b/src/test/java/random/changxian/stackAndQueue/stack/Test.java new file mode 100644 index 00000000..b3bb71eb --- /dev/null +++ b/src/test/java/random/changxian/stackAndQueue/stack/Test.java @@ -0,0 +1,37 @@ +package random.changxian.stackAndQueue.stack; + +import dataStructures.stack.Stack; + +/** + * Basic Testing. + */ +public class Test { + /** + * Runs custom test script. + * + * @param args unused. + */ + public static void main(String[] args) { + System.out.println("Testing on empty stack: "); + Stack myStack = new Stack<>(); + System.out.println(myStack.pop()); + System.out.println(myStack.peek()); + + System.out.println("Initialize stack with 'abc', 'def', 'ghi'"); + myStack = new Stack<>("abc", "def", "ghi"); + + System.out.println("call peek(): "); + System.out.println(myStack.peek()); + + System.out.println("call pop(): "); + System.out.println(myStack.pop()); + + System.out.println("call peek(): "); + System.out.println(myStack.peek()); + + System.out.println("pushing an element: "); + myStack.push("midnight prince"); + System.out.println("peek to see pushed element: "); + System.out.println(myStack.peek()); + } +} diff --git a/src/test/java/random/kaiting/avlTree/Test.java b/src/test/java/random/kaiting/avlTree/Test.java new file mode 100644 index 00000000..33664d8b --- /dev/null +++ b/src/test/java/random/kaiting/avlTree/Test.java @@ -0,0 +1,75 @@ +package random.kaiting.avlTree; + +import dataStructures.avlTree.AVLTree; + +/** + * Basic testing. + * Note that a tree structure can be entirely + * determined with one of the following: + * 1. In-order + pre-order + * 2. In-order + post-order + * 3. In-order + level-order + */ +public class Test { + /** + * Runs the custom test. + * + * @param args unused. + */ + public static void main(String[] args) { + AVLTree tree = new AVLTree<>(); + System.out.println("Inserting..."); + tree.insert(5); + tree.insert(4); + tree.insert(8); + tree.insert(6); + tree.insert(7); + tree.insert(3); + tree.insert(1); + + // tree: + // 5 + // / \ + // 3 7 + // / \ / \ + // 1 4 6 8 + // + + Integer x = 6; + System.out.println("parent of " + x + " is " + tree.search(x).getParent().getKey().toString()); + + System.out.println("Deleting..."); + tree.delete(5); + tree.delete(7); + tree.printInorder(); + tree.printPreorder(); + tree.printLevelorder(); + + System.out.println("Inserting..."); + tree.insert(9); + tree.insert(2); + tree.printInorder(); + tree.printPreorder(); + tree.printPostorder(); + + // updated tree: + // 6 + // / \ + // 3 8 + // / \ \ + // 1 4 9 + // \ + // 2 + + x = 4; + System.out.println("parent of " + x + " is " + tree.search(x).getParent().getKey().toString()); + + System.out.println("Testing successors & predecessors queries..."); + System.out.println(tree.predecessor(1)); + System.out.println(tree.predecessor(6)); + + System.out.println(tree.successor(1)); + System.out.println(tree.successor(6)); + System.out.println(tree.successor(9)); + } +} diff --git a/src/test/java/randomTests/kaiting/quickSort/test.java b/src/test/java/random/kaiting/quickSort/Test.java similarity index 99% rename from src/test/java/randomTests/kaiting/quickSort/test.java rename to src/test/java/random/kaiting/quickSort/Test.java index d9f45613..3269873d 100644 --- a/src/test/java/randomTests/kaiting/quickSort/test.java +++ b/src/test/java/random/kaiting/quickSort/Test.java @@ -23,4 +23,4 @@ // } // System.out.println(); // } -//} \ No newline at end of file +//} diff --git a/src/test/java/random/kaiting/trie/TrieTest.java b/src/test/java/random/kaiting/trie/TrieTest.java new file mode 100644 index 00000000..ab4bc78b --- /dev/null +++ b/src/test/java/random/kaiting/trie/TrieTest.java @@ -0,0 +1,29 @@ +package random.kaiting.trie; + +import dataStructures.trie.Trie; + +/** + * Basic testing of trie. + */ +public class TrieTest { + /** + * Runs the custom test. + * + * @param args unused. + */ + public static void main(String[] args) { + Trie trie = new Trie(); + trie.insert("apple"); + trie.insert("coconut"); + trie.insert("apollo"); + trie.insert("fail"); + trie.insert("failure"); + + System.out.println(trie.search("apple")); + System.out.println(trie.search("app")); + System.out.println(trie.startsWith("app")); + trie.prune("fail"); + System.out.println(trie.search("fail")); + System.out.println(trie.search("failure")); + } +} diff --git a/src/test/java/random/kaiting/trie/legacy/TrieTest.java b/src/test/java/random/kaiting/trie/legacy/TrieTest.java new file mode 100644 index 00000000..eca86e9a --- /dev/null +++ b/src/test/java/random/kaiting/trie/legacy/TrieTest.java @@ -0,0 +1,24 @@ +package random.kaiting.trie.legacy; + +import dataStructures.trie.legacy.Trie; + +/** + * Basic testing of trie. + */ +public class TrieTest { + /** + * Runs the custom test. + * + * @param args unused. + */ + public static void main(String[] args) { + Trie trie = new Trie(); + trie.insert("apple"); + trie.insert("coconut"); + trie.insert("apollo"); + + System.out.println(trie.search("apple")); + System.out.println(trie.search("app")); + System.out.println(trie.startsWith("app")); + } +} diff --git a/src/test/java/random/owen/unionFind/matrixToListConverter/MatrixConverterTest.java b/src/test/java/random/owen/unionFind/matrixToListConverter/MatrixConverterTest.java new file mode 100644 index 00000000..d48b7bb9 --- /dev/null +++ b/src/test/java/random/owen/unionFind/matrixToListConverter/MatrixConverterTest.java @@ -0,0 +1,76 @@ +package random.owen.unionFind.matrixToListConverter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +import algorithms.util.MatrixToListConverter; +import algorithms.util.MatrixToListConverter.InvalidMatrixException; + +/** + * Test cases for {@link MatrixToListConverter}. + */ +public class MatrixConverterTest { + @Test + public void test_convertArray_shouldReturnAccurateRepresentation() { + // empty matrix + int[][] firstMatrix = new int[0][0]; + ArrayList[] firstList = new ArrayList[0]; + List[] firstResult = MatrixToListConverter.convert(firstMatrix); + + // symmetric matrix (undirected graph) + int[][] secondMatrix = { + {0, 1, 1}, + {1, 0, 1}, + {1, 1, 0}, + }; + ArrayList[] secondList = new ArrayList[3]; + for (int i = 0; i < 3; i++) { + secondList[i] = new ArrayList<>(); + } + Collections.addAll(secondList[0], 1, 2); + Collections.addAll(secondList[1], 0, 2); + Collections.addAll(secondList[2], 0, 1); + List[] secondResult = MatrixToListConverter.convert(secondMatrix); + + //asymmetric matrix (directed graph) + int[][] thirdMatrix = { + {0, 0, 1}, + {1, 0, 0}, + {1, 1, 1}, + }; + ArrayList[] thirdList = new ArrayList[3]; + for (int i = 0; i < 3; i++) { + thirdList[i] = new ArrayList<>(); + } + Collections.addAll(thirdList[0], 2); + Collections.addAll(thirdList[1], 0); + Collections.addAll(thirdList[2], 0, 1, 2); + List[] thirdResult = MatrixToListConverter.convert(thirdMatrix); + + //invalid matrix (n x m graph, n != m) + int[][] fourthMatrix = { + {0, 0}, + {1, 0}, + {1, 1}, + }; + List[] fourthList = new ArrayList[3]; + + assert Arrays.equals(firstList, firstResult); + for (int i = 0; i < 1; i++) { + assert secondList[i].equals(secondResult[i]); + } + for (int i = 0; i < 1; i++) { + assert thirdList[i].equals(thirdResult[i]); + } + try { + MatrixToListConverter.convert(fourthMatrix); + } catch (InvalidMatrixException error) { + assert error.getMessage() == "The matrix must be N x N, not N x M!"; + } + + } +} diff --git a/src/test/java/random/owen/unionFind/quickFind/generalised/Test.java b/src/test/java/random/owen/unionFind/quickFind/generalised/Test.java new file mode 100644 index 00000000..2090bcb7 --- /dev/null +++ b/src/test/java/random/owen/unionFind/quickFind/generalised/Test.java @@ -0,0 +1,68 @@ +package random.owen.unionFind.quickFind.generalised; + +import java.util.ArrayList; +import java.util.List; + +import dataStructures.disjointSet.quickFind.generalised.QuickFind; + +/** + * Some simple tests. + */ +public class Test { + /** + * Runs custom test script. + * + * @param args unused. + */ + public static void main(String[] args) { + List lst = new ArrayList<>(); + lst.add("Dog"); + lst.add("Egg"); + lst.add("Carabiner"); + lst.add("Cat"); + lst.add("Potatoes"); + lst.add("Hamster"); + lst.add("Rope"); + lst.add("Fox"); + lst.add("Chalk"); + for (int i = 0; i < lst.size(); i++) { + System.out.println("Added " + lst.get(i)); + } + + QuickFind qf = new QuickFind<>(lst); + qf.union("Dog", "Cat"); + qf.union("Fox", "Hamster"); + qf.union("Fox", "Cat"); + + qf.union("Carabiner", "Rope"); + qf.union("Chalk", "Rope"); + + qf.union("Egg", "Potatoes"); + + List comp1 = qf.retrieveComponent("Hamster"); + System.out.print("Comp1: "); + for (int i = 0; i < comp1.size(); i++) { + System.out.print(comp1.get(i) + ", "); + } + + List comp2 = qf.retrieveComponent("Chalk"); + System.out.print("\nComp2: "); + for (int i = 0; i < comp2.size(); i++) { + System.out.print(comp2.get(i) + ", "); + } + + List comp3 = qf.retrieveComponent("Egg"); + System.out.print("\nComp3: "); + for (int i = 0; i < comp3.size(); i++) { + System.out.print(comp3.get(i) + ", "); + } + + qf.add("Coconut"); + qf.union("Egg", "Coconut"); + List update = qf.retrieveComponent("Egg"); + System.out.print("\nUpdate Comp3: "); + for (int i = 0; i < update.size(); i++) { + System.out.print(update.get(i) + ", "); + } + } +} diff --git a/src/test/java/random/owen/unionFind/quickFind/simplified/Test.java b/src/test/java/random/owen/unionFind/quickFind/simplified/Test.java new file mode 100644 index 00000000..32c6c03c --- /dev/null +++ b/src/test/java/random/owen/unionFind/quickFind/simplified/Test.java @@ -0,0 +1,43 @@ +package random.owen.unionFind.quickFind.simplified; + +import java.util.List; + +import dataStructures.disjointSet.quickFind.simplified.QuickFind; + +/** + * Basic testing. + */ +public class Test { + /** + * Runs custom test script. + * + * @param args unused. + */ + public static void main(String[] args) { + QuickFind qf = new QuickFind(10); + + qf.union(1, 9); + qf.union(4, 6); + qf.union(1, 4); + + qf.union(2, 8); + qf.union(5, 7); + qf.union(2, 7); + + List comp1 = qf.retrieveComponent(1); + for (int i = 0; i < comp1.size(); i++) { + System.out.printf("%d, ", comp1.get(i)); + } + System.out.println("\nTesting next component:"); + List comp2 = qf.retrieveComponent(2); + for (int i = 0; i < comp2.size(); i++) { + System.out.printf("%d, ", comp2.get(i)); + } + System.out.println("\nTesting next component:"); + List comp3 = qf.retrieveComponent(3); + for (int i = 0; i < comp3.size(); i++) { + System.out.printf("%d, ", comp3.get(i)); + } + System.out.println(); + } +} diff --git a/src/test/java/random/owen/unionFind/weightedUnion/Test.java b/src/test/java/random/owen/unionFind/weightedUnion/Test.java new file mode 100644 index 00000000..3a60e800 --- /dev/null +++ b/src/test/java/random/owen/unionFind/weightedUnion/Test.java @@ -0,0 +1,50 @@ +package random.owen.unionFind.weightedUnion; + +import java.util.ArrayList; +import java.util.List; + +import dataStructures.disjointSet.weightedUnion.Union; + +/** + * Some simple tests. + */ +public class Test { + /** + * Runs the custom test. + * + * @param args unused. + */ + public static void main(String[] args) { + List lst = new ArrayList<>(); + lst.add("Dog"); + lst.add("Egg"); + lst.add("Carabiner"); + lst.add("Cat"); + lst.add("Potatoes"); + lst.add("Hamster"); + lst.add("Rope"); + lst.add("Fox"); + lst.add("Chalk"); + for (int i = 0; i < lst.size(); i++) { + System.out.println("Added " + lst.get(i)); + } + + Union wu = new Union<>(lst); + wu.combine("Dog", "Cat"); + wu.combine("Fox", "Hamster"); + wu.combine("Fox", "Cat"); + System.out.println(wu.isSameComponent("Dog", "Fox")); // true + System.out.println(wu.isSameComponent("Rope", "Fox")); // false + + wu.combine("Carabiner", "Rope"); + wu.combine("Chalk", "Rope"); + System.out.println(wu.isSameComponent("Rope", "Carabiner")); // true + + wu.combine("Egg", "Potatoes"); + System.out.println(wu.isSameComponent("Egg", "pot")); // duuhh.. + false + + wu.add("Coconut"); + wu.combine("Egg", "Coconut"); + System.out.println(wu.isSameComponent("Coconut", "Potatoes")); // true + } +} diff --git a/src/test/java/randomTests/andre/Test.java b/src/test/java/randomTests/andre/Test.java deleted file mode 100644 index 09937fda..00000000 --- a/src/test/java/randomTests/andre/Test.java +++ /dev/null @@ -1,44 +0,0 @@ -package randomTests.andre; - -import java.util.ArrayList; -import java.util.Arrays; - -import dataStructures.heap.MaxHeap; - -/** - * Basic testing of MaxHeap structure. - */ -public class Test { - /** - * Runs custom test. - * - * @param args unused. - */ - public static void main(String[] args) { - MaxHeap heap = new MaxHeap<>(); - heap.offer(1); - heap.offer(2); - heap.offer(9); - heap.offer(4); - heap.offer(5); - System.out.println(heap); - - System.out.println("Peek: " + heap.peek()); - System.out.println("Poll: " + heap.poll()); - System.out.println("Peek: " + heap.peek()); - - heap.offer(6); - - System.out.println("Peek: " + heap.peek()); - System.out.println("Poll: " + heap.poll()); - System.out.println("Poll: " + heap.poll()); - System.out.println(heap); - - heap.heapify(new ArrayList<>(Arrays.asList(5, 4, 6, 7, 2, 1, 9, 8, 0, 3))); - System.out.println(heap); - heap.remove(10); - heap.remove(5); - System.out.println(heap); - } -} - diff --git a/src/test/java/randomTests/andre/lruCache/test.java b/src/test/java/randomTests/andre/lruCache/test.java deleted file mode 100644 index 29e4cff2..00000000 --- a/src/test/java/randomTests/andre/lruCache/test.java +++ /dev/null @@ -1,27 +0,0 @@ -package randomTests.andre.lruCache; - -import dataStructures.lruCache.LRU; - -/** - * Basic testing. - */ -public class Test { - /** - * Runs the custom test. - * - * @param args unused. - */ - public static void main(String[] args) { - LRU lru = new LRU(4); - lru.update(5, "abc"); - lru.update(2, "def"); - lru.update(3, "ghi"); - lru.update(1, "jkl"); - lru.print(); // 5->2->3->1 - lru.update(2, "lmn"); - lru.print(); // 5->3->1->2 - lru.update(7, "opq"); // 5 should be evicted - lru.update(8, "rst"); // 3 should be evicted - lru.print(); // 1->2->7->8 - } -} diff --git a/src/test/java/randomTests/andre/radixSort/Test.java b/src/test/java/randomTests/andre/radixSort/Test.java deleted file mode 100644 index fdc8b4ab..00000000 --- a/src/test/java/randomTests/andre/radixSort/Test.java +++ /dev/null @@ -1,56 +0,0 @@ -package randomTests.andre.radixSort; - -import algorithms.sorting.radixSort.RadixSort; - -/** - * Custom Test class for radixSort. To be phased out to JUnit tests. - */ -public class Test { - /** - * Helper method to print int elements in an array. - * - * @param arr The given array. - */ - private static void print(int[] arr) { - for (int num : arr) { - System.out.print(String.format("%d ", num)); - } - System.out.println(); - } - - /** - * Runs custom test cases. - * - * @param args unused. - */ - public static void main(String[] args) { - int[] test1 = - new int[] {2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 1, 2, 15, 12, 20, 21, 12, 11, 5, 7, 23, - 30}; - RadixSort.radixSort(test1); - print(test1); - // Arrays.sort(test1); - // print(test1); - - int[] test2 = - new int[] {9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, - 9, 10}; - RadixSort.radixSort(test2); - print(test2); - // Arrays.sort(test2); - // print(test2); - - int[] test3 = new int[] {9, 8, 7, 6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 100}; - RadixSort.radixSort(test3); - print(test3); - // Arrays.sort(test3); - // print(test3); - - int[] test4 = - new int[] {1 << 25 + 123, 1 << 28 + 340230, 34954043, 5678, 987654, 121234, 1, 0, 20493943, - 1 << 20 + 543298}; - // System.out.println(1 << 25 + 123); - RadixSort.radixSort(test4); - print(test4); - } -} diff --git a/src/test/java/randomTests/andre/segmentTree/improved/test.java b/src/test/java/randomTests/andre/segmentTree/improved/test.java deleted file mode 100644 index 5ceaffef..00000000 --- a/src/test/java/randomTests/andre/segmentTree/improved/test.java +++ /dev/null @@ -1,21 +0,0 @@ -package randomTests.andre.segmentTree.improved; - -import dataStructures.segmentTree.improvedSegmentTree.ImprovedSegmentTree; - -/** - * Basic testing. - */ -public class Test { - /** - * Custom test case for ImprovedSegmentTree. - * - * @param args unused. - */ - public static void main(String[] args) { - int[] t = new int[] {1, 2, 3, 4, 5, 6, 7}; - ImprovedSegmentTree tree = new ImprovedSegmentTree(t); - System.out.println(tree.sumRange(2, 5)); - tree.update(3, 10); - System.out.println(tree.sumRange(2, 5)); - } -} diff --git a/src/test/java/randomTests/andre/segmentTree/test.java b/src/test/java/randomTests/andre/segmentTree/test.java deleted file mode 100644 index 07ee86a4..00000000 --- a/src/test/java/randomTests/andre/segmentTree/test.java +++ /dev/null @@ -1,21 +0,0 @@ -package randomTests.andre.segmentTree; - -import dataStructures.segmentTree.SegmentTree; - -/** - * Basic testing. - */ -public class Test { - /** - * Custom test case for SegmentTree. - * - * @param args unused. - */ - public static void main(String[] args) { - int[] t = new int[] {1, 2, 3, 4, 5, 6, 7}; - SegmentTree tree = new SegmentTree(t); - System.out.println(tree.sumRange(2, 5)); - tree.update(3, 10); - System.out.println(tree.sumRange(2, 5)); - } -} diff --git a/src/test/java/randomTests/changxian/linkedList/Test.java b/src/test/java/randomTests/changxian/linkedList/Test.java deleted file mode 100644 index e722fc3a..00000000 --- a/src/test/java/randomTests/changxian/linkedList/Test.java +++ /dev/null @@ -1,89 +0,0 @@ -package randomTests.changxian.linkedList; - -import dataStructures.linkedList.LinkedList; - -/** - * Basic Testing. - */ -public class Test { - /** - * Run custom test script. - * - * @param args unused. - */ - public static void main(String[] args) { - /* - * Testing insert methods - */ - System.out.println("Testing insert methods: "); - LinkedList linkedList = new LinkedList<>(); - System.out.println(linkedList); - linkedList.insertFront(2); - linkedList.insertEnd(3); - linkedList.insertEnd(5); - linkedList.insertFront(1); - System.out.println(linkedList); - linkedList.insert(0, 0); - linkedList.insert(4, 4); - linkedList.insert(6, 7); - linkedList.insert(7, 6); - linkedList.insert(6, 6); - System.out.println(linkedList); - System.out.println(); - - /* - * Testing search and get methods - */ - System.out.println("Testing search & get methods: "); - System.out.println(linkedList.search(4)); - System.out.println(linkedList.search(3)); - System.out.println(linkedList.get(3)); - System.out.println(linkedList.get(10)); - System.out.println(linkedList); - System.out.println(); - - /* - * Testing remove methods - */ - System.out.println("Testing remove methods: "); - linkedList.remove(5); - linkedList.delete(6); - linkedList.pop(); - linkedList.poll(); - System.out.println(linkedList); - System.out.println(); - - /* - * Testing reverse of Linked Lists - */ - System.out.println("Testing reverse method: "); - LinkedList newLinkedList = new LinkedList<>(); - newLinkedList.reverse(); - System.out.println(newLinkedList); - newLinkedList.insertFront(3); - System.out.println(newLinkedList); - linkedList.reverse(); - System.out.println(linkedList); - linkedList.reverse(); - System.out.println(linkedList); - System.out.println(); - - /* - * Test sorting - * Note: testing with entirely new nodes and linked list to avoid mutating linkedList - */ - LinkedList sortLinkedList = new LinkedList<>(); - sortLinkedList.insertFront(5); - sortLinkedList.insertEnd(2); - sortLinkedList.insertFront(7); - sortLinkedList.insertEnd(3); - sortLinkedList.insertEnd(4); - sortLinkedList.insertEnd(3); - System.out.println(sortLinkedList); - sortLinkedList.sort(); - System.out.println(sortLinkedList); - sortLinkedList.insertFront(8); - sortLinkedList.sort(); - System.out.println(sortLinkedList); - } -} diff --git a/src/test/java/randomTests/changxian/minimumSpanningTree/kruskals/test.java b/src/test/java/randomTests/changxian/minimumSpanningTree/kruskals/test.java deleted file mode 100644 index 9451ae52..00000000 --- a/src/test/java/randomTests/changxian/minimumSpanningTree/kruskals/test.java +++ /dev/null @@ -1,49 +0,0 @@ -package randomTests.changxian.minimumSpanningTree.kruskals; - -import algorithms.minimumSpanningTree.kruskals.Kruskal; - -/** - * Basic testing. - * Motivating Example: Minimum Cost to Connect All Points - * A -9- C -2- E - * / / \ \ - * 3 4 7 2 - * / / \ / - * F -1- B --5-- D - */ - -public class Test { - /** - * Runs the custom test. - * - * @param args unused. - */ - public static void main(String[] args) { - int[][] adjM = new int[6][6]; - adjM[0][2] = 9; - adjM[2][0] = 9; - - adjM[0][5] = 3; - adjM[5][0] = 3; - - adjM[1][5] = 1; - adjM[5][1] = 1; - - adjM[1][3] = 5; - adjM[3][1] = 5; - - adjM[1][2] = 4; - adjM[2][1] = 4; - - adjM[2][3] = 7; - adjM[3][2] = 7; - - adjM[2][4] = 2; - adjM[4][2] = 2; - - adjM[3][4] = 2; - adjM[4][3] = 2; - Kruskal kruskalAlgor = new Kruskal(); - System.out.println(kruskalAlgor.minCostConnectPoints(adjM)); - } -} diff --git a/src/test/java/randomTests/changxian/minimumSpanningTree/prims/test.java b/src/test/java/randomTests/changxian/minimumSpanningTree/prims/test.java deleted file mode 100644 index 455ebe29..00000000 --- a/src/test/java/randomTests/changxian/minimumSpanningTree/prims/test.java +++ /dev/null @@ -1,50 +0,0 @@ -package randomTests.changxian.minimumSpanningTree.prims; - -import algorithms.minimumSpanningTree.prims.Prim; - -/** - * Basic testing. - * Motivating Example: Minimum Cost to Connect All Points - * A -9- C -2- E - * / / \ \ - * 3 4 7 2 - * / / \ / - * F -1- B --5-- D - */ - -public class Test { - /** - * Runs the custom test. - * - * @param args unused. - */ - public static void main(String[] args) { - int[][] adjM = new int[6][6]; - adjM[0][2] = 9; - adjM[2][0] = 9; - - adjM[0][5] = 3; - adjM[5][0] = 3; - - adjM[1][5] = 1; - adjM[5][1] = 1; - - adjM[1][3] = 5; - adjM[3][1] = 5; - - adjM[1][2] = 4; - adjM[2][1] = 4; - - adjM[2][3] = 7; - adjM[3][2] = 7; - - adjM[2][4] = 2; - adjM[4][2] = 2; - - adjM[3][4] = 2; - adjM[4][3] = 2; - Prim primsAlgor = new Prim(); - System.out.println(primsAlgor.minCostConnectPoints(adjM)); - System.out.println(primsAlgor.minCostConnectPoints2(adjM)); - } -} diff --git a/src/test/java/randomTests/changxian/stackAndQueue/monotonicQueue/Test.java b/src/test/java/randomTests/changxian/stackAndQueue/monotonicQueue/Test.java deleted file mode 100644 index 5d472d60..00000000 --- a/src/test/java/randomTests/changxian/stackAndQueue/monotonicQueue/Test.java +++ /dev/null @@ -1,37 +0,0 @@ -package randomTests.changxian.stackAndQueue.monotonicQueue; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import dataStructures.queue.MonotonicQueue; - -/** - * Basic Testing. - * Maintains a non-increasing queue structure; useful for maximum/minimum sliding window fixed - * length problem - * Expected behaviour: Numbers that come before a larger number will be displayed as that larger - * number - */ -public class Test { - /** - * Runs the custom test. - * - * @param args unused. - */ - public static void main(String[] args) { - List toInsert = - new ArrayList<>(Arrays.asList(2, 7, 1, 3, 7, 5, 4, 3, 2, 5)); - MonotonicQueue mq = new MonotonicQueue<>(); - for (int num : toInsert) { - System.out.println(String.format("Pushing %d", num)); - mq.push(num); - System.out.println(String.format("Max in queue now is %d", mq.max())); - } - - System.out.println("\nPopping thrice: "); - for (int i = 0; i < 7; i++) { - System.out.println(mq.pop()); - } - } -} diff --git a/src/test/java/randomTests/changxian/stackAndQueue/queue/Test.java b/src/test/java/randomTests/changxian/stackAndQueue/queue/Test.java deleted file mode 100644 index e58d484f..00000000 --- a/src/test/java/randomTests/changxian/stackAndQueue/queue/Test.java +++ /dev/null @@ -1,26 +0,0 @@ -package randomTests.changxian.stackAndQueue.queue; - -import dataStructures.queue.Queue; - -/** - * Basic Testing. - */ -public class Test { - /** - * Runs the custom test. - * - * @param args unused. - */ - public static void main(String[] args) { - Queue myQueue = new Queue<>(); - System.out.println("Adding 7, 17, 27"); - myQueue.enqueue(7); - myQueue.enqueue(17); - myQueue.enqueue(27); - System.out.println("size: " + myQueue.size()); - System.out.println("peek: " + myQueue.peek()); - System.out.println("dequeue: " + myQueue.dequeue()); - System.out.println("peek: " + myQueue.peek()); - System.out.println("size: " + myQueue.size()); - } -} diff --git a/src/test/java/randomTests/changxian/stackAndQueue/stack/Test.java b/src/test/java/randomTests/changxian/stackAndQueue/stack/Test.java deleted file mode 100644 index 30ff8674..00000000 --- a/src/test/java/randomTests/changxian/stackAndQueue/stack/Test.java +++ /dev/null @@ -1,37 +0,0 @@ -package randomTests.changxian.stackAndQueue.stack; - -import dataStructures.stack.Stack; - -/** - * Basic Testing. - */ -public class Test { - /** - * Runs custom test script. - * - * @param args unused. - */ - public static void main(String[] args) { - System.out.println("Testing on empty stack: "); - Stack myStack = new Stack<>(); - System.out.println(myStack.pop()); - System.out.println(myStack.peek()); - - System.out.println("Initialize stack with 'abc', 'def', 'ghi'"); - myStack = new Stack<>("abc", "def", "ghi"); - - System.out.println("call peek(): "); - System.out.println(myStack.peek()); - - System.out.println("call pop(): "); - System.out.println(myStack.pop()); - - System.out.println("call peek(): "); - System.out.println(myStack.peek()); - - System.out.println("pushing an element: "); - myStack.push("midnight prince"); - System.out.println("peek to see pushed element: "); - System.out.println(myStack.peek()); - } -} diff --git a/src/test/java/randomTests/kaiting/avlTree/Test.java b/src/test/java/randomTests/kaiting/avlTree/Test.java deleted file mode 100644 index 8cc67eca..00000000 --- a/src/test/java/randomTests/kaiting/avlTree/Test.java +++ /dev/null @@ -1,75 +0,0 @@ -package randomTests.kaiting.avlTree; - -import dataStructures.avlTree.AVLTree; - -/** - * Basic testing. - * Note that a tree structure can be entirely - * determined with one of the following: - * 1. In-order + pre-order - * 2. In-order + post-order - * 3. In-order + level-order - */ -public class Test { - /** - * Runs the custom test. - * - * @param args unused. - */ - public static void main(String[] args) { - AVLTree tree = new AVLTree<>(); - System.out.println("Inserting..."); - tree.insert(5); - tree.insert(4); - tree.insert(8); - tree.insert(6); - tree.insert(7); - tree.insert(3); - tree.insert(1); - - // tree: - // 5 - // / \ - // 3 7 - // / \ / \ - // 1 4 6 8 - // - - Integer x = 6; - System.out.println("parent of " + x.toString() + " is " + tree.search(x).parent.key.toString()); - - System.out.println("Deleting..."); - tree.delete(5); - tree.delete(7); - tree.printInorder(); - tree.printPreorder(); - tree.printLevelorder(); - - System.out.println("Inserting..."); - tree.insert(9); - tree.insert(2); - tree.printInorder(); - tree.printPreorder(); - tree.printPostorder(); - - // updated tree: - // 6 - // / \ - // 3 8 - // / \ \ - // 1 4 9 - // \ - // 2 - - x = 4; - System.out.println("parent of " + x.toString() + " is " + tree.search(x).parent.key.toString()); - - System.out.println("Testing successors & predecessors queries..."); - System.out.println(tree.predecessor(1)); - System.out.println(tree.predecessor(6)); - - System.out.println(tree.successor(1)); - System.out.println(tree.successor(6)); - System.out.println(tree.successor(9)); - } -} diff --git a/src/test/java/randomTests/kaiting/trie/TrieTest.java b/src/test/java/randomTests/kaiting/trie/TrieTest.java deleted file mode 100644 index 57096835..00000000 --- a/src/test/java/randomTests/kaiting/trie/TrieTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package randomTests.kaiting.trie; - -import dataStructures.trie.Trie; - -/** - * Basic testing of trie. - */ -public class TrieTest { - /** - * Runs the custom test. - * - * @param args unused. - */ - public static void main(String[] args) { - Trie trie = new Trie(); - trie.insert("apple"); - trie.insert("coconut"); - trie.insert("apollo"); - trie.insert("fail"); - trie.insert("failure"); - - System.out.println(trie.search("apple")); - System.out.println(trie.search("app")); - System.out.println(trie.startsWith("app")); - trie.prune("fail"); - System.out.println(trie.search("fail")); - System.out.println(trie.search("failure")); - } -} diff --git a/src/test/java/randomTests/kaiting/trie/legacy/TrieTest.java b/src/test/java/randomTests/kaiting/trie/legacy/TrieTest.java deleted file mode 100644 index d0af03ca..00000000 --- a/src/test/java/randomTests/kaiting/trie/legacy/TrieTest.java +++ /dev/null @@ -1,24 +0,0 @@ -package randomTests.kaiting.trie.legacy; - -import dataStructures.trie.legacy.Trie; - -/** - * Basic testing of trie. - */ -public class TrieTest { - /** - * Runs the custom test. - * - * @param args unused. - */ - public static void main(String[] args) { - Trie trie = new Trie(); - trie.insert("apple"); - trie.insert("coconut"); - trie.insert("apollo"); - - System.out.println(trie.search("apple")); - System.out.println(trie.search("app")); - System.out.println(trie.startsWith("app")); - } -} diff --git a/src/test/java/randomTests/owen/unionFind/matrixToListConverter/MatrixConverterTest.java b/src/test/java/randomTests/owen/unionFind/matrixToListConverter/MatrixConverterTest.java deleted file mode 100644 index 1ed3750f..00000000 --- a/src/test/java/randomTests/owen/unionFind/matrixToListConverter/MatrixConverterTest.java +++ /dev/null @@ -1,76 +0,0 @@ -package randomTests.owen.unionFind.matrixToListConverter; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.junit.Test; - -import algorithms.util.matrixToListConverter; -import algorithms.util.matrixToListConverter.InvalidMatrixException; - -/** - * Test cases for {@link matrixToListConverter}. - */ -public class MatrixConverterTest { - @Test - public void test_convertArray_shouldReturnAccurateRepresentation() { - // empty matrix - int[][] firstMatrix = new int[0][0]; - ArrayList[] firstList = new ArrayList[0]; - List[] firstResult = matrixToListConverter.convert(firstMatrix); - - // symmetric matrix (undirected graph) - int[][] secondMatrix = { - {0, 1, 1}, - {1, 0, 1}, - {1, 1, 0}, - }; - ArrayList[] secondList = new ArrayList[3]; - for (int i = 0; i < 3; i++) { - secondList[i] = new ArrayList<>(); - } - Collections.addAll(secondList[0], 1, 2); - Collections.addAll(secondList[1], 0, 2); - Collections.addAll(secondList[2], 0, 1); - List[] secondResult = matrixToListConverter.convert(secondMatrix); - - //asymmetric matrix (directed graph) - int[][] thirdMatrix = { - {0, 0, 1}, - {1, 0, 0}, - {1, 1, 1}, - }; - ArrayList[] thirdList = new ArrayList[3]; - for (int i = 0; i < 3; i++) { - thirdList[i] = new ArrayList<>(); - } - Collections.addAll(thirdList[0], 2); - Collections.addAll(thirdList[1], 0); - Collections.addAll(thirdList[2], 0, 1, 2); - List[] thirdResult = matrixToListConverter.convert(thirdMatrix); - - //invalid matrix (n x m graph, n != m) - int[][] fourthMatrix = { - {0, 0}, - {1, 0}, - {1, 1}, - }; - List[] fourthList = new ArrayList[3]; - - assert Arrays.equals(firstList, firstResult); - for (int i = 0; i < 1; i++) { - assert secondList[i].equals(secondResult[i]); - } - for (int i = 0; i < 1; i++) { - assert thirdList[i].equals(thirdResult[i]); - } - try { - matrixToListConverter.convert(fourthMatrix); - } catch (InvalidMatrixException error) { - assert error.getMessage() == "The matrix must be N x N, not N x M!"; - } - - } -} diff --git a/src/test/java/randomTests/owen/unionFind/quickFind/generalised/Test.java b/src/test/java/randomTests/owen/unionFind/quickFind/generalised/Test.java deleted file mode 100644 index bd308f89..00000000 --- a/src/test/java/randomTests/owen/unionFind/quickFind/generalised/Test.java +++ /dev/null @@ -1,68 +0,0 @@ -package randomTests.owen.unionFind.quickFind.generalised; - -import java.util.ArrayList; -import java.util.List; - -import dataStructures.disjointSet.quickFind.generalised.QuickFind; - -/** - * Some simple tests. - */ -public class Test { - /** - * Runs custom test script. - * - * @param args unused. - */ - public static void main(String[] args) { - List lst = new ArrayList<>(); - lst.add("Dog"); - lst.add("Egg"); - lst.add("Carabiner"); - lst.add("Cat"); - lst.add("Potatoes"); - lst.add("Hamster"); - lst.add("Rope"); - lst.add("Fox"); - lst.add("Chalk"); - for (int i = 0; i < lst.size(); i++) { - System.out.println("Added " + lst.get(i)); - } - - QuickFind qf = new QuickFind<>(lst); - qf.union("Dog", "Cat"); - qf.union("Fox", "Hamster"); - qf.union("Fox", "Cat"); - - qf.union("Carabiner", "Rope"); - qf.union("Chalk", "Rope"); - - qf.union("Egg", "Potatoes"); - - List comp1 = qf.retrieveComponent("Hamster"); - System.out.print("Comp1: "); - for (int i = 0; i < comp1.size(); i++) { - System.out.print(comp1.get(i) + ", "); - } - - List comp2 = qf.retrieveComponent("Chalk"); - System.out.print("\nComp2: "); - for (int i = 0; i < comp2.size(); i++) { - System.out.print(comp2.get(i) + ", "); - } - - List comp3 = qf.retrieveComponent("Egg"); - System.out.print("\nComp3: "); - for (int i = 0; i < comp3.size(); i++) { - System.out.print(comp3.get(i) + ", "); - } - - qf.add("Coconut"); - qf.union("Egg", "Coconut"); - List update = qf.retrieveComponent("Egg"); - System.out.print("\nUpdate Comp3: "); - for (int i = 0; i < update.size(); i++) { - System.out.print(update.get(i) + ", "); - } - } -} diff --git a/src/test/java/randomTests/owen/unionFind/quickFind/simplified/Test.java b/src/test/java/randomTests/owen/unionFind/quickFind/simplified/Test.java deleted file mode 100644 index aec59351..00000000 --- a/src/test/java/randomTests/owen/unionFind/quickFind/simplified/Test.java +++ /dev/null @@ -1,43 +0,0 @@ -package randomTests.owen.unionFind.quickFind.simplified; - -import java.util.List; - -import dataStructures.disjointSet.quickFind.simplified.QuickFind; - -/** - * Basic testing. - */ -public class Test { - /** - * Runs custom test script. - * - * @param args unused. - */ - public static void main(String[] args) { - QuickFind qf = new QuickFind(10); - - qf.union(1, 9); - qf.union(4, 6); - qf.union(1, 4); - - qf.union(2, 8); - qf.union(5, 7); - qf.union(2, 7); - - List comp1 = qf.retrieveComponent(1); - for (int i = 0; i < comp1.size(); i++) { - System.out.print(String.format("%d, ", comp1.get(i))); - } - System.out.println("\nTesting next component:"); - List comp2 = qf.retrieveComponent(2); - for (int i = 0; i < comp2.size(); i++) { - System.out.print(String.format("%d, ", comp2.get(i))); - } - System.out.println("\nTesting next component:"); - List comp3 = qf.retrieveComponent(3); - for (int i = 0; i < comp3.size(); i++) { - System.out.print(String.format("%d, ", comp3.get(i))); - } - System.out.println(); - } -} diff --git a/src/test/java/randomTests/owen/unionFind/weightedUnion/Test.java b/src/test/java/randomTests/owen/unionFind/weightedUnion/Test.java deleted file mode 100644 index 5b8af374..00000000 --- a/src/test/java/randomTests/owen/unionFind/weightedUnion/Test.java +++ /dev/null @@ -1,50 +0,0 @@ -package randomTests.owen.unionFind.weightedUnion; - -import java.util.ArrayList; -import java.util.List; - -import dataStructures.disjointSet.weightedUnion.Union; - -/** - * Some simple tests. - */ -public class Test { - /** - * Runs the custom test. - * - * @param args unused. - */ - public static void main(String[] args) { - List lst = new ArrayList<>(); - lst.add("Dog"); - lst.add("Egg"); - lst.add("Carabiner"); - lst.add("Cat"); - lst.add("Potatoes"); - lst.add("Hamster"); - lst.add("Rope"); - lst.add("Fox"); - lst.add("Chalk"); - for (int i = 0; i < lst.size(); i++) { - System.out.println("Added " + lst.get(i)); - } - - Union wu = new Union<>(lst); - wu.combine("Dog", "Cat"); - wu.combine("Fox", "Hamster"); - wu.combine("Fox", "Cat"); - System.out.println(wu.isSameComponent("Dog", "Fox")); // true - System.out.println(wu.isSameComponent("Rope", "Fox")); // false - - wu.combine("Carabiner", "Rope"); - wu.combine("Chalk", "Rope"); - System.out.println(wu.isSameComponent("Rope", "Carabiner")); // true - - wu.combine("Egg", "Potatoes"); - System.out.println(wu.isSameComponent("Egg", "pot")); // duuhh.. + false - - wu.add("Coconut"); - wu.combine("Egg", "Coconut"); - System.out.println(wu.isSameComponent("Coconut", "Potatoes")); // true - } -}