indices) {
42 | StringBuilder prettySeq = new StringBuilder(" Sequence: ");
43 | StringBuilder prettyPtr = new StringBuilder("Start Pts: ");
44 | int currPos = 0;
45 | for (int i = 0; i < seq.length(); i++) {
46 | char curr = seq.charAt(i);
47 | prettySeq.append(String.format("%c ", curr));
48 |
49 | if (currPos < indices.size()) {
50 | int currIdx = indices.get(currPos);
51 | if (currIdx == i) {
52 | prettyPtr.append("^ ");
53 | currPos++;
54 | } else {
55 | prettyPtr.append(" ");
56 | }
57 | }
58 | }
59 | System.out.println(" Pattern: " + pattern);
60 | System.out.println(prettySeq);
61 | System.out.println(prettyPtr);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/scripts/algorithms/sorting/countingSort/RunCountingSort.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.countingSort;
2 |
3 | /**
4 | * Script to run Counting Sort.
5 | */
6 | public class RunCountingSort {
7 |
8 | //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
9 | ////////////////////////////////////////// This section is for user input \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
10 | private static int[] toSort =
11 | 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};
12 |
13 | //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
14 |
15 | public static void main(String[] args) {
16 | int[] sorted = CountingSort.sort(toSort);
17 | display(sorted);
18 | }
19 |
20 | /**
21 | * Prints the string representation of the array.
22 | *
23 | * @param arr the given array.
24 | */
25 | public static void display(int[] arr) {
26 | StringBuilder toDisplay = new StringBuilder("[");
27 | for (int num : arr) {
28 | toDisplay.append(String.format("%d ", num));
29 | }
30 | toDisplay = toDisplay.replace(toDisplay.length() - 1, toDisplay.length(), "]");
31 | System.out.println(toDisplay);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/scripts/algorithms/sorting/insertionSort/RunInsertionSort.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.insertionSort;
2 |
3 | /**
4 | * Script to run Insertion Sort.
5 | */
6 | public class RunInsertionSort {
7 |
8 | //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
9 | ////////////////////////////////////////// This section is for user input \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
10 | private static int[] toSort =
11 | 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};
12 |
13 | //\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//
14 |
15 | public static void main(String[] args) {
16 | int[] sorted = InsertionSort.sort(toSort);
17 | display(sorted);
18 | }
19 |
20 | /**
21 | * Prints the string representation of the array.
22 | *
23 | * @param arr the given array.
24 | */
25 | public static void display(int[] arr) {
26 | StringBuilder toDisplay = new StringBuilder("[");
27 | for (int num : arr) {
28 | toDisplay.append(String.format("%d ", num));
29 | }
30 | toDisplay = toDisplay.replace(toDisplay.length() - 1, toDisplay.length(), "]");
31 | System.out.println(toDisplay);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | /*
2 | * This file was generated by the Gradle 'init' task.
3 | *
4 | * The settings file is used to specify which projects to include in your build.
5 | * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.3/userguide/building_swift_projects.html in the Gradle documentation.
6 | */
7 |
8 | rootProject.name = 'data-structures-and-algorithms'
9 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/binarySearch/README.md:
--------------------------------------------------------------------------------
1 | # Binary Search
2 |
3 | ## Background
4 |
5 | Binary search is a search algorithm that finds the position of a target value within a sorted array or list. It compares
6 | the target value to the middle element of the search range, then, based on the comparison, narrows the search to the
7 | upper or lower half of the current search range.
8 |
9 | ## Implementation Invariant
10 |
11 | At the end of each iteration, the target value is either within the search range or does not exist in the search space.
12 |
13 | ## BinarySearch and BinarySearchTemplate
14 |
15 | We will discuss more implementation-specific details and complexity analysis in the respective folders. In short,
16 | 1. The [binarySearch](binarySearch) method is a more straightforward and intuitive version of the binary search
17 | algorithm.
18 | 2. The [binarySearchTemplate](binarySearchTemplated) method provides a more generalised template that can be used for
19 | most binary search problems by introducing a condition method that can be modified based on the requirements of the
20 | implementation.
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/binarySearch/binarySearch/BinarySearch.java:
--------------------------------------------------------------------------------
1 | package algorithms.binarySearch.binarySearch;
2 |
3 | /**
4 | * Here, we are implementing BinarySearch where we search an array for a target value at O(log n) time complexity.
5 | *
6 | * Assumptions:
7 | * The array is sorted in ascending order.
8 | * All elements in the array are unique. (to allow for easy testing)
9 | *
10 | * Brief Description and Implementation Invariant:
11 | *
12 | * Brief Description:
13 | * With the assumption that the array is sorted in ascending order, BinarySearch reduces the search range by half or
14 | * half + 1 (due to floor division) after every loop. This is done by reassigning the max (high) or min (low) of the
15 | * search range to the middle of the search range when the target value is smaller than or larger than the current
16 | * middle value respectively, and continuing the search thereafter.
17 | *
18 | * In both scenarios where the target is not equal to arr[mid], the high and low pointers are reassigned mid decremented
19 | * /incremented by 1. This ensures that there will never be an infinite loop as the search range will no longer include
20 | * the mid-value, causing the search range to constantly "shrink". We know we can decrement/increment mid by 1 during
21 | * the reassignment as the mid-value is definitely not the target and should no longer be in the search range.
22 | *
23 | * At the end of every loop, we know that the target value is either within the search range or does not exist in the
24 | * array, thereby ensuring the correctness of the algorithm.
25 | *
26 | * Since after each iteration, the search range is halved, it will only take a maximum of O(log n) times before the
27 | * target is either found or determined to not exist in the array.
28 | */
29 | public class BinarySearch {
30 | /**
31 | * Searches for a target value within a sorted array using the binary search algorithm.
32 | *
33 | * @param arr the sorted array in which the search is to be performed.
34 | * @param target the value to be searched for.
35 | * @return the index of the target if found, otherwise -1.
36 | */
37 | public static int search(int[] arr, int target) {
38 | int high = arr.length - 1; // max index is 3 if array length is 4
39 | int low = 0;
40 | while (low <= high) { // When low == high, arr[low] can still == target, therefore should still check
41 | int mid = low + (high - low) / 2; // equivalent to high + low / 2 but reduces cases of integer overflow
42 | if (arr[mid] > target) {
43 | high = mid - 1; // -1 since current mid is not == target and should not be in the search range anymore
44 | } else if (arr[mid] < target) {
45 | low = mid + 1; // +1 since current mid is not == target and should not be in the search range anymore
46 | } else {
47 | return mid;
48 | }
49 | }
50 |
51 | return -1;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/binarySearch/binarySearch/README.md:
--------------------------------------------------------------------------------
1 | # BinarySearch
2 |
3 | ## Background
4 |
5 | BinarySearch is a more straightforward and intuitive version of the binary search algorithm. In this approach, after the
6 | mid-value is calculated, the high or low pointer is adjusted by just one unit.
7 |
8 | ### Illustration
9 |
10 | 
11 |
12 | Image Source: GeeksforGeeks
13 |
14 | From the above example, after mid points to index 4 in the first search, the low pointer moves to index 5 (+1 from 4)
15 | when narrowing the search. Similarly, when mid points to index 7 in the second search, the high pointer shifts to index
16 | 6 (-1 from 7) when narrowing the search. This prevents any possibility of infinite loops. During the search, the moment
17 | mid-value is equal to the target value, the search ends prematurely.
18 |
19 | ## Complexity Analysis
20 | **Time**:
21 | - Worst case: O(log n)
22 | - Average case: O(log n)
23 | - Best case: O(1)
24 |
25 | In the worst case, the target is either in the first or last index or does not exist in the array at all.
26 | In the best case, the target is the middle (odd number of element) or the first middle element (even number of elements)
27 | if floor division is used to determine the middle.
28 |
29 | **Space**: O(1)
30 |
31 | Since no new data structures are used and searching is only done within the array given.
--------------------------------------------------------------------------------
/src/main/java/algorithms/binarySearch/binarySearchTemplated/BinarySearchTemplated.java:
--------------------------------------------------------------------------------
1 | package algorithms.binarySearch.binarySearchTemplated;
2 |
3 | /**
4 | * Here, we are implementing BinarySearchTemplated where we search an array for a target value at O(log n) time
5 | * complexity.
6 | *
7 | * Assumptions:
8 | * The array is sorted in ascending order.
9 | * All elements in the array are unique. (to allow for easy testing)
10 | *
11 | * Brief Description and Implementation Invariant:
12 | * With the assumption that the array is sorted in ascending order, BinarySearchTemplated reduces the search range by
13 | * half or half + 1 (due to floor division) after every loop. This is done by reassigning the max (high) or min (low) of
14 | * the search range to the middle of the search range when the target value is smaller than or larger than the current
15 | * middle value respectively, and continuing the search thereafter.
16 | *
17 | * In this version, there is no condition to check if the current mid is equal to the target to prematurely end the
18 | * search. Hence, the only way for the loop to complete is when low exceeds high.
19 | *
20 | * At the end of every loop, we know that the target value is either within the search range or does not exist in the
21 | * array, thereby ensuring the correctness of the algorithm.
22 | *
23 | * Since after each iteration, the search range is halved, it will only take a maximum of O(log n) times before the
24 | * target is either found or determined to not exist in the array.
25 | */
26 | public class BinarySearchTemplated {
27 | /**
28 | * A utility method to compare two integers.
29 | *
30 | * @param value The current value from the array.
31 | * @param target The value being searched for.
32 | * @return true if the current value is less than the target, otherwise false.
33 | */
34 | // The condition should be changed accordingly
35 | public static boolean condition(int value, int target) {
36 | return value >= target;
37 | }
38 |
39 | /**
40 | * Conducts a binary search to find the target within the sorted array.
41 | *
42 | * @param arr The sorted input array.
43 | * @param target The value to be searched within the array.
44 | * @return The index of the target value if found, otherwise -1.
45 | */
46 | public static int search(int[] arr, int target) {
47 | // The search space i.e. high and low should be changed accordingly.
48 | int high = arr.length - 1; // max index is 3 if array length is 4
49 | int low = 0;
50 | while (low < high) {
51 | int mid = low + (high - low) / 2; // equivalent to high + low / 2 but reduces cases of integer overflow
52 | if (condition(arr[mid], target)) { // if value >= target
53 | high = mid;
54 | } else { // if value < target
55 | low = mid + 1;
56 | }
57 | }
58 |
59 | // Checks if low value is indeed the target
60 | // Note that the following checks may not be required in other use cases of this template
61 | if (low < arr.length && arr[low] == target) {
62 | // returned value should be changed accordingly (low or low - 1)
63 | return low;
64 | }
65 | // Returns -1 if loop was exited without finding the target
66 | return -1;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/minimumSpanningTree/README.md:
--------------------------------------------------------------------------------
1 | # Minimum Spanning Tree Algorithms
2 |
3 | ## Background
4 |
5 | Minimum Spanning Tree (MST) algorithms are used to find the minimum spanning tree of a weighted, connected graph. A
6 | spanning tree of a graph is a connected, acyclic subgraph that includes all the vertices of the original graph. An MST
7 | is a spanning tree with the minimum possible total edge weight.
8 |
9 | ### 4 Properties of MST
10 | 1. An MST should not have any cycles
11 | 2. If you cut an MST at any single edge, the two pieces will also be MSTs
12 | 3. **Cycle Property:** For every cycle, the maximum weight edge is not in the MST
13 |
14 | 
15 |
16 | Image Source: CS2040S 22/23 Sem 2 Lecture Slides
17 |
18 | 4. **Cut Property:** For every partition of the nodes, the minimum weight edge across the cut is in the MST
19 |
20 | 
21 |
22 | Image Source: CS2040S 22/23 Sem 2 Lecture Slides
23 |
24 | Note that the other edges across the partition may or may not be in the MST.
25 |
26 | ## Prim's Algorithm and Kruskal's Algorithm
27 |
28 | We will discuss more implementation-specific details and complexity analysis in the respective folders. In short,
29 | 1. [Prim's Algorithm](prim) is a greedy algorithm that finds the minimum spanning tree of a graph by starting from an
30 | arbitrary node (vertex) and adding the edge with the minimum weight that connects the current tree to a new node, adding
31 | the node to the current tree, until all nodes are included in the tree.
32 | <<<<<<< HEAD
33 | 2. [Kruskal's Algorithm](kruskal) is a greedy algorithm that finds the minimum spanning tree of a graph by sorting the
34 | edges by weight and adding the edge with the minimum weight that does not form a cycle into the current tree.
35 |
36 | ## Notes
37 |
38 | ### Difference between Minimum Spanning Tree and Shortest Path
39 | It is important to note that a Minimum Spanning Tree of a graph does not represent the shortest path between all the
40 | nodes. See below for an example:
41 |
42 | The below graph is a weighted, connected graph with 5 nodes and 6 edges:
43 | 
44 |
45 | The following is the Minimum Spanning Tree of the above graph:
46 | 
47 |
48 | Taking node A and D into consideration, the shortest path between them is A -> D, with a total weight of 4.
49 | 
50 |
51 | However, the shortest path between A and D in the Minimum Spanning Tree is A -> C -> D, with a total weight of 5, which
52 | is not the shortest path in the original graph.
53 | 
54 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/minimumSpanningTree/kruskal/README.md:
--------------------------------------------------------------------------------
1 | # Kruskal's Algorithm
2 |
3 | ## Background
4 | Kruskal's Algorithm is a greedy algorithm used to find the minimum spanning tree (MST) of a connected, weighted graph.
5 | It works by sorting all the edges in the graph by their weight in non-decreasing order and then adding the smallest edge
6 | to the MST, provided it does not form a cycle with the already included edges. This is repeated until all vertices are
7 | included in the MST.
8 |
9 | ## Implementation Details
10 | Kruskal's Algorithm uses a simple `ArrayList` to sort the edges by weight.
11 |
12 | A [`DisjointSet`](/dataStructures/disjointSet/weightedUnion) data structure is also used to keep track of the
13 | connectivity of vertices and detect cycles.
14 |
15 | ## Complexity Analysis
16 |
17 | **Time Complexity:**
18 | Sorting the edges by weight: O(E log E) = O(E log V), where V and E is the number of vertices and edges respectively.
19 | Union-Find operations: O(E α(V)), where α is the inverse Ackermann function.
20 | Overall complexity: O(E log V)
21 |
22 | **Space Complexity:**
23 | O(V + E) for the storage of vertices in the disjoint set and edges in the priority queue.
--------------------------------------------------------------------------------
/src/main/java/algorithms/minimumSpanningTree/prim/README.md:
--------------------------------------------------------------------------------
1 | # Prim's Algorithm
2 |
3 | ## Background
4 |
5 | Prim's Algorithm is a greedy algorithm that finds the minimum spanning tree of a graph by starting from an
6 | arbitrary node (vertex) and adding the edge, with the minimum weight that connects the current tree to an unexplored
7 | node, and the unexplored node to the current tree, until all nodes are included in the tree.
8 |
9 | ### Implementation Details
10 |
11 | A `PriorityQueue` (binary heap) is utilised to keep track of the minimum weight edge that connects the current tree to
12 | an unexplored node. In an ideal scenario, the minimum weight edge to each node in the priority queue should be updated each
13 | time a lighter edge is found to maintain a single unique node in the priority queue. This means that a decrease key
14 | operation is required.
15 |
16 | **Decrease Key Operation:**
17 |
18 | However, we know that the decrease key operation of a binary heap implementation of a priority
19 | queue will take O(V) time, which will result in a larger time complexity for the entire algorithm compared to using only
20 | O(log V) operations for each edge. Hence, in our implementation, to avoid the use of a decrease key operation, we will simply insert duplicate nodes with
21 | their new minimum weight edge, which will take O(log E) = O(log V) given an upper bound of E = V^2, into the queue,
22 | while leaving the old node in the queue. Additionally, we will track if a node has already been added into the MST to
23 | avoid adding duplicate nodes.
24 |
25 | **Priority Queue Implementation:**
26 |
27 | Note that a priority queue is an abstract data type that can be implemented using different data structures. In this
28 | implementation, the default Java `PriorityQueue` is used, which is a binary heap. By implementing the priority queue
29 | with an AVL tree, a decrease key operation that has a time complexity of O(log V) can also be achieved.
30 |
31 | ## Complexity Analysis
32 |
33 | **Time Complexity:**
34 | - O(V^2 log V) for the basic version with an adjacency matrix, where V is the number of vertices.
35 | - O(E log V) with a binary heap and adjacency list, where V and E is the number of vertices and edges
36 | respectively.
37 |
38 | **Space Complexity:**
39 | - O(V^2) for the adjacency matrix representation.
40 | - O(V + E) for the adjacency list representation.
41 |
42 | ## Notes
43 |
44 | ### Difference between Prim's Algorithm and Dijkstra's Algorithm
45 |
46 | | | Prim's Algorithm | Dijkstra's Algorithm |
47 | |-------------------------------------|---------------------------------------------------------------------------------|----------------------------------------------------------|
48 | | Purpose | Finds MST - minimum sum of edge weights that includes all vertices in the graph | Finds shortest path from a single source to all vertices |
49 | | Property Compared in Priority Queue | Minimum weight of incoming edge to a vertex | Minimum distance from source vertex to current vertex |
50 |
51 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/orthogonalRangeSearching/RangeTreeNode.java:
--------------------------------------------------------------------------------
1 | package algorithms.orthogonalRangeSearching;
2 |
3 | /**
4 | * This class is the node class for building a range tree.
5 | * @param Generic type of the node class
6 | */
7 | public class RangeTreeNode {
8 | private T val;
9 | private int height;
10 | private RangeTreeNode left = null;
11 | private RangeTreeNode right = null;
12 | private RangeTreeNode parent = null;
13 | private RangeTreeNode yTree = null;
14 |
15 | public RangeTreeNode(T val) {
16 | this.val = val;
17 | }
18 |
19 | /**
20 | * Constructor for range tree node
21 | * @param val value of node
22 | * @param left left child of node
23 | * @param right right child of node
24 | */
25 | public RangeTreeNode(T val, RangeTreeNode left, RangeTreeNode right) {
26 | this.val = val;
27 | this.left = left;
28 | this.right = right;
29 | }
30 |
31 | public T getVal() {
32 | return this.val;
33 | }
34 |
35 | public int getHeight() {
36 | return this.height;
37 | }
38 |
39 | public RangeTreeNode getLeft() {
40 | return this.left;
41 | }
42 |
43 | public RangeTreeNode getRight() {
44 | return this.right;
45 | }
46 |
47 | public RangeTreeNode getParent() {
48 | return this.parent;
49 | }
50 |
51 | public RangeTreeNode getYTree() {
52 | return this.yTree;
53 | }
54 |
55 | public void setVal(T val) {
56 | this.val = val;
57 | }
58 |
59 | public void setLeft(RangeTreeNode left) {
60 | this.left = left;
61 | }
62 |
63 | public void setRight(RangeTreeNode right) {
64 | this.right = right;
65 | }
66 |
67 | public void setParent(RangeTreeNode parent) {
68 | this.parent = parent;
69 | }
70 |
71 | public void setHeight(int height) {
72 | this.height = height;
73 | }
74 |
75 | public void setYTree(RangeTreeNode yTree) {
76 | this.yTree = yTree;
77 | }
78 |
79 | @Override
80 | public boolean equals(Object other) {
81 | if (other == this) {
82 | return true;
83 | }
84 | if (!(other instanceof RangeTreeNode)) {
85 | return false;
86 | }
87 | RangeTreeNode node = (RangeTreeNode) other;
88 | return this.val == node.val;
89 | }
90 |
91 | @Override
92 | public String toString() {
93 | return String.valueOf(this.val);
94 | }
95 |
96 | }
97 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/orthogonalRangeSearching/oneDim/README.md:
--------------------------------------------------------------------------------
1 | # 1D Orthogonal Range Searching
2 |
3 | 1D orthogonal range searching is a computational problem where you search for elements or data points within a
4 | specified 1D range (interval) in a collection of 1D data (e.g. Find me everyone between ages 22 and 27). Additionally,
5 | we also want to support efficient insertions of new data points into the maintained set.
6 |
7 | One strategy would be to sort all the data points in O(nlogn) time, then insertion would take O(n). We can binary
8 | search the low and high of the specified range to return the start and end indices of all the data points within
9 | in O(logn) time. This would be a reasonable approach if the no. of queries >> no. of insertions.
10 |
11 | In cases where the no. of insertions >> no. of queries, we might want to further optimise the time complexity of
12 | insertions to O(logn) using a 1D range tree.
13 |
14 | Strategy:
15 | 1. Use a binary search tree
16 | 2. Store all points in the leaves of the tree (internal nodes only store copies)
17 | 3. Each internal node v stores the MAX of any leaf in the left sub-tree (**range tree property**)
18 |
19 | 
20 |
21 | Say we want to find all the nodes between 10 and 50 i.e. query(10, 50). We would want to:
22 | 1. Find split node: highest node where search includes both left & right subtrees
23 | => we want to make use of the range tree property to perform binary search to find our split node. See findSplit(root, low, high) in code.
24 | 2. Left traversal & right traversal
25 | - Left traversal covers the range within [low, splitNode]
26 | - Right traversal covers the range within (splitNode, high]
27 |
28 | 
29 | Image Source: Lecture Slides
30 |
31 | ## Complexity Analysis
32 | **Time**:
33 |
34 | Build Tree (cost incurred once only):
35 | - if given an unsorted array, O(nlogn) limited by sorting step
36 | - if given a sorted array, O(n)
37 |
38 | Querying: O(k + logn)
39 | - Find Split Node: O(logn) (binary search)
40 | - Left Traversal: at every step, we either
41 | 1. output all-right subtree (O(k) where k is no. of leaves) and recurse left
42 | 2. recurse right (at most logn times)
43 | - Right Traversal: similar to left traversal
44 |
45 | **Space**: S(n) = S(n / 2) + O(n) => O(nlogn)
46 |
47 | ## Notes
48 | ### Dynamic Updates
49 | If we need to dynamically update the tree, insertion and deletion is done in a manner similar to AVL trees
50 | (insert/delete and rotate to maintain height balance), except now we need to ensure that we are adding the new node
51 | as a leaf node, and we still need to adhere to the range tree property.
52 |
53 | Note how the ORS tree property enables efficient dynamic updating of the tree, as the value of the nodes do not need
54 | to change after rotation.
55 |
56 | 
57 |
58 | For more implementation details, refer to the code below "// Functions from here onwards are designed to support
59 | dynamic updates."
60 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/orthogonalRangeSearching/twoDim/README.md:
--------------------------------------------------------------------------------
1 | # 2D Orthogonal Range Searching
2 |
3 | 2D orthogonal range searching is a computational problem that involves efficiently answering queries about points
4 | in a two-dimensional space that fall within a specified axis-aligned rectangular range (orthogonal range). In other
5 | words, given a set of points in 2D, find me all the points that lie within a specific rectangle.
6 |
7 | To do so, we can extend the idea of 1D range trees to the 2D context.
8 |
9 | Strategy
10 | - Each node in the x-tree has a set of points in its subtree
11 | - Store a y-tree at each x-node containing all the points in the x-subtree
12 |
13 | 
14 |
15 | 1. Build an x-tree using only x-coordinates.
16 | - This should be done in the exact same way as you would for a 1D range tree.
17 | 2. For every node in the x-tree, build a y-tree out of nodes in the x-subtree using only y-coordinates.
18 |
19 | 
20 |
21 | Given the 2D range tree, we now want to query the points in a given rectangle
22 | i.e. search(tree, xLow, xHigh, yLow, yHigh).
23 |
24 | We first want to find the points that will satisfy the x-condition i.e. find me all points whose x-coordinates are
25 | between xLow and xHigh. To do so, we first need to find our split node in the x-tree, by performing binary search[^1]
26 | while traversing the x-tree - similar to how we found the split node in a 1D range tree. This will give us our X-split.
27 |
28 | Now given our X-split, we want to find points that satisfy both our x-condition and y-condition.
29 |
30 | Let's first explore our x-left subtree (X-left traversal). If (xLow <= x-coordinate of curr node), we are confident
31 | that the entire right subtree of the curr node satisfies our x-condition (because of BST property).
32 |
33 | Now we want to perform a Y-search to find the points that also satisfy the y-condition. To do so, we first need to
34 | find our split node in the y-tree i.e. Y-split. Notice that the problem is now reduced to a 1D ORS problem. Once we
35 | have found Y-split, we can simply perform left traversal and right traversal from Y-split, similar to how it is done
36 | in 1D ORS.
37 |
38 | A similar symmetric logic applies in exploring our x-right subtree.
39 |
40 | 
41 | Image Source: https://www.cse.wustl.edu/~taoju/cse546/lectures/Lecture21_rangequery_2d.pdf
42 |
43 | ## Complexity Analysis
44 | **Time**:
45 |
46 | Build Tree (cost incurred once only): O(nlog^2n)
47 |
48 | Querying: O(k + log^2n)
49 | - O(logn) to find X-split
50 | - O(logn) recursing steps in X-left traversal/X-right-traversal
51 | - O(logn) y-tree searches of cost O(logn) => overall: O(log^2n)
52 | - O(k) enumerating output
53 |
54 | **Space**: O(nlogn)
55 | - Each point appears in at most 1 y-tree per level. (Refer to 1st image for visualisation)
56 | - There are O(logn) levels in the x-tree.
57 | - Therefore, each node appears in at most O(logn) y-trees. => overall: O(nlogn)
58 | - The x-tree takes O(n) space.
59 |
60 | ## Notes
61 | ### Dynamic Updates
62 | Unlike 1D range trees, dynamic updates in a 2D range tree are inefficient as rotations in the x-tree might involve
63 | entirely rebuilding the y-trees for the rotated notes. Therefore, 2D ORS is mainly used for static querying.
64 |
65 | ### d-dimensional Range Trees
66 | - Query cost: O(k + log^dn)
67 | - Build tree cost: O(nlog^(d-1)n)
68 | - Space: O(nlog^(d-1)n)
69 |
70 | [^1] This reference to binary search differs from our typical binary search formulation, but essentially refers
71 | to the findSplit function where you walk down the balanced tree and remove roughly half of the nodes each time.
--------------------------------------------------------------------------------
/src/main/java/algorithms/patternFinding/README.md:
--------------------------------------------------------------------------------
1 | # Knuth-Moris-Pratt Algorithm
2 |
3 | ## Background
4 | KMP match is a type of pattern-searching algorithm that improves the efficiency of naive search by avoiding unnecessary
5 | comparisons. It is most notable when the pattern has repeating sub-patterns.
6 |
7 | Pattern-searching problems is prevalent across many fields of CS, for instance,
8 | in text editors when searching for a pattern, in computational biology sequence matching problems,
9 | in NLP problems, and even for looking for file patterns for effective file management.
10 | It is hence crucial that we develop an efficient algorithm.
11 |
12 | Typically, the algorithm returns a list of indices that denote the start of each occurrence of the pattern string.
13 |
14 | 
15 | Image Source: GeeksforGeeks
16 |
17 | ### Intuition
18 | It's efficient because it utilizes the information gained from previous character comparisons. When a mismatch occurs,
19 | the algorithm uses this information to skip over as many characters as possible.
20 |
21 | Considering the string pattern:
22 |
23 | Pattern:| X | Y | X | Y | C | X | Y | X | Y | F |
24 | |-------|---|---|---|---|---|---|---|---|---|---|
25 | Position:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10|
26 |
27 | and string:
28 |
29 | String: | X | Y | X | Y | C | X | Y | X | Y | C | X | Y | X | Y | F | G | A | B | C |
30 | |--------|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
31 | Position:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11| 12| 13| 14| 15| 16| 17| 18| 19|
32 |
33 | KMP has, during its initial processing of the pattern, identified that "XYXY" is a repeating sub-pattern.
34 | This means when the mismatch at F (10th character of the pattern) and C (10th character of the string) occurs,
35 | KMP doesn't need to start matching again from the very beginning of the pattern.
36 | Instead, it leverages the information that "XYXY" has already been matched.
37 |
38 | Therefore, the algorithm continues matching from the 5th character of the pattern string.
39 |
40 | This is the key idea behind KMP algorithm and is applied throughout the string.
41 | How it knows to 'reuse' previously identified sub-patterns is by keeping
42 | track of what is known as the Longest Prefix Suffix (LPS; Longest prefix that is also a suffix) Table. Look at the
43 | implementation for a step-by-step explanation for how LPS table is generated.
44 |
45 | Pattern: | | X | Y | X | Y | C | X | Y | X | Y | F |
46 | |--------|---|---|---|---|---|---|---|---|---|---|---|
47 | Position:| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10|
48 | LPS: | -1| 0 | 0 | 1 | 2 | 0 | 1 | 2 | 3 | 4 | 0 |
49 |
50 | ## Complexity Analysis
51 | Let k be the length of the pattern and n be the length of the string to match against.
52 |
53 | Naively, we can look for patterns in a given sequence in O(nk) where n is the length of the sequence and k
54 | is the length of the pattern. We do this by iterating every character of the sequence, and look at the
55 | immediate k-1 characters that come after it. This is not a big issue if k is known to be small, but there's
56 | no guarantee this is the case.
57 |
58 | **Time complexity**: O(n+k)
59 |
60 | KMP does this in O(n+k) by making use of previously identified sub-patterns. It identifies sub-patterns
61 | by first processing the pattern input in O(k) time, allowing identification of patterns in
62 | O(n) traversal of the sequence. More details found in the src code.
63 |
64 | **Space complexity**: O(k) auxiliary space to store suffix that matches with prefix of the pattern string
65 |
66 | ## Notes
67 | 1. A detailed illustration of how the algorithm works is shown in the code.
68 | But if you have trouble understanding the implementation,
69 | here is a good [video](https://www.youtube.com/watch?v=EL4ZbRF587g) as well.
70 | 2. A subroutine to find Longest Prefix Suffix (LPS) is commonly involved in the preprocessing step of KMP.
71 | It may be useful to interpret these numbers as the number of characters matched between the suffix and prefix.
72 | Knowing the number of characters of prefix would help in informing the position of the next character of the pattern to
73 | match.
74 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/bubbleSort/BubbleSort.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.bubbleSort;
2 |
3 | /**
4 | * Here, we are implementing BubbleSort where we sort the array in increasing (or more precisely, non-decreasing)
5 | * order. Below are some details specific to this implementation.
6 | *
7 | * Early Termination:
8 | * 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)
9 | * elements are correctly sorted at the final (n-1) positions of the array, leaving the last 1 element placed correctly
10 | * in the first position of the array. Therefore, (n-1) iterations of the outer loop is sufficient.
11 | *
12 | * Slight optimisation:
13 | * At the kth iteration of the outer loop, we only require (n-k) adjacent comparisons to get the kth-largest element
14 | * to its correct position.
15 | */
16 | public class BubbleSort {
17 | /**
18 | * Sorts the given array in-place in non-decreasing order.
19 | *
20 | * @param arr array to be sorted.
21 | * @return the same array arr that is sorted.
22 | */
23 | public static int[] sort(int[] arr) {
24 | int n = arr.length;
25 | boolean swapped; // tracks of the presence of swaps within one iteration of the outer loop to
26 | // facilitate early termination
27 | for (int i = 0; i < n - 1; i++) { // outer loop which supports the invariant; n-1 suffice
28 | swapped = false;
29 | for (int j = 0; j < n - 1 - i; j++) { // inner loop that does the adjacent comparisons
30 | if (arr[j] > arr[j + 1]) { // if we changed this to <, we will sort the array in non-increasing order
31 | int temp = arr[j];
32 | arr[j] = arr[j + 1];
33 | arr[j + 1] = temp;
34 | swapped = true;
35 | }
36 | }
37 | if (!swapped) {
38 | break;
39 | }
40 | }
41 | return arr;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/bubbleSort/README.md:
--------------------------------------------------------------------------------
1 | # Bubble Sort
2 |
3 | ## Background
4 |
5 | Bubble sort is one of the more intuitive comparison-based sorting algorithms.
6 | It makes repeated comparisons between neighbouring elements, 'bubbling' (side-by-side swaps)
7 | largest (or smallest) element in the unsorted region to the sorted region (often the front or the back).
8 |
9 | 
10 |
11 | ### Implementation Invariant
12 | **After the kth iteration, the biggest k items are correctly sorted at the final k positions of the array**.
13 |
14 | The job of the kth iteration of the outer loop is to bubble the kth-largest element to the kth position of the array
15 | from the right (i.e. its correct position).
16 | This is done through repeatedly comparing adjacent elements and swapping them if they are in the wrong order.
17 |
18 | ## Complexity Analysis
19 |
20 | **Time**:
21 |
22 | - Worst case (reverse sorted array): O(n^2)
23 | - Average case: O(n^2)
24 | - Best case (sorted array): O(n)
25 |
26 | In the worst case, during each iteration of the outer loop, the number of adjacent comparisons is upper-bounded
27 | by n. Since BubbleSort requires (n-1) iterations of the outer loop to sort the entire array, the total number
28 | of comparisons performed can be upper-bounded by (n-1) * n ≈ n^2.
29 |
30 | This implementation of BubbleSort terminates the outer loop once there are no swaps within one iteration of the
31 | outer loop. This improves the best case time complexity to O(n) for an already sorted array.
32 |
33 | **Space**: O(1) since sorting is done in-place
34 |
35 | ## Notes
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/countingSort/CountingSort.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.countingSort;
2 |
3 | /**
4 | *
Stable implementation of Counting Sort.
5 | * This non-comparison-based sorting algorithm is efficient for sorting integers with a small range.
6 | * For detailed explanation and complexity analysis, see README.
7 | */
8 | public class CountingSort {
9 | /**
10 | * Sorts the given array.
11 | *
12 | * @param arr array to be sorted.
13 | * @return new array that is sorted.
14 | */
15 | public static int[] sort(int[] arr) {
16 | int k = 0;
17 | int n = arr.length;
18 | // Find the max. value in arr. This tells us the size of the freq map array.
19 | for (int i = 0; i < n; i++) {
20 | k = Math.max(k, arr[i]);
21 | }
22 | // Obtain frequency map
23 | int[] freq = new int[k + 1];
24 | for (int num : arr) {
25 | freq[num]++;
26 | }
27 | // Obtain prefix sum of freq map
28 | for (int i = 1; i < k + 1; i++) {
29 | freq[i] += freq[i - 1];
30 | }
31 | // Sort the array by placing element in the output array
32 | int[] sorted = new int[n];
33 | for (int i = arr.length - 1; i >= 0; i--) { // we start from the back to maintain stable property
34 | int num = arr[i];
35 | sorted[freq[num] - 1] = num; // freq[num]-1 because 0-indexed
36 | freq[num]--;
37 | }
38 | return sorted;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/countingSort/README.md:
--------------------------------------------------------------------------------
1 | # Counting Sort
2 |
3 | ## Background
4 |
5 | Counting sort is a non-comparison-based sorting algorithm and isn't bounded by the O(nlogn) lower-bound
6 | of most sorting algorithms.
7 | It first obtains the frequency map of all elements (i.e. counting the occurrence of every element), then
8 | computes the prefix sum for the map. This prefix map tells us which position an element should be inserted.
9 | It is updated after each insertion to reflect the new position to insert the next time the same element is
10 | encountered.
11 |
12 | 
13 |
14 | Image Source: https://www.oreilly.com/library/view/mastering-algorithms-with/1565924533/ch12s13.html
15 |
16 | _To align with the naming convention of our implementation, data => arr, counts => freq, temp => sorted._
17 |
18 | ### Implementation Invariant
19 |
20 | **At the end of the ith iteration, the ith element (of the original array) from the back will be placed in
21 | its correct position**.
22 |
23 | Note: An alternative implementation from the front is easily done with minor modification.
24 | The catch is that this implementation would not be stable.
25 |
26 | ### Common Misconception
27 |
28 | _"Counting sort does not require total ordering of elements since it is non-comparison based."_
29 |
30 | This is incorrect. It requires total ordering of elements to determine their relative positions in the sorted output.
31 | In our implementation, the total ordering property is reflected by virtue of the structure of the frequency map.
32 |
33 | ## Complexity Analysis
34 |
35 | **Time**: O(k+n)=O(max(k,n))
36 | **Space**: O(k+n)=O(max(k,n))
37 | where k is the value of the largest element and n is the number of elements.
38 |
39 | Counting sort is most efficient if the range of input values do not exceed the number of input values.
40 | Counting sort is NOT AN IN-PLACE algorithm. For one, it requires additional space to store freq map.
41 |
42 | ## Notes
43 |
44 | 1. Our counting sort implementation works only on non-negative integers. However, to adapt it to work for arrays with
45 | negative integers, we can add an offset of +m, where m is the smallest integer in the array.
46 | 2. Counting sort (stable version) is often used as a sub-routine for radix sort.
47 | 3. Supplementary: Here is a [video](https://www.youtube.com/watch?v=OKd534EWcdk) if you are still having troubles.
48 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/cyclicSort/README.md:
--------------------------------------------------------------------------------
1 | # Cyclic Sort
2 |
3 | ## Background
4 |
5 | Cyclic sort is a comparison-based, in-place algorithm that performs sorting (generally) in O(n^2) time.
6 | Under some special conditions (discussed later), the algorithm is non-comparison based and
7 | the best case could be done in O(n) time. This is the version that tends to be used in practice.
8 |
9 | ### Implementation Invariant
10 |
11 | **At the end of the kth iteration, the smallest (largest) i items are correctly sorted
12 | in the first (last) i positions of the array**.
13 |
14 | ### Comparison to Selection Sort
15 |
16 | Its invariant is quite similar as selection sort's. But they differ in maintaining this invariant.
17 | The algorithm for cyclic sort does a bit more than selection sort's.
18 | In the process of trying to find the correct element for the ith position, any element that was
19 | encountered will be correctly placed in their positions as well.
20 |
21 | This allows cyclic sort to have a time complexity of O(n) for certain inputs.
22 | (where the relative ordering of the elements is already known). This is discussed in the [**simple**](./simple) case.
23 |
24 | ## Simple and Generalised Case
25 |
26 | We discuss more implementation-specific details and complexity analysis in the respective folders. In short,
27 |
28 | 1. The [**simple**](./simple) case discusses the **non-comparison based** implementation of cyclic sort under
29 | certain conditions. This allows the best case to be better than O(n^2).
30 | 2. The [**generalised**](./generalised) case discusses cyclic sort for general inputs. This is comparison-based and is
31 | typically implemented in O(n^2).
32 |
33 | Note that, in practice, the generalised case is hardly used. There are more efficient algorithms to use for sorting,
34 | e.g. merge and quick sort. If the concern is the number of swaps, generalized cyclic sort does indeed require fewer
35 | swaps, but likely won't lower than selection sort's.
36 |
37 | In other words, **cyclic sort is specially designed for situations where the elements to be sorted are
38 | known to fall within a specific, continuous range, such as integers from 1 to n, without any gaps or duplicates.**
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/cyclicSort/generalised/CyclicSort.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.cyclicSort.generalised;
2 | /**
3 | * Implementation of cyclic sort in the generalised case where the input can contain any integer and duplicates.
4 | */
5 | public class CyclicSort {
6 | /**
7 | * Sorts the given array using CyclicSort. Generalised case where the input array can contain any integer, and
8 | * can contain duplicates.
9 | * See documentation in README for more details.
10 | *
11 | * @param arr the given array to be sorted. Can contain any integers and can contain duplicates.
12 | * @param n the size of the given array to be sorted.
13 | */
14 | public static void cyclicSort(int[] arr, int n) {
15 | for (int currIdx = 0; currIdx < n - 1; currIdx++) {
16 | int currElement;
17 | int rightfulPos;
18 |
19 | do {
20 | rightfulPos = currIdx; // initialization since elements before currIdx are correctly placed
21 | currElement = arr[currIdx];
22 | for (int i = currIdx + 1; i < n; i++) { // O(n) find rightfulPos for the currElement
23 | if (arr[i] < currElement) {
24 | rightfulPos++;
25 | }
26 | }
27 | if (rightfulPos == currIdx) { // verified curr position is correct for curr element
28 | break;
29 | }
30 | while (currElement == arr[rightfulPos]) { // duplicates found, so find next suitable position
31 | rightfulPos++;
32 | }
33 | int tmp = currElement; // swap, put item in its rightful position
34 | arr[currIdx] = arr[rightfulPos];
35 | arr[rightfulPos] = tmp;
36 | } while (rightfulPos != currIdx);
37 | }
38 | }
39 |
40 | /**
41 | * Overloaded helper method that calls internal implementation.
42 | * @param arr the input array to be sorted.
43 | */
44 | public static void sort(int[] arr) {
45 | cyclicSort(arr, arr.length);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/cyclicSort/generalised/README.md:
--------------------------------------------------------------------------------
1 | # Generalized Case
2 |
3 | ## Background
4 |
5 | Implementation of cyclic sort in the generalised case where the input can contain any integer and duplicates.
6 |
7 | This is a comparison-based algorithm. In short, for the element encountered at the ith position of the original array,
8 | the algorithm does a O(n) traversal to search for its rightful position and does a swap. It repeats this until a swap
9 | placing the correct element at the ith position, before moving onto the next (i+1)th.
10 |
11 | ### Illustration
12 |
13 | Result: 6 10 6 5 7 4 2 1
14 | Read: ^ 6 should be placed at index 4, so we do a swap with 6 and 7,
15 | Result: 7 10 6 5 6 4 2 1
16 | Read: ^ 7 should be placed at index 6, so we do a swap. Note that index 5 should be taken up by dup 6
17 | Result: 2 10 6 5 6 4 7 1
18 | Read: ^ 2 should be placed at index 1, so swap.
19 | Result: 10 2 6 5 6 4 7 1
20 | Read: ^ 10 is the largest, should be placed at last index, so swap.
21 | Result: 1 2 6 5 6 4 7 10
22 | Read: ^ Correctly placed, so move on. Same for 2.
23 | Read:
24 | ^ 6 should be placed at index 4. But index 4 already has a 6. So place at index 5 and so on.
25 | Result: 1 2 4 5 6 6 7 10
26 | Read:
27 | ^ ^ ^ ^ ^ Continue with O(n) verification of correct position at each iteration
28 |
29 | ## Complexity Analysis
30 |
31 | **Time**:
32 |
33 | - Best: O(n^2) even if the ith element is encountered in the ith position, a O(n) traversal validation check is needed
34 | - Worst: O(n^2) since we need O(n) time to find / validate the correct position of an element and
35 | the total number of O(n) traversals is bounded by O(n).
36 | - Average: O(n^2), it's bounded by the above two
37 |
38 | **Space**: O(1) auxiliary space, this is an in-place algorithm
39 |
40 | ## Notes
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/cyclicSort/simple/CyclicSort.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.cyclicSort.simple;
2 |
3 | /**
4 | * Implementation of cyclic sort in the simple case where the n elements of the given array are contiguous,
5 | * but not in sorted order. Below illustrates the idea using integers from 0 to n-1.
6 | */
7 | public class CyclicSort {
8 | /**
9 | * Sorts the given array.
10 | *
11 | * @param arr the array to be sorted.
12 | */
13 | public static void sort(int[] arr) {
14 | int curr = 0; // can be easily modified to work on n numbers starting at some other number
15 | while (curr < arr.length) { // iterate until the end of the array
16 | int ele = arr[curr]; // encounter an element that may not be in its correct position
17 | assert ele >= 0 && ele < arr.length : "Input array should only have integers from 0 to n-1 (inclusive)";
18 | if (ele != curr) { // verified that it is indeed not the correct element to be placed at curr position
19 | int tmp = arr[ele]; // go to the correct position of ele
20 | arr[ele] = ele; // do a swap
21 | arr[curr] = tmp; // note that curr isn't incremented because we haven't yet placed the correct element
22 | } else {
23 | curr += 1; // found the correct element to be placed at curr, which in this eg, is itself, so, increment
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/cyclicSort/simple/FindFirstMissingNonNegative.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.cyclicSort.simple;
2 |
3 | /**
4 | * Cyclic sort algorithm can be easily modified to find first missing non-negative integer (i.e. starting from 0)
5 | * in O(n).
6 | */
7 | public class FindFirstMissingNonNegative {
8 | /**
9 | * Finds the first missing non-negative integer in the array.
10 | *
11 | * @param arr the given array.
12 | * @return the first missing, non-negative integer.
13 | */
14 | public static int findMissing(int[] arr) {
15 | int curr = 0;
16 | while (curr < arr.length) {
17 | int ele = arr[curr];
18 | if (ele >= 0 && ele < arr.length && ele != curr) { // if ele (still) needs to be placed in its correct pos
19 | if (arr[ele] == ele) { // the correct position of ele already has ele (i.e. duplicates), so just move on
20 | curr += 1;
21 | continue;
22 | }
23 | int tmp = arr[ele]; // do the swap and place ele at its right position first
24 | arr[ele] = ele;
25 | arr[curr] = tmp;
26 | } else {
27 | curr += 1; // either found the correct element, or a number out of range to ignore first.
28 | }
29 | }
30 |
31 | for (int i = 0; i < arr.length; i++) { // iterate to look for the missing number which will be out of place.
32 | if (arr[i] != i) {
33 | return i; // this is the missing non-negative element!
34 | }
35 | }
36 | return arr.length; // 0 to n-1 integers are all present. First missing non-negative element is n.
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/cyclicSort/simple/README.md:
--------------------------------------------------------------------------------
1 | # Simple Case
2 |
3 | ## Background
4 |
5 | Cyclic Sort can achieve O(n) time complexity in cases where the elements of the collection have a known,
6 | continuous range, and there exists a direct, O(1) time-complexity mapping from each element to its respective index in
7 | the sorted order.
8 |
9 | This is typically applicable when sorting a sequence of integers that are in a consecutive range
10 | or can be easily mapped to such a range. We illustrate the idea with n integers from 0 to n-1.
11 |
12 | In this implementation, the algorithm is **not comparison-based**! (unlike the general case).
13 | It makes use of the known inherent ordering of the numbers,
14 | bypassing the `nlogn` lower bound for most sorting algorithms.
15 |
16 |
17 | Duplicates
18 | Not designed to hande duplicates. When duplicates are present, the algorithm can run into issues,
19 | such as overwriting elements or getting stuck in infinite loops,
20 | because it assumes that each element has a unique position in the array.
21 |
22 | If you need to handle duplicates, modifications are required,
23 | such as checking for duplicate values before placing elements,
24 | which can impact the simplicity and efficiency (possibly degrade to `O(n^2)`) of the algorithm.
25 |
26 |
27 |
28 | Inherent Ordering..?
29 | This property allows the sorting algorithm to avoid comparing elements with each other
30 | and instead directly place each element in its correct position.
31 |
32 | For example, if sorting integers from 0 to n-1, the number 0 naturally belongs at index 0, 1 at index 1, and so on.
33 | This inherent structure allows Cyclic Sort to achieve `O(n)` time complexity,
34 | bypassing the typical `O(nlogn)` time bound of comparison-based sorting algorithms
35 | ([proof](https://tildesites.bowdoin.edu/~ltoma/teaching/cs231/fall07/Lectures/sortLB.pdf))
36 | by using the known order of elements rather than making comparisons to determine their positions.
37 |
38 |
39 | ## Complexity Analysis
40 |
41 | **Time**:
42 |
43 | - Best: O(n) since the array has to be traversed
44 | - Worst: O(n) since each element is at most seen twice
45 | - Average: O(n), it's bounded by the above two
46 |
47 | **Space**: O(1) auxiliary space, this is an in-place algorithm
48 |
49 | ## Case Study: Find First Missing Non-negative Integer
50 |
51 | Cyclic sort algorithm can be easily modified to find first missing non-negative integer (i.e. starting from 0) in O(n).
52 | The invariant is the same, but for numbers that are out-of-range (negative or greater than n),
53 | simply ignore the number at the position and
54 | move on first. It may be subject to swap later.
55 |
56 | There are other ways of doing so, using a hash set for instance, but what makes cyclic sort stand out is that it is
57 | able to do so in O(1) space. In other words, it is in-place and require no additional space.
58 |
59 | The algorithm does a 2-pass iteration.
60 |
61 | 1. In the 1st iteration, it places elements at its rightful position where possible.
62 | 2. In the 2nd iteration, it will look for the first out of place element (element that is not supposed
63 | to be in that position). The answer will be the index of that position.
64 |
65 | Note that the answer is necessarily between 0 and n (inclusive), where n is the length of the array,
66 | otherwise there would be a contradiction.
67 |
68 | ## Notes
69 |
70 | 1. It may seem quite trivial to sort integers from 0 to n-1 when one could simply generate such a sequence.
71 | But this algorithm is useful in cases where the integers to be sorted are keys to associated values (or some mapping)
72 | and sorting needs to be done in O(1) auxiliary space.
73 | 2. The implementation here uses integers from 0 to n-1. This can be easily modified for n contiguous integers starting
74 | at some arbitrary number (simply offset by this start number).
75 | 3. This version of cyclic sort does not handle duplicates (at least, sorting might not be guaranteed to be in O(n))
76 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/insertionSort/InsertionSort.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.insertionSort;
2 |
3 | /**
4 | * Here, we are implementing InsertionSort where we sort the array in increasing (or more precisely, non-decreasing)
5 | * order.
6 | *
7 | * Note:
8 | * 1. the loop invariant here slightly differs from the lecture slides as we are using 0-based indexing
9 | * 2. Insertion into the sorted portion is done byb 'bubbling' elements as in bubble sort
10 | */
11 |
12 | public class InsertionSort {
13 | /**
14 | * Sorts the given array in-place in non-decreasing order.
15 | *
16 | * @param arr array to be sorted.
17 | * @return the same array arr that is sorted.
18 | */
19 | public static int[] sort(int[] arr) {
20 | int n = arr.length;
21 | for (int i = 1; i < n; i++) { //loop which supports the invariant
22 | arr = insert(arr, i, arr[i]);
23 | }
24 | return arr;
25 | }
26 |
27 | /**
28 | * Inserts val within the sorted portion of the array. The sorted portion of the array is arr[0, idx - 1].
29 | *
30 | * @param arr the array to be sorted (of length idx)
31 | * @param idx index of the element to be inserted into the sorted portion of the array
32 | * @param val value of the element to be inserted into the sorted portion of the array
33 | * @return returns arr with arr[0, idx] in sorted order
34 | */
35 | private static int[] insert(int[] arr, int idx, int val) {
36 | int pointer = idx - 1;
37 |
38 | while (pointer >= 0 && arr[pointer] > val) { //if we change this to arr[pointer] < val,
39 | // we can sort the array in non-increasing order
40 | arr[pointer + 1] = arr[pointer];
41 | pointer -= 1;
42 | }
43 | arr[pointer + 1] = val;
44 |
45 | return arr;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/insertionSort/README.md:
--------------------------------------------------------------------------------
1 | # Insertion Sort
2 |
3 | ## Background
4 |
5 | Insertion sort is a comparison-based sorting algorithm that builds the final sorted array one element at a
6 | time. It works by repeatedly taking an element from the unsorted portion of the array and
7 | inserting it correctly (portion remains sorted) into the sorted portion. Note that the position is not final
8 | since subsequent elements from unsorted portion may displace previously inserted elements. What's important is
9 | the sorted region remains sorted.
10 |
11 | More succinctly, at the kth iteration, we take the element arr[k] and insert
12 | it into arr[0, k-1] following sorted order, returning us arr[0, k] in sorted order.
13 |
14 | 
15 |
16 | ### Implementation Invariant
17 | The loop invariant: **At the end of kth iteration, the first (k+1) items in the array are in sorted order**.
18 |
19 | At the end of the (n-1)th iteration, all n items in the array will be in sorted order.
20 |
21 | ## Complexity Analysis
22 |
23 | **Time**:
24 |
25 | - Worst case (reverse sorted array): O(n^2)
26 | - Average case: O(n^2)
27 | - Best case (sorted array): O(n)
28 |
29 | In the worst case, inserting an element into the sorted array of length m requires us to iterate through the
30 | entire array, requiring O(m) time. Since InsertionSort does this insertion (n - 1) times, the time complexity
31 | of InsertionSort in the worst case is 1 + 2 + ... + (n-2) + (n-1) = O(n^2).
32 |
33 | In the best case of an already sorted array, inserting an element into the sorted array of length m requires
34 | O(1) time as we insert it directly behind the first position of the pointer in the sorted array. Since InsertionSort
35 | does this insertion (n-1) times, the time complexity of InsertionSort in the best case is O(1) * (n-1) = O(n).
36 |
37 | **Space**: O(1) since sorting is done in-place
38 |
39 | ## Notes
40 |
41 | ### Common Misconception
42 |
43 | Its invariant is often confused with selection sort's. In selection sort, an element in the unsorted region will
44 | be immediately placed in its correct and final position as it would be in the sorted array. This is not the case
45 | for insertion sort. However, it is because of this 'looser' invariant that allows for a better best case time complexity
46 | for insertion sort.
47 |
48 | Image Source: https://www.hackerrank.com/challenges/correctness-invariant/problem
49 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/mergeSort/iterative/MergeSort.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.mergeSort.iterative;
2 |
3 | /**
4 | * Here, we are implementing MergeSort where we sort the array in increasing (or more precisely, non-decreasing)
5 | * order iteratively.
6 | */
7 |
8 | public class MergeSort {
9 |
10 | /**
11 | * Sorts the given array in non-decreasing order.
12 | *
13 | * @param arr The given array to be sorted.
14 | */
15 | public static void sort(int[] arr) {
16 | int interval = 1;
17 | int n = arr.length;
18 | int[] temp = new int[n];
19 |
20 | while (interval < n) {
21 | for (int i = 0; i < n - interval; i += 2 * interval) {
22 | int end = Math.min(i + 2 * interval - 1, n - 1);
23 | int mid = i + interval - 1;
24 | merge(arr, i, mid, end, temp);
25 | }
26 | interval *= 2;
27 | }
28 | }
29 |
30 | /**
31 | * Merges two sorted sub-arrays within the given array. The two sub-arrays are arr[start, mid] and
32 | * arr[mid + 1, end]. Upon completion of this function, arr[start, end] will be in sorted order.
33 | *
34 | * @param arr The array containing the sub-arrays to be merged.
35 | * @param start The starting index of the first sub-array to be merged.
36 | * @param mid The ending index (inclusive) of the first sub-array to be merged. In the iterative implementation,
37 | * the mid parameter is required in the merge function to determine the splitting point between the
38 | * sub-arrays to be merged.
39 | * @param end The ending index (inclusive) of the second sub-array to be merged.
40 | * @param temp A temporary array used for merging intermediate results.
41 | */
42 | private static void merge(int[] arr, int start, int mid, int end, int[] temp) {
43 | int i = start;
44 | int j = mid + 1;
45 | int pointer = start;
46 |
47 | // Merge the two sorted sub-arrays into the temp array
48 | while (i <= mid && j <= end) {
49 | if (arr[i] <= arr[j]) {
50 | //we use <= here to maintain stability of MergeSort. If arr[i] == arr[j], the algorithm prefers the
51 | //one from the left sub-array (arr[i]). This decision preserves the relative order of equal elements.
52 |
53 | //if we change this to arr[i] >= arr[j], we can sort the array in non-increasing order.
54 |
55 | temp[pointer] = arr[i];
56 | i++;
57 | } else {
58 | temp[pointer] = arr[j];
59 | j++;
60 | }
61 | pointer++;
62 | }
63 |
64 | // Copy any remaining elements from the left sub-array
65 | while (i <= mid) {
66 | temp[pointer] = arr[i];
67 | i++;
68 | pointer++;
69 | }
70 |
71 | // Copy any remaining elements from the right sub-array
72 | while (j <= end) {
73 | temp[pointer] = arr[j];
74 | j++;
75 | pointer++;
76 | }
77 |
78 | // Copy the merged elements back to the original array
79 | if (end + 1 - start >= 0) {
80 | System.arraycopy(temp, start, arr, start, end + 1 - start);
81 | }
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/mergeSort/iterative/README.md:
--------------------------------------------------------------------------------
1 | # Merge Sort
2 |
3 | ### Background
4 | The iterative implementation of MergeSort takes a bottom-up approach, where the sorting process starts by merging
5 | intervals of size 1. Intervals of size 1 are trivially in sorted order. The algorithm then proceeds to merge
6 | adjacent sorted intervals, doubling the interval size with each merge step, until the entire array is fully sorted.
7 |
8 | 
9 |
10 | Image Source: https://www.chelponline.com/iterative-merge-sort-12243
11 |
12 | ### Implementation Invariant
13 | At each iteration of the merging process, the main array is divided into sub-arrays of a certain interval
14 | size (interval). Each of these sub-arrays is sorted within itself. The last sub-array may not be of size
15 | interval, but it is still sorted since its size is necessarily less than interval.
16 |
17 | ### Complexity Analysis
18 | Time:
19 | - Worst case: O(nlogn)
20 | - Average case: O(nlogn)
21 | - Best case: O(nlogn)
22 |
23 | 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
24 | we have to iterate through every element in both arrays once.
25 |
26 | - At the 1st level of the merge process, our merging subroutine involves n sub-arrays of size 1, taking O(n) time.
27 | - At the 2nd level of the merge process, our merging subroutine involves (n/2) sub-arrays of size 2, taking O(n) time.
28 | - At the kth level of the merge process, our merging subroutine involves (n/(2^(k-1))) sub-arrays of size (2^(k-1)),
29 | taking O(n) time.
30 |
31 | Since interval doubles at every iteration of the merge process, there are logn such levels. Every level takes
32 | O(n) time, hence overall time complexity is n * logn = O(nlogn)
33 |
34 | Regardless of how sorted the input array is, MergeSort carries out the partitioning and merging process, so the
35 | time complexity of MergeSort is O(nlogn) for all cases.
36 |
37 | Space:
38 | - O(n) since we require a temporary array to temporarily store the merged elements in sorted order
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/mergeSort/recursive/MergeSort.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.mergeSort.recursive;
2 |
3 | /**
4 | * Here, we are implementing MergeSort where we sort the array in increasing (or more precisely, non-decreasing)
5 | * order recursively.
6 | */
7 |
8 | public class MergeSort {
9 |
10 | /**
11 | * Sorts the sub-array arr[start, end] using the MergeSort algorithm.
12 | *
13 | * @param arr The given array to be sorted.
14 | * @param start The starting index of the sub-array to be sorted.
15 | * @param end The ending index (inclusive) of the sub-array to be sorted.
16 | * @param temp A temporary array used for merging.
17 | */
18 | private static void mergeSort(int[] arr, int start, int end, int[] temp) {
19 | if (start >= end) {
20 | return;
21 | }
22 | int mid = (start + end) / 2;
23 | mergeSort(arr, start, mid, temp);
24 | mergeSort(arr, mid + 1, end, temp);
25 | merge(arr, start, end, temp);
26 | }
27 |
28 | /**
29 | * Merges two sorted sub-arrays within the given array. The two sub-arrays are arr[start, mid] and
30 | * arr[mid + 1, end]. Upon completion of this function, arr[start, end] will be in sorted order.
31 | *
32 | * @param arr The array containing the sub-arrays to be merged.
33 | * @param start The starting index of the first sub-array to be merged.
34 | * @param end The ending index (inclusive) of the second sub-array to be merged.
35 | * @param temp A temporary array used for merging intermediate results.
36 | */
37 | private static void merge(int[] arr, int start, int end, int[] temp) {
38 | int mid = (start + end) / 2;
39 | int i = start;
40 | int j = mid + 1;
41 | int pointer = start;
42 |
43 | // Merge the two sorted sub-arrays into the temp array
44 | while (i <= mid && j <= end) {
45 | if (arr[i] <= arr[j]) {
46 | //we use <= here to maintain stability of MergeSort. If arr[i] == arr[j], the algorithm prefers the
47 | //one from the left sub-array (arr[i]). This decision preserves the relative order of equal elements.
48 |
49 | //if we change this to arr[i] >= arr[j], we can sort the array in non-increasing order.
50 |
51 | temp[pointer] = arr[i];
52 | i++;
53 | } else {
54 | temp[pointer] = arr[j];
55 | j++;
56 | }
57 | pointer++;
58 | }
59 |
60 | // Copy any remaining elements from the left sub-array
61 | while (i <= mid) {
62 | temp[pointer] = arr[i];
63 | i++;
64 | pointer++;
65 | }
66 |
67 | // Copy any remaining elements from the right sub-array
68 | while (j <= end) {
69 | temp[pointer] = arr[j];
70 | j++;
71 | pointer++;
72 | }
73 |
74 | // Copy the merged elements back to the original array
75 | if (end + 1 - start >= 0) {
76 | System.arraycopy(temp, start, arr, start, end + 1 - start);
77 | }
78 | }
79 |
80 | /**
81 | * Sorts the given array in non-decreasing order.
82 | *
83 | * @param arr The given array to be sorted
84 | */
85 | public static void sort(int[] arr) {
86 | int n = arr.length;
87 | int[] temp = new int[n];
88 | mergeSort(arr, 0, n - 1, temp);
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/mergeSort/recursive/README.md:
--------------------------------------------------------------------------------
1 | # Merge Sort
2 |
3 | ### Background
4 | MergeSort is a divide-and-conquer sorting algorithm. The recursive implementation takes a top-down approach by
5 | recursively dividing the array into two halves, sorting each half separately, and then merging the sorted halves
6 | to produce the final sorted output.
7 |
8 | 
9 |
10 | Image Source: https://www.101computing.net/merge-sort-algorithm/
11 |
12 | ### Implementation Invariant (for the merging subroutine)
13 | The sub-array temp[start, (k-1)] consists of the (𝑘−start) smallest elements of arr[start, mid] and
14 | arr[mid + 1, end], in sorted order.
15 |
16 | ### Complexity Analysis
17 | Time:
18 | - Worst case: O(nlogn)
19 | - Average case: O(nlogn)
20 | - Best case: O(nlogn)
21 | Merging two sorted sub-arrays of size (n/2) requires O(n) time as we need to iterate through every element in both
22 | sub-arrays in order to merge the two sorted sub-arrays into one sorted array.
23 |
24 | Recursion expression: T(n) = 2T(n/2) + O(n) => O(nlogn)
25 |
26 | Regardless of how sorted the input array is, MergeSort carries out the same divide-and-conquer strategy, so the
27 | time complexity of MergeSort is O(nlogn) for all cases.
28 |
29 | Space:
30 | - O(n) since we require a temporary array to temporarily store the merged elements in sorted order
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/quickSort/README.md:
--------------------------------------------------------------------------------
1 | # QuickSort
2 |
3 | This page will provide a high-level overview of the different types of QuickSort and the significance of each
4 | enhancement.
5 |
6 | 
7 |
8 | We start off with Simple QuickSort, which utilises Hoare's / Lomuto's partitioning with a fixed pivot. But the time
9 | complexity of Simple QuickSort degrades to O(n^2) when pivots chosen are continuously bad. This could be because of
10 | inputs such as sorted arrays, where picking the first element as the pivot will always lead to an extremely imbalanced
11 | partitioning. This could also be because of the presence of duplicates, as we now handle duplicates by putting them to
12 | one side of the pivot.
13 |
14 | Therefore, we introduced randomisation in pivot selection to prevent fixed pivot selection from continuously choosing
15 | a bad pivot. We also added a good pivot check (Paranoid) to Simple QuickSort to ensure that the pivots we select are
16 | "good" and will not cause extremely imbalanced partitions. However, the presence of the good pivot checks will cause
17 | infinite recursion when the array contains many duplicates, since any pivot you choose in the array will fail the good
18 | pivot check.
19 |
20 | So, we introduced 3-way partitioning to handle duplicates by partitioning the array into three sections: elements less
21 | than the pivot, elements equal to the pivot, and elements greater than the pivot. Now the good pivot check ignores the
22 | size of the segment that comprises elements = to pivot.
23 |
24 | ## Recommended Order of Reading
25 | 1. [Hoares](./hoares)
26 | 2. [Lomuto](./lomuto)
27 | 3. [Paranoid](./paranoid)
28 | 4. [3-way partitioning](./threeWayPartitioning)
29 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/quickSort/hoares/QuickSort.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.quickSort.hoares;
2 |
3 | /**
4 | * Here, we are implementing Hoares's QuickSort where we sort the array in increasing (or more precisely,
5 | * non-decreasing) order. We will follow lecture implementation here, which differs slightly from the usual
6 | * implementation of Hoare's. See more under notes in README.
7 | *
8 | * Implementation Invariant:
9 | * The pivot is in the correct position, with elements to its left being <= it, and elements to its right being > it.
10 | * (We edited the pseudocode a bit to keep the duplicates to the left of the pivot.)
11 | *
12 | * This implementation picks the first element as the pivot.
13 | */
14 |
15 | public class QuickSort {
16 | /**
17 | * Sorts the given array in-place in non-decreasing order.
18 | *
19 | * @param arr array to be sorted.
20 | */
21 | public static void sort(int[] arr) {
22 | int n = arr.length;
23 | quickSort(arr, 0, n - 1);
24 | }
25 |
26 | /**
27 | * Recursively sorts the sub-array from index 'start' to index 'end' in non-decreasing order
28 | * using the QuickSort algorithm.
29 | *
30 | * @param arr the array containing the sub-array to be sorted.
31 | * @param start the starting index (inclusive) of the sub-array to be sorted.
32 | * @param end the ending index (inclusive) of the sub-array to be sorted.
33 | */
34 | private static void quickSort(int[] arr, int start, int end) {
35 | if (start < end) {
36 | int pIdx = partition(arr, start, end);
37 | quickSort(arr, start, pIdx - 1);
38 | quickSort(arr, pIdx + 1, end);
39 | }
40 | }
41 |
42 | /**
43 | * Partitions the sub-array from index 'start' to index 'end' around a randomly selected pivot element.
44 | * The elements less than or equal to the pivot are placed on the left side, and the elements greater than
45 | * the pivot are placed on the right side.
46 | *
47 | * Given a sub-array of length m, the time complexity of the partition subroutine is O(m) as we need to iterate
48 | * through every element in the sub-array once.
49 | *
50 | * @param arr the array containing the sub-array to be partitioned.
51 | * @param start the starting index (inclusive) of the sub-array to be partitioned.
52 | * @param end the ending index (inclusive) of the sub-array to be partitioned.
53 | * @return the index of the pivot element in its correct position after partitioning.
54 | */
55 | private static int partition(int[] arr, int start, int end) {
56 | int pivot = arr[start];
57 | int low = start;
58 | int high = end + 1;
59 |
60 | while (low < high) {
61 | do {
62 | low++;
63 | } while (low < high && arr[low] <= pivot); // we use <= as opposed to < to pack duplicates to the left side
64 | // of the pivot
65 | do {
66 | high--;
67 | } while (low < high && arr[high] > pivot);
68 |
69 | if (low < high) {
70 | swap(arr, low, high);
71 | }
72 | }
73 | swap(arr, start, low - 1);
74 |
75 | return low - 1;
76 | }
77 |
78 | /**
79 | * Swaps the elements at indices 'i' and 'j' in the given array.
80 | *
81 | * @param arr the array in which the elements should be swapped.
82 | * @param i the index of the first element to be swapped.
83 | * @param j the index of the second element to be swapped.
84 | */
85 | private static void swap(int[] arr, int i, int j) {
86 | int temp = arr[i];
87 | arr[i] = arr[j];
88 | arr[j] = temp;
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/quickSort/lomuto/QuickSort.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.quickSort.lomuto;
2 |
3 | /**
4 | * Here, we are implementing Lomuto's QuickSort where we sort the array in increasing (or more precisely,
5 | * non-decreasing) order.
6 | */
7 |
8 | public class QuickSort {
9 | /**
10 | * Sorts the given array in-place in non-decreasing order.
11 | *
12 | * @param arr array to be sorted.
13 | */
14 | public static void sort(int[] arr) {
15 | int n = arr.length;
16 | quickSort(arr, 0, n - 1);
17 | }
18 |
19 | /**
20 | * Recursively sorts the sub-array from index 'start' to index 'end' in non-decreasing order
21 | * using the QuickSort algorithm.
22 | *
23 | * @param arr the array containing the sub-array to be sorted.
24 | * @param start the starting index (inclusive) of the sub-array to be sorted.
25 | * @param end the ending index (inclusive) of the sub-array to be sorted.
26 | */
27 | private static void quickSort(int[] arr, int start, int end) {
28 | if (start < end) {
29 | int pIdx = partition(arr, start, end);
30 | quickSort(arr, start, pIdx - 1);
31 | quickSort(arr, pIdx + 1, end);
32 | }
33 | }
34 |
35 | /**
36 | * Partitions the sub-array from index 'start' to index 'end' around a randomly selected pivot element.
37 | * The elements less than or equal to the pivot are placed on the left side, and the elements greater than
38 | * the pivot are placed on the right side.
39 | *
40 | * Given a sub-array of length m, the time complexity of the partition subroutine is O(m) as we need to iterate
41 | * through every element in the sub-array once.
42 | *
43 | * @param arr the array containing the sub-array to be partitioned.
44 | * @param start the starting index (inclusive) of the sub-array to be partitioned.
45 | * @param end the ending index (inclusive) of the sub-array to be partitioned.
46 | * @return the index of the pivot element in its correct position after partitioning.
47 | */
48 | private static int partition(int[] arr, int start, int end) {
49 | int pIdx = random(start, end);
50 | int pivot = arr[pIdx];
51 |
52 | swap(arr, start, pIdx); // swap the pivot to the start of the array
53 |
54 | int idx = start + 1; // interpret: at the end, all elements at indices less than this var is <= pivot
55 |
56 | for (int i = start + 1; i <= end; i++) {
57 | if (arr[i] <= pivot) {
58 | swap(arr, idx, i);
59 | idx++;
60 | }
61 | }
62 |
63 | swap(arr, idx - 1, start); // swap the pivot to its correct position
64 |
65 | return idx - 1;
66 | }
67 |
68 | /**
69 | * Swaps the elements at indices 'i' and 'j' in the given array.
70 | *
71 | * @param arr the array in which the elements should be swapped.
72 | * @param i the index of the first element to be swapped.
73 | * @param j the index of the second element to be swapped.
74 | */
75 | private static void swap(int[] arr, int i, int j) {
76 | int temp = arr[i];
77 | arr[i] = arr[j];
78 | arr[j] = temp;
79 | }
80 |
81 | /**
82 | * Generates a random integer within the range [start, end].
83 | *
84 | * @param start the lower bound of the random integer (inclusive).
85 | * @param end the upper bound of the random integer (inclusive).
86 | * @return a random integer within the specified range.
87 | */
88 | private static int random(int start, int end) {
89 | return (int) (Math.random() * (end - start + 1)) + start;
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/quickSort/lomuto/README.md:
--------------------------------------------------------------------------------
1 | # Lomuto's QuickSort
2 |
3 | ## Background
4 | QuickSort is a divide-and-conquer sorting algorithm. The basic idea behind Quicksort is to choose a pivot element,
5 | places it in its correct position in the sorted array, and then recursively sorts the sub-arrays on either side of
6 | the pivot. When we introduce randomization in pivot selection, every element has equal probability of being
7 | selected as the pivot. This means the chance of an extreme element getting chosen as the pivot is decreased, so we
8 | reduce the probability of encountering the worst-case scenario of imbalanced partitioning.
9 |
10 | This is how QuickSort works if we always pick the first element as the pivot with Lomuto's partitioning.
11 |
12 | 
13 |
14 | Image Source: https://www.geeksforgeeks.org/implement-quicksort-with-first-element-as-pivot/
15 |
16 | If we use randomised pivot selection, the idea is very similar to the above implementation. All we
17 | need to do is to swap the random pivot to the first element in the array, then partition as per usual,
18 | then swap the pivot back to its correct position. Below is an illustration:
19 |
20 | 
21 |
22 | ## Implementation Invariant
23 | The pivot is in the correct position, with elements to its left being <= it, and elements to its right being > it.
24 |
25 | ## Complexity Analysis:
26 | * The complexity analysis for Lomuto's quicksort is the same as that of Hoare's quicksort.
27 | Time:
28 | - Expected worst case (poor choice of pivot): O(n^2)
29 | - Expected average case: O(nlogn)
30 | - Expected best case (balanced pivot): O(nlogn)
31 |
32 | In the best case of a balanced pivot, the partitioning process divides the array in half, which leads to log n
33 | levels of recursion. Given a sub-array of length m, the time complexity of the partition subroutine is O(m) as we
34 | need to iterate through every element in the sub-array once.
35 | Therefore, the recurrence relation is: T(n) = 2T(n/2) + O(n) => O(nlogn).
36 |
37 | Even in the average case where the chosen pivot partitions the array by a fraction, there will still be log n levels
38 | of recursion. (e.g. T(n) = T(n/10) + T(9n/10) + O(n) => O(nlogn))
39 |
40 | 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,
41 | 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
42 | duplicates in the array leads to extremely unbalanced partitioning, leading to a O(n^2) time complexity.
43 |
44 | Space:
45 | - O(1) excluding memory allocated to the call stack, since partitioning is done in-place
46 |
47 | ## Notes
48 | ### Lomuto's vs Hoare's QuickSort
49 |
50 | Lomuto's partition scheme is in contrast to Hoare's partition scheme. Hoare's uses two pointers, while Lomuto's uses
51 | one. Hoare's partition scheme is generally more efficient as it requires less swaps. See more at
52 | https://www.geeksforgeeks.org/hoares-vs-lomuto-partition-scheme-quicksort/.
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/quickSort/paranoid/README.md:
--------------------------------------------------------------------------------
1 | # Paranoid QuickSort
2 |
3 | ## Background
4 | Paranoid Quicksort is the naive quicksort with that allow additional attempts to guarantee a good pivot selection.
5 |
6 | 
7 |
8 | ## Complexity Analysis:
9 | Time: (this analysis assumes the absence of many duplicates in our array)
10 | - Expected worst case: O(nlogn)
11 | - Expected average case: O(nlogn)
12 | - Expected best case: O(nlogn)
13 |
14 | The additional check to guarantee a good pivot guards against the worst case scenario where the chosen pivot results
15 | in an extremely imbalanced partitioning. Since the chosen pivot has to at least partition the array into a
16 | 1/10, 9/10 split, the recurrence relation will be: T(n) = T(n/10) + T(9n/10) + n(# iterations of pivot selection).
17 |
18 | The number of iterations of pivot selection is expected to be <2 (more precisely, 1.25). This is because
19 | P(good pivot) = 8/10. Expected number of tries to get a good pivot = 1 / P(good pivot) = 10/8 = 1.25.
20 |
21 | Therefore, the expected time-complexity is: T(n) = T(n/10) + T(9n/10) + 1.25n => O(nlogn).
22 |
23 | - Edge case: does not terminate
24 | The presence of this additional check and repeating pivot selection means that if we have an array of
25 | length n >= 10 containing all/many duplicates of the same number, any pivot we pick will be a bad pivot and we will
26 | enter an infinite loop of repeating pivot selection.
27 |
28 | Space:
29 | - O(1) excluding memory allocated to the call stack, since partitioning is done in-place
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/quickSort/threeWayPartitioning/README.md:
--------------------------------------------------------------------------------
1 | # Three-Way Partitioning
2 |
3 | ## Background
4 | Three-way partitioning is an improved partitioning scheme, used in QuickSort, to tackle the scenario where there are
5 | many duplicate elements. This partitioning scheme will resolve the infinite loop error possibly faced by
6 | Paranoid Quicksort.
7 |
8 | The idea behind three-way partitioning is to divide the array into three sections: elements less than the pivot,
9 | elements equal to the pivot, and elements greater than the pivot. By doing so, we can avoid unnecessary comparisons
10 | and swaps with duplicate elements, making the sorting process more efficient.
11 |
12 | Note that during the partitioning process, there would be a 4th region - 'In Progress' region that will hold elements
13 | that haven't yet been placed in the right section (see below).
14 |
15 | 
16 |
17 | ## Implementation Invariant:
18 | The pivot and any element numerically equal to the pivot will be in the correct positions in the array. Elements
19 | to their left are < them and elements to their right are > than them.
20 |
21 | ## Complexity Analysis:
22 | Time:
23 | - Worst case: O(nlogn)
24 | - Average case: O(nlogn)
25 | - Best case: O(nlogn)
26 |
27 | Space:
28 | - O(1) excluding memory allocated to the call stack, since partitioning is done in-place
29 |
30 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/radixSort/RadixSort.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.radixSort;
2 |
3 | /**
4 | * This class implements a Radix Sort Algorithm.
5 | */
6 | public class RadixSort {
7 |
8 | private static final int NUM_BITS = 8;
9 | private static final int NUM_SEGMENTS = 4;
10 |
11 | /**
12 | * Creates masking on the segment to obtain the value of the digit.
13 | *
14 | * @param num The number.
15 | * @param segment The segment we are interested in.
16 | * @return The value of the digit in the number at the given segment.
17 | */
18 | private static int getSegmentMasked(int num, int segment) {
19 | // bit masking here to extract each segment from the integer.
20 | int mask = (1 << NUM_BITS) - 1;
21 | return (num >> (segment * NUM_BITS)) & mask; // we do a right-shift on num to focus on the desired segment
22 | }
23 |
24 | /**
25 | * Radix sorts a given input array and updates the output array in-place.
26 | *
27 | * @param arr original input array.
28 | * @param sorted output array.
29 | */
30 | private static void radixSort(int[] arr, int[] sorted) {
31 | // Code in the loop is essentially counting sort; sort the N numbers by segments, starting from right-most
32 | for (int i = 0; i < NUM_SEGMENTS; i++) {
33 | int[] freqMap = new int[1 << NUM_BITS]; // at most this number of elements
34 |
35 | // count each element
36 | for (int num : arr) {
37 | freqMap[getSegmentMasked(num, i)]++;
38 | }
39 | // get prefix sum
40 | for (int j = 1; j < freqMap.length; j++) {
41 | freqMap[j] += freqMap[j - 1];
42 | }
43 | // place each number in its correct sorted position up until the given segment
44 | for (int k = arr.length - 1; k >= 0; k--) {
45 | int curr = arr[k];
46 | int id = getSegmentMasked(curr, i);
47 | sorted[freqMap[id] - 1] = curr;
48 | freqMap[id]--;
49 | }
50 | // We do a swap so that our results above for this segment is
51 | // saved and passed as input to the next segment.
52 | // By doing this we no longer need to create a new array
53 | // every time we shift to a new segment to sort.
54 | // We can constantly reuse the array, allowing us to only use O(n) space.
55 | int[] tmp = arr;
56 | arr = sorted;
57 | sorted = tmp;
58 | }
59 | sorted = arr;
60 | }
61 |
62 | /**
63 | * Calls radix sort inplace on a given array.
64 | *
65 | * @param arr The array to be sorted.
66 | */
67 | public static void radixSort(int[] arr) {
68 | int[] sorted = new int[arr.length];
69 | radixSort(arr, sorted);
70 | arr = sorted; // swap back lol
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/selectionSort/README.md:
--------------------------------------------------------------------------------
1 | # Selection Sort
2 |
3 | ## Background
4 |
5 | Selection sort is another intuitive comparison-based sorting algorithm. It works similarly to other sorting algorithms
6 | like bubble and insertion in the sense that it maintains a sorted and unsorted region. It does so by repeatedly finding
7 | smallest (or largest) element in the unsorted region, and places the element in the correct and final position as it
8 | would be in the sorted array.
9 |
10 | 
11 |
12 | ### Implementation Invariant
13 | Let the array of length n to be sorted be A.
14 |
15 | The loop invariant is:
16 | **At the end of the kth iteration, the smallest k items are correctly sorted in the first k positions of the array**.
17 |
18 | 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)
19 | positions of the array, leaving the last item correctly positioned in the last index of the array. Therefore,
20 | (n-1) iterations of the loop is sufficient.
21 |
22 | ## Complexity Analysis
23 |
24 | **Time**:
25 |
26 | - Worst case: O(n^2)
27 | - Average case: O(n^2)
28 | - Best case: O(n^2)
29 |
30 | Regardless of how sorted the input array is, selectionSort will run the minimum element finding algorithm (n-1)
31 | times. For an input array of length m, finding the minimum element necessarily takes O(m) time. Therefore, the
32 | time complexity of selectionSort is n + (n-1) + (n-2) + ... + 2 = O(n^2)
33 |
34 | **Space**: O(1) since sorting is done in-place
35 |
36 | Image Source: https://www.hackerearth.com/practice/algorithms/sorting/selection-sort/tutorial/
37 |
38 | ## Notes
39 | The number of swaps made is always O(n) (careful not to confuse this with the number of comparisons).
40 |
--------------------------------------------------------------------------------
/src/main/java/algorithms/sorting/selectionSort/SelectionSort.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.selectionSort;
2 |
3 | /**
4 | * Here, we are implementing SelectionSort where we sort the array in increasing (or more precisely, non-decreasing)
5 | * order.
6 | */
7 |
8 | public class SelectionSort {
9 | /**
10 | * Sorts the given array in-place in non-decreasing order.
11 | *
12 | * @param arr array to be sorted.
13 | * @return the same array arr that is sorted.
14 | */
15 | public static int[] sort(int[] arr) {
16 | int n = arr.length;
17 | for (int i = 0; i < n - 1; i++) { //loop which supports the invariant
18 | int minElemIdx = minElemIdx(i, n, arr);
19 | int temp = arr[i];
20 | arr[i] = arr[minElemIdx];
21 | arr[minElemIdx] = temp;
22 | }
23 | return arr;
24 | }
25 |
26 | /**
27 | * Finds the index of the minimum element within the specified range of the array.
28 | * The range is from 'start' (inclusive) to 'end' (exclusive).
29 | *
30 | * @param start the starting index (inclusive) of the range to be considered.
31 | * @param end the ending index (exclusive) of the range to be considered.
32 | * @param arr the array to be sorted.
33 | * @return the index of the minimum element within the range.
34 | *
35 | * We can easily modify this method to find maxElemIdx instead to sort the array in non-increasing order.
36 | */
37 | private static int minElemIdx(int start, int end, int[] arr) {
38 | int min = Integer.MAX_VALUE;
39 | int idx = -1;
40 | for (int i = start; i < end; i++) {
41 | if (arr[i] < min) {
42 | min = arr[i];
43 | idx = i;
44 | }
45 | }
46 | return idx;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/dataStructures/avlTree/Node.java:
--------------------------------------------------------------------------------
1 | package dataStructures.avlTree;
2 |
3 | /**
4 | * TreeNode implementation for AVL Tree.
5 | * Note: Properties should rightfully be kept private
6 | * and accessed/modified via public getters/setters.
7 | * But it was left as such to avoid clutter.
8 | *
9 | * @param generic type of objects to be stored in the tree; must be comparable
10 | */
11 | public class Node> {
12 | private T key;
13 | private Node left;
14 | private Node right;
15 | private Node parent;
16 | private int height;
17 | /*
18 | * Can insert more properties here for augmentation
19 | * e.g. If key is not unique, introduce a value property as a tie-breaker
20 | * or weight property for order statistics
21 | */
22 |
23 | public Node(T key) {
24 | this.key = key;
25 | }
26 |
27 | public boolean isLeaf() {
28 | return this.left == null && this.right == null;
29 | }
30 |
31 | public T getKey() {
32 | return key;
33 | }
34 |
35 | public void setKey(T key) {
36 | this.key = key;
37 | }
38 |
39 | public Node getLeft() {
40 | return left;
41 | }
42 |
43 | public void setLeft(Node left) {
44 | this.left = left;
45 | }
46 |
47 | public Node getRight() {
48 | return right;
49 | }
50 |
51 | public void setRight(Node right) {
52 | this.right = right;
53 | }
54 |
55 | public Node getParent() {
56 | return parent;
57 | }
58 |
59 | public void setParent(Node parent) {
60 | this.parent = parent;
61 | }
62 |
63 | public int getHeight() {
64 | return height;
65 | }
66 |
67 | public void setHeight(int height) {
68 | this.height = height;
69 | }
70 |
71 | @Override
72 | public String toString() {
73 | return key.toString();
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/dataStructures/binarySearchTree/Node.java:
--------------------------------------------------------------------------------
1 | package dataStructures.binarySearchTree;
2 |
3 | /**
4 | * Node implementation for Binary Search Tree.
5 | * Note: Properties should rightfully be kept private
6 | * and accessed/modified via public getters/setters.
7 | * But it was left as such to avoid clutter.
8 | *
9 | * @param generic type of objects to be stored in the tree; must be comparable
10 | */
11 | public class Node, V> {
12 | private T key;
13 | private Node left;
14 | private Node right;
15 | private Node parent;
16 | private V value;
17 |
18 | /**
19 | * Constructor for a BST node.
20 | * Can insert more properties here.
21 | * If key is not unique, introduce a value property, so when nodes are being compared, a distinction can be made.
22 | * @param key the key for the BST Node.
23 | * @param value the value encapsulated by the BST node.
24 | */
25 | public Node(T key, V value) {
26 | this.key = key;
27 | this.value = value;
28 | }
29 |
30 | /**
31 | * Determines whether the node is a leaf node.
32 | * @return true if the node is a leaf node, false if not.
33 | */
34 | public boolean isLeaf() {
35 | return this.left == null && this.right == null;
36 | }
37 |
38 | public T getKey() {
39 | return key;
40 | }
41 |
42 | public void setKey(T key) {
43 | this.key = key;
44 | }
45 |
46 | public Node getLeft() {
47 | return left;
48 | }
49 |
50 | public void setLeft(Node left) {
51 | this.left = left;
52 | }
53 |
54 | public Node getRight() {
55 | return right;
56 | }
57 |
58 | public void setRight(Node right) {
59 | this.right = right;
60 | }
61 |
62 | public Node getParent() {
63 | return parent;
64 | }
65 |
66 | public void setParent(Node parent) {
67 | this.parent = parent;
68 | }
69 |
70 | public V getValue() {
71 | return value;
72 | }
73 |
74 | public void setValue(V value) {
75 | this.value = value;
76 | }
77 |
78 | @Override
79 | public String toString() {
80 | return "Key: " + key.toString() + ", Value: " + (value == null ? "null" : value.toString());
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/dataStructures/binarySearchTree/README.md:
--------------------------------------------------------------------------------
1 | # Binary Search Tree
2 |
3 | ## Overview
4 |
5 | A Binary Search Tree (BST) is a tree-based data structure in which each node has at most two children, referred to as
6 | the left child and the right child. Each node in a BST contains a unique key and an associated value. The tree is
7 | structured so that, for any given node:
8 |
9 | 1. The left subtree contains nodes with keys less than the node's key.
10 | 2. The right subtree contains nodes with keys greater than the node's key.
11 |
12 | This property makes BSTs efficient for operations like searching, as the average time complexity for many operations is
13 | proportional to the tree's height.
14 |
15 | Note: in the following explanation a "smaller" node refers to a node with a smaller key and a "larger" node refers to a
16 | node with a larger key.
17 |
18 | ## Implementation
19 |
20 | ### BinarySearchTree Class
21 |
22 | The BinarySearchTree class is a generic implementation of a BST. It supports a variety of operations that allow
23 | interaction with the tree:
24 |
25 | - root(): Retrieve the root node of the tree.
26 | - insert(T key, V value): Insert a key-value pair into the tree.
27 | - delete(T key): Remove a key and its associated value from the tree.
28 | - search(T key): Find a node with a specified key.
29 | - predecessor(T key): Find the key of the predecessor of a specified key.
30 | - successor(T key): Find the key of the successor of a specified key.
31 | - searchMin(): Find the node with the minimum key in the tree.
32 | - searchMax(): Find the node with the maximum key in the tree.
33 | - getInorder(): Return an in-order traversal of the tree.
34 | - getPreorder(): Return a pre-order traversal of the tree.
35 | - getPostorder(): Return a post-order traversal of the tree.
36 | - getLevelorder(): Return a level-order traversal of the tree.
37 |
38 | We will expand on the delete implementation due to its relative complexity.
39 |
40 | #### Delete Implementation Details
41 |
42 | The delete operation is split into three different cases - when the node to be deleted has no children, one child or
43 | two children.
44 |
45 | **No children:** Simply delete the node.
46 |
47 | **One child:** Reassign the parent attribute of the child to the parent of the node to be deleted. This will not violate
48 | the binary search tree property as the right child will definitely be smaller than the parent of the deleted node.
49 |
50 | **Two children:** Replace the deleted node with its successor. This works because the binary search tree property is
51 | maintained:
52 |
53 | 1. the entire left subtree will definitely be smaller than the successor as the successor is larger than the deleted
54 | node
55 | 2. the entire right subtree will definitely be larger than the successor as the successor will be the smallest node in
56 | the right subtree
57 |
58 | ### Node
59 |
60 | The Node class represents the nodes within the BinarySearchTree. Each Node instance contains:
61 |
62 | - key: The unique key associated with the node.
63 | - value: The value associated with the key.
64 | - left: Reference to the left child.
65 | - right: Reference to the right child.
66 | - parent: Reference to the parent node.
67 |
68 | ## Complexity Analysis
69 |
70 | **Time Complexity:** For a balanced tree, most operations (insert, delete, search) can be performed in O(log n) time,
71 | except tree traversal operations which can be performed in O(n) time. However, in the worst case (an unbalanced tree),
72 | these operations may degrade to O(n).
73 |
74 | **Space Complexity:** O(n), where n is the number of elements in the tree.
75 |
--------------------------------------------------------------------------------
/src/main/java/dataStructures/disjointSet/README.md:
--------------------------------------------------------------------------------
1 | # Union Find / Disjoint Set
2 |
3 | ## Background
4 |
5 | A disjoint-set structure also known as a union-find or merge-find set, is a data structure that tracks a set of elements
6 | partitioned into a number of disjoint (non-overlapping) subsets. It is commonly used to check for connectivity
7 | (e.g. if two objects are 'grouped' together/belong to some component).
8 |
9 | In CS2040s, this is introduced in the context of checking for dynamic connectivity. For instance, Kruskal's algorithm
10 | in graph theory to find minimum spanning tree of a graph utilizes disjoint set to efficiently
11 | query if there already exists a path between 2 nodes.
12 |
13 | Generally, there are 2 main operations:
14 |
15 | 1. **Union**: Join two subsets into a single subset
16 | 2. **Find**: Determine which subset a particular element is in. In practice, this is often done to check
17 | if two elements are in the same subset or component.
18 |
19 | The Disjoint Set structure is often introduced in 3 parts, with each iteration being better than the
20 | previous either in time or space complexity (or both). More details can be found in the respective folders.
21 | Below is a brief overview:
22 |
23 | 1. **Quick Find** - Elements are assigned a component identity.
24 | Querying for connectivity and updating usually tracked with an internal array.
25 |
26 | 2. **Quick Union** - Component an element belongs to is now tracked with a tree structure. Nothing to enforce
27 | a balanced tree and hence complexity does not necessarily improve
28 | - Note, this is not implemented but details can be found under weighted union folder.
29 |
30 | 3. **Weighted Union** - Same idea of using a tree, but constructed in a way that the tree is balanced, leading to
31 | improved complexities.
32 | - Can be further augmented with path compression.
33 |
34 | ## Applications
35 | Because of its efficiency and simplicity in implementing, Disjoint Set structures are widely used in practice:
36 | 1. As mentioned, it is often used as a helper structure for Kruskal's MST algorithm
37 | 2. It can be used in the context of network connectivity
38 | - Managing a network of computers
39 | - Or even analyse social networks, finding communities and determining if two users are connected through a chain
40 | 3. Can be part of clustering algorithms to group data points based on similarity - useful for ML
41 | 4. It can be used to detect cycles in dependency graphs, e.g, software dependency management systems
42 | 1. Static analysis tools like `mypy` use this to identify circular dependencies!
43 | 5. It can be used for image processing, in labelling different connected components of an image
44 |
45 | ## Notes
46 | Disjoint Set is a data structure designed to keep track of a set of elements partitioned into a number of
47 | non-overlapping subsets. **It is not suited for handling duplicates**, so our implementation ignores duplicates.
48 |
--------------------------------------------------------------------------------
/src/main/java/dataStructures/disjointSet/quickFind/DisjointSet.java:
--------------------------------------------------------------------------------
1 | package dataStructures.disjointSet.quickFind;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | /**
9 | * Implementation of quick-find structure; Turns a list of objects into a data structure that supports union operations
10 | * Note DS structure is not suited with duplicate elements!
11 | *
12 | * @param generic type of object to be stored
13 | */
14 | public class DisjointSet {
15 | private final Map identifier;
16 |
17 | /**
18 | * Basic constructor to create the Disjoint Set data structure.
19 | */
20 | public DisjointSet() {
21 | identifier = new HashMap<>();
22 | }
23 |
24 | /**
25 | * Constructor to initialize Disjoint Set with a known list of objects.
26 | * @param objects
27 | */
28 | public DisjointSet(List objects) {
29 | identifier = new HashMap<>();
30 | int size = objects.size();
31 | for (int i = 0; i < size; i++) {
32 | // internally, component identity is tracked with integers
33 | identifier.put(objects.get(i), identifier.size()); // each obj initialize with a unique identity using size;
34 | }
35 | }
36 |
37 | /**
38 | * Constructor to initialize Disjoint Set with a known array of objects.
39 | * @param objects
40 | */
41 | public DisjointSet(T[] objects) {
42 | identifier = new HashMap<>();
43 | int size = objects.length;
44 | for (int i = 0; i < size; i++) {
45 | // internally, component identity is tracked with integers
46 | identifier.put(objects[i], identifier.size()); // each obj initialize with a unique identity using size;
47 | }
48 | }
49 |
50 | public int size() {
51 | return identifier.size();
52 | }
53 |
54 | /**
55 | * Adds an object into the structure.
56 | * @param obj
57 | */
58 | public void add(T obj) {
59 | identifier.put(obj, identifier.size());
60 | }
61 |
62 | /**
63 | * Checks if object a and object b are in the same component.
64 | * @param a
65 | * @param b
66 | * @return a boolean value
67 | */
68 | public boolean find(T a, T b) {
69 | if (!identifier.containsKey(a) || !identifier.containsKey(b)) { // key(s) does not even exist
70 | return false;
71 | }
72 | return identifier.get(a).equals(identifier.get(b));
73 | }
74 |
75 | /**
76 | * Merge the components of object a and object b.
77 | * @param a
78 | * @param b
79 | */
80 | public void union(T a, T b) {
81 | if (!identifier.containsKey(a) || !identifier.containsKey(b)) { // key(s) does not even exist; do nothing
82 | return;
83 | }
84 |
85 | if (identifier.get(a).equals(identifier.get(b))) { // already same; do nothing
86 | return;
87 | }
88 |
89 | int compOfA = identifier.get(a);
90 | int compOfB = identifier.get(b);
91 | for (T obj : identifier.keySet()) {
92 | if (identifier.get(obj).equals(compOfA)) {
93 | identifier.put(obj, compOfB);
94 | }
95 | }
96 | }
97 |
98 | /**
99 | * Retrieves all elements that are in the same component as the specified object. Not a typical operation
100 | * but here to illustrate other use case.
101 | * @param a
102 | * @return a list of objects
103 | */
104 | public List retrieveFromSameComponent(T a) {
105 | List ret = new ArrayList<>();
106 | for (T obj : identifier.keySet()) {
107 | if (find(a, obj)) {
108 | ret.add(obj);
109 | }
110 | }
111 | return ret;
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/dataStructures/disjointSet/quickFind/README.md:
--------------------------------------------------------------------------------
1 | # Quick Find
2 |
3 | ## Background
4 | Every object will be assigned a component identity. The implementation of Quick Find often involves
5 | an underlying array or hash map that tracks the component identity of each object.
6 | Our implementation uses a hash map (to easily handle the case when objects aren't integers).
7 |
8 |
9 |

10 |
11 | Credits: CS2040s Lecture Slides
12 |
13 |
14 | ### Union
15 | Between the two components, decide on the component d, to represent the combined set. Let the other
16 | component's identity be d'. Simply iterate over the component identifier array / map, and for any element with
17 | identity d', assign it to d.
18 |
19 | ### Find
20 | Simply use the component identifier array to query for the component identity of the two elements
21 | and check if they are equal. This is why this implementation is known as "Quick Find".
22 |
23 | ## Complexity Analysis
24 | Let n be the number of elements in consideration.
25 |
26 | **Time**:
27 | - Union: O(n)
28 | - Find: O(1)
29 |
30 | **Space**: O(n) auxiliary space for the component identifier
31 |
32 | ## Notes
--------------------------------------------------------------------------------
/src/main/java/dataStructures/lruCache/LRU.java:
--------------------------------------------------------------------------------
1 | package dataStructures.lruCache;
2 |
3 | import java.util.HashMap;
4 |
5 | /**
6 | * Implementation of Least Recently Used (LRU) Cache
7 | *
8 | * @param generic type of key to be stored
9 | * @param generic type of associated value corresponding to a given key
10 | * Constructor:
11 | * LRU(int capacity)
12 | * Client methods:
13 | * get(K key)
14 | * put(K key, V value)
15 | * Both methods above run in expected O(1) time complexity
16 | */
17 | class LRU {
18 | /**
19 | * Helper node class that implements doubly linked list
20 | */
21 | private class DoublyLinkedListNode {
22 | private K key;
23 | private V val;
24 | private DoublyLinkedListNode next;
25 | private DoublyLinkedListNode prev;
26 | }
27 |
28 | private DoublyLinkedListNode dllHead;
29 | private DoublyLinkedListNode dllTail;
30 | private HashMap> keyToNode = new HashMap<>();
31 | private int capacity;
32 | private int lengthOfList = 0;
33 |
34 | /**
35 | * Constructs an instance of Least Recently Used Cache
36 | *
37 | * @param capacity the maximum capacity of the cache
38 | */
39 | public LRU(int capacity) {
40 | this.capacity = capacity;
41 |
42 | dllHead = new DoublyLinkedListNode<>();
43 | dllTail = new DoublyLinkedListNode<>();
44 | dllHead.next = dllTail;
45 | dllTail.prev = dllHead;
46 | }
47 |
48 | /**
49 | * Return the value of the key if it exists or return null
50 | *
51 | * @param key key of the value to be obtained from LRU cache
52 | */
53 | public V get(K key) {
54 | if (!keyToNode.containsKey(key)) {
55 | return null;
56 | }
57 |
58 | DoublyLinkedListNode temp = keyToNode.get(key);
59 | temp.prev.next = temp.next;
60 | temp.next.prev = temp.prev;
61 |
62 | temp.next = dllHead.next;
63 | dllHead.next.prev = temp;
64 | temp.prev = dllHead;
65 | dllHead.next = temp;
66 |
67 | return keyToNode.get(key).val;
68 | }
69 |
70 | /**
71 | * Insert key-value pair to LRU cache
72 | *
73 | * @param key key of the value to be inserted to LRU cache
74 | * @param value value to be inserted to LRU cache
75 | */
76 | public void put(K key, V value) {
77 | boolean addingNewNode = true;
78 |
79 | DoublyLinkedListNode newlyCached;
80 |
81 | if (!keyToNode.containsKey(key)) {
82 | newlyCached = new DoublyLinkedListNode<>();
83 | newlyCached.key = key;
84 | newlyCached.val = value;
85 | keyToNode.put(key, newlyCached);
86 | } else {
87 | newlyCached = keyToNode.get(key);
88 | newlyCached.val = value;
89 | addingNewNode = false;
90 |
91 | newlyCached.prev.next = newlyCached.next;
92 | newlyCached.next.prev = newlyCached.prev;
93 | }
94 |
95 | newlyCached.next = dllHead.next;
96 | dllHead.next.prev = newlyCached;
97 | newlyCached.prev = dllHead;
98 | dllHead.next = newlyCached;
99 |
100 | if (addingNewNode) {
101 | if (lengthOfList == capacity) {
102 | keyToNode.remove(dllTail.prev.key);
103 | dllTail.prev.prev.next = dllTail;
104 | dllTail.prev = dllTail.prev.prev;
105 | } else {
106 | lengthOfList++;
107 | }
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/dataStructures/queue/Deque/README.md:
--------------------------------------------------------------------------------
1 | # Double Ended Queue (Deque)
2 |
3 | 
4 |
5 | *Source: GeeksForGeeks*
6 |
7 | Deque is a variant of queue where elements can be removed or added from the head and tail of the queue.
8 | Deque could come in handy when trying to solve sliding window problems. This means it neither follows a fixed FIFO
9 | or LIFO order but rather can utilise either orders flexibly.
10 |
11 | A deque can be implemented in multiple ways, using doubly linked lists, arrays or two stacks.
12 |
13 | ## Analysis
14 |
15 | Much like a queue, deque operations involves the head / tail, resulting in *O(1)* complexity for most operations.
16 |
17 | ## Notes
18 |
19 | Just like a queue, a monotonic deque could also be created to solve more specific sliding window problems.
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/main/java/dataStructures/queue/Queue.java:
--------------------------------------------------------------------------------
1 | package dataStructures.queue;
2 |
3 | /**
4 | * Implementation of a queue structure using LinkedList.
5 | * Node class to be used as nodes for the LinkedList
6 | * is documented at the bottom.
7 | *
8 | * Callable methods / commonly supported ops:
9 | * size()
10 | * isEmpty()
11 | * enqueue(T item) - java's offer() / add() equivalent
12 | * dequeue() - java's poll() / remove() equivalent
13 | * peek()
14 | *
15 | * Note: calling dequeue() or peek() on an empty queue
16 | * returns null
17 | *
18 | * @param generic type of object to be stored in the queue
19 | */
20 | public class Queue {
21 | private Node first;
22 | private Node last;
23 | private int size;
24 |
25 | /**
26 | * Constructor to initialize an empty queue.
27 | */
28 | public Queue() {
29 | first = last = null;
30 | size = 0;
31 | }
32 |
33 | /**
34 | * Gets the size of the queue.
35 | *
36 | * @return size of queue
37 | */
38 | public int size() {
39 | return this.size;
40 | }
41 |
42 | /*
43 | * Checks if queue is empty.
44 | * @return boolean value
45 | */
46 | public boolean isEmpty() {
47 | return this.size == 0;
48 | }
49 |
50 | /**
51 | * Add an element to the end of the queue.
52 | * Note: java's equivalent methods are offer() / add().
53 | *
54 | * @param item item to be pushed
55 | */
56 | public void enqueue(T item) {
57 | Node toInsert = new Node<>(item);
58 | if (this.isEmpty()) {
59 | this.first = this.last = toInsert;
60 | } else {
61 | this.last.next = toInsert;
62 | this.last = toInsert;
63 | }
64 | this.size++;
65 | }
66 |
67 | /**
68 | * Remove an element from the start of the queue.
69 | * Note: java's equivalent methods are poll() / remove().
70 | *
71 | * @return head of the queue; null is empty
72 | */
73 | public T dequeue() {
74 | if (this.isEmpty()) {
75 | return null;
76 | }
77 | this.size--;
78 | Node removed = this.first;
79 | this.first = this.first.next;
80 | return removed.val;
81 | }
82 |
83 | /**
84 | * Display the element at the head of the queue.
85 | *
86 | * @return first item of the queue; null is queue is empty
87 | */
88 | public T peek() {
89 | if (this.isEmpty()) {
90 | return null;
91 | }
92 | return this.first.val;
93 | }
94 |
95 | /**
96 | * Node class to wrap the object.
97 | *
98 | * @param generic type of object to be stored in the queue
99 | */
100 | private static class Node {
101 | private final T val;
102 | private Node next;
103 |
104 | private Node(T val) {
105 | this.val = val;
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/java/dataStructures/queue/README.md:
--------------------------------------------------------------------------------
1 | # Queue
2 |
3 | ## Background
4 | A queue is a linear data structure that restricts the order in which operations can be performed on its elements.
5 |
6 | ### Operation Orders
7 |
8 | 
9 |
10 | *Source: GeeksForGeeks*
11 |
12 | Queue follows a FIFO, first in first out order.
13 | This means the earliest element
14 | added to the stack is the one operations are conducted on first.
15 |
16 | A [stack](../stack/README.md) is a queue with operations conducted in an opposite manner.
17 |
18 | ## Analysis
19 |
20 | As a queue only interacts with either the first or last element regardless during its operations,
21 | it only needs to keep the pointers of the two element at hand, which is constantly updated as more
22 | elements are removed / added. This allows queue operations to only incur a *O(1)* time complexity.
23 |
24 | ## Notes
25 |
26 | ### Stack vs Queues
27 |
28 | Stack and queues only differ in terms of operation order, you should aim to use a stack when
29 | you want the most recent elements to be operated on.
30 | Some situations where a stack would work well include build redo / undo systems and backtracking problems.
31 |
32 | On the other hand, a queue allows you to operate on elements that enter first. Some situations where
33 | this would be useful include Breadth First Search algorithms and task / resource allocation systems.
34 |
35 | ### Arrays vs Linked List
36 |
37 | It is worth noting that queues can be implemented with either a array or with a [linked list](../linkedList/README.md).
38 | In the context of ordered operations, the lookup is only restricted to the first element.
39 |
40 | Hence, there is not much advantage in using a array, which only has a better lookup speed (*O(1)* time complexity)
41 | to implement a queue. Especially when using a linked list to construct your queue
42 | would allow you to grow or shrink the queue as you wish.
43 |
--------------------------------------------------------------------------------
/src/main/java/dataStructures/queue/monotonicQueue/MonotonicQueue.java:
--------------------------------------------------------------------------------
1 | package dataStructures.queue;
2 |
3 |
4 | import java.util.ArrayDeque;
5 | import java.util.Deque;
6 |
7 | /**
8 | * Implementation of a non-increasing (decreasing) monotonic queue
9 | * for certain (but common) use case:
10 | * When larger objects pushed to the queue are able to replace and represent
11 | * smaller objects that come before it.
12 | * Callable methods are:
13 | * isEmpty()
14 | * max()
15 | * pop()
16 | * push()
17 | *
18 | * Movement within the monotonic-queue:
19 | * index v Increasing queue Decreasing queue
20 | * 1 5 [5] [5]
21 | * 2 3 [3] 3 kick out 5 [5, 3] #3->5
22 | * 3 1 [1] 1 kick out 3 [5, 3, 1] #1->3
23 | * 4 2 [1, 2] #2->1 [5, 3, 2] 2 kick out 1 #2->3
24 | * 5 4 [1, 2, 4] #4->2 [5,4] 4 kick out 2, 3 #4->2
25 | *
26 | * @param generic type for objects to be stored or queried
27 | */
28 |
29 | public class MonotonicQueue> {
30 | private final Deque dq = new ArrayDeque<>(); // or LinkedList
31 |
32 | /**
33 | * Checks if queue is empty.
34 | *
35 | * @return boolean
36 | */
37 | public boolean isEmpty() {
38 | return dq.isEmpty();
39 | }
40 |
41 | /**
42 | * Push an object into the queue and maintain the non-increasing property.
43 | *
44 | * @param obj to be inserted.
45 | */
46 | public void push(T obj) {
47 | Integer count = 0;
48 | while (!dq.isEmpty() && obj.compareTo(dq.peekLast()) > 0) {
49 | dq.pollLast(); // Removes elements that do not conform the non-increasing sequence.
50 | }
51 | dq.offerLast(obj);
52 | }
53 |
54 | /**
55 | * Returns the highest value stored in the queue.
56 | *
57 | * @return the first value of the queue.
58 | */
59 | public T max() {
60 | if (isEmpty()) {
61 | return null;
62 | }
63 | return dq.peek();
64 | }
65 |
66 | /**
67 | * Returns the highest valued object in the queue; i.e the first object;
68 | * Removal will only be done all representation of the object has been accounted for.
69 | *
70 | * @return The first value in the deque. (Highest valued object)
71 | */
72 | public T pop() {
73 | if (dq.isEmpty()) {
74 | return null;
75 | }
76 | return dq.poll(); // Returns & remove head of deque
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/dataStructures/queue/monotonicQueue/README.md:
--------------------------------------------------------------------------------
1 | # Monotonic Queue
2 |
3 | This is a variant of queue where elements within the queue are either strictly increasing or decreasing.
4 | Monotonic queues are often implemented with a deque.
5 |
6 | Within a increasing monotonic queue, any element that is smaller than the current minimum is removed.
7 | Within a decreasing monotonic queue, any element that is larger than the current maximum is removed.
8 |
9 | It is worth mentioning that the most elements added to the monotonic queue would always be in a
10 | increasing / decreasing order,
11 | hence, we only need to compare down the monotonic queue from the back when adding new elements.
12 |
13 | ## Operation Orders
14 |
15 | Just like a queue, a monotonic queue mainly has *O(1)* operations,
16 | which is unique given that it ensures a certain order, which usually incurs operations of a higher complexity.
17 |
--------------------------------------------------------------------------------
/src/main/java/dataStructures/stack/README.md:
--------------------------------------------------------------------------------
1 | # Stack
2 |
3 | ## Background
4 | Stack is a linear data structure that restricts
5 | the order in which operations can be performed on its elements.
6 |
7 | 
8 |
9 | *Source: Programiz*
10 |
11 | ### Operation Orders
12 |
13 | Stack follows a LIFO, last in first out order.
14 | This means the most recent element
15 | added to the stack is the one operations are conducted on first.
16 |
17 | A [queue](../queue/README.md) conducts operations in the opposite order.
18 |
19 | ## Analysis
20 |
21 | As a stack only interacts with the most recent element regardless of operation,
22 | it keeps the pointer of the most recent element at hand, which is constantly updated as more
23 | elements are removed / added. This allows stack operations to only incur a *O(1)* time complexity.
24 |
25 | ## Notes
26 |
27 | ### Stack vs Queues
28 |
29 | Stack and queues only differ in terms of operation order, you should aim to use a stack when
30 | you want the most recent elements to be operated on.
31 | Some situations where a stack would work well include build redo / undo systems and backtracking problems.
32 |
33 | On the other hand, a queue allows you to operate on elements that enter first. Some situations where
34 | this would be useful include Breadth First Search algorithms and task / resource allocation systems.
35 |
36 | ### Arrays vs Linked List
37 |
38 | It is worth noting that stacks can be implemented with either a array or with a [linked list](../linkedList/README.md).
39 | In the context of ordered operations, the lookup is only restricted to the first element.
40 |
41 | Hence, there is not much advantage in using a array, which only has a better lookup speed (*O(1)* time complexity)
42 | to implement a stack. Especially when using a linked list to construct your stack
43 | would allow you to grow or shrink the stack as you wish.
44 |
--------------------------------------------------------------------------------
/src/main/java/dataStructures/stack/Stack.java:
--------------------------------------------------------------------------------
1 | package dataStructures.stack;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 | import java.util.List;
6 |
7 | /**
8 | * Simple implementation of a stack using ArrayList.
9 | * Note: ArrayList can be implemented using LinkedList
10 | * and hence the code below can be replicated with
11 | * LinkedList.
12 | * Callable methods:
13 | * push(T item)
14 | * pop()
15 | * peek()
16 | * isEmpty()
17 | * Note: Calling pop() or peek() on an empty stack returns null
18 | *
19 | * @param generic type of objects to be stored in the stack
20 | */
21 | public class Stack {
22 | private final List stack;
23 |
24 | /**
25 | * Constructor to create an empty stack
26 | */
27 | public Stack() {
28 | stack = new ArrayList<>();
29 | }
30 |
31 | /**
32 | * Constructor to create a stack with a given list holding the same type
33 | */
34 | public Stack(List lst) {
35 | stack = lst;
36 | }
37 |
38 | /**
39 | * Constructor to create a stack given a sequence of objects of the same generic type
40 | */
41 | public Stack(T... lst) {
42 | stack = new ArrayList();
43 | Collections.addAll(stack, lst);
44 | }
45 |
46 | /**
47 | * @param item element to be pushed as the last element into the stack
48 | */
49 | public void push(T item) {
50 | stack.add(item);
51 | }
52 |
53 | /**
54 | * Removes the last element from the stack.
55 | *
56 | * @return last element of the stack; null if the stack is empty
57 | */
58 | public T pop() {
59 | if (this.isEmpty()) {
60 | return null;
61 | }
62 | // note that the operation below is O(1) when called on the last index
63 | return stack.remove(stack.size() - 1);
64 | }
65 |
66 | /**
67 | * Displays the last element of the stack.
68 | *
69 | * @return last element of the stack; null if the stack is empty
70 | */
71 | public T peek() {
72 | if (this.isEmpty()) {
73 | return null;
74 | }
75 | // similarly, below is an O(1) operation
76 | return stack.get(stack.size() - 1);
77 | }
78 |
79 | /**
80 | * Checks if the stack is empty.
81 | *
82 | * @return boolean value representing whether the stack is empty
83 | */
84 | public boolean isEmpty() {
85 | return stack.isEmpty();
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/binarySearch/BinarySearchTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.binarySearch;
2 |
3 | import static org.junit.Assert.assertEquals;
4 |
5 | import org.junit.Test;
6 |
7 | import algorithms.binarySearch.binarySearch.BinarySearch;
8 | import algorithms.binarySearch.binarySearchTemplated.BinarySearchTemplated;
9 |
10 | public class BinarySearchTest {
11 | @Test
12 | public void test_binarySearch() {
13 | // Test 1: even number of elements
14 | int[] firstArray = {1, 5, 10, 12};
15 | int firstResult = BinarySearch.search(firstArray, 1);
16 |
17 | // Test 2: odd number of elements
18 | int[] secondArray = {1, 5, 10, 11, 12};
19 | int secondResult = BinarySearch.search(secondArray, 11);
20 |
21 | // Test 3: target not in array
22 | int[] thirdArray = {1, 5, 10, 11, 12};
23 | int thirdResult = BinarySearch.search(thirdArray, 3);
24 |
25 | assertEquals(0, firstResult);
26 | assertEquals(3, secondResult);
27 | assertEquals(-1, thirdResult);
28 | }
29 |
30 | @Test
31 | public void test_binarySearchTemplated() {
32 | // Test 1: even number of elements
33 | int[] firstArray = {1, 5, 10, 12};
34 | int firstResult = BinarySearchTemplated.search(firstArray, 1);
35 |
36 | // Test 2: odd number of elements
37 | int[] secondArray = {1, 5, 10, 11, 12};
38 | int secondResult = BinarySearchTemplated.search(secondArray, 11);
39 |
40 | // Test 3: target not in array but could exist within search space
41 | int[] thirdArray = {1, 5, 10, 11, 12};
42 | int thirdResult = BinarySearchTemplated.search(thirdArray, 3);
43 |
44 | // Test 4: target not in array but could exist on the right of search space
45 | int[] fourthArray = {1, 5, 10, 11, 12};
46 | int fourthResult = BinarySearchTemplated.search(thirdArray, 13);
47 |
48 | // Test 3: target not in array but could exist on the left of search space
49 | int[] fifthArray = {1, 5, 10, 11, 12};
50 | int fifthResult = BinarySearchTemplated.search(thirdArray, 0);
51 |
52 | assertEquals(0, firstResult);
53 | assertEquals(3, secondResult);
54 | assertEquals(-1, thirdResult);
55 | assertEquals(-1, fourthResult);
56 | assertEquals(-1, fifthResult);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/minimumSpanningTree/kruskal/KruskalTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.minimumSpanningTree.kruskal;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import org.junit.Test;
6 |
7 | public class KruskalTest {
8 | @Test
9 | public void test_simpleGraph() {
10 | // Graph setup (Adjacency Matrix)
11 | // B
12 | // / \
13 | // 1 1
14 | // / \
15 | // A - 1 - C
16 | int[][] adjacencyMatrix = {
17 | {0, 1, 1}, // A: A-B, A-C
18 | {1, 0, 1}, // B: B-A, B-C
19 | {1, 1, 0} // C: C-A, C-B
20 | };
21 |
22 | Kruskal.Node[] nodes = {
23 | new Kruskal.Node("A", 0),
24 | new Kruskal.Node("B", 1),
25 | new Kruskal.Node("C", 2)
26 | };
27 |
28 | // Run Kruskal's algorithm
29 | int[][] actualMST = Kruskal.getKruskalMST(nodes, adjacencyMatrix);
30 |
31 | // Expected MST
32 | // A -1- B -1- C
33 | int[][] expectedMST = {
34 | {0, 1, 1}, // A: A-B, A-C
35 | {1, 0, Integer.MAX_VALUE}, // B: B-A
36 | {1, Integer.MAX_VALUE, 0} // C: C-A
37 | };
38 |
39 | // Assertion
40 | assertArrayEquals(expectedMST, actualMST);
41 | }
42 |
43 | @Test
44 | public void test_complexGraph() {
45 | // Graph setup
46 | // A
47 | // / | \
48 | // 1 4 3
49 | /// | \
50 | //B --3-- D
51 | // \ | /
52 | // 2 4 1
53 | // \|/
54 | // C
55 | int[][] adjacencyMatrix = {
56 | {0, 1, 4, 3}, // A: A-B, A-C, A-D
57 | {1, 0, 2, 3}, // B: B-A, B-C, B-D
58 | {4, 2, 0, 1}, // C: C-A, C-B, C-D
59 | {3, 3, 1, 0} // D: D-A, D-B, D-C
60 | };
61 |
62 | Kruskal.Node[] nodes = {
63 | new Kruskal.Node("A", 0),
64 | new Kruskal.Node("B", 1),
65 | new Kruskal.Node("C", 2),
66 | new Kruskal.Node("D", 3)
67 | };
68 |
69 | // Run Prim's algorithm
70 | int[][] actualMST = Kruskal.getKruskalMST(nodes, adjacencyMatrix);
71 |
72 | // Expected MST
73 | // Based on the graph, assuming the MST is correctly computed
74 | int[][] expectedMST = {
75 | {0, 1, Integer.MAX_VALUE, Integer.MAX_VALUE}, // A: A-B
76 | {1, 0, 2, Integer.MAX_VALUE}, // B: B-A, B-C
77 | {Integer.MAX_VALUE, 2, 0, 1}, // C: C-B, C-D
78 | {Integer.MAX_VALUE, Integer.MAX_VALUE, 1, 0} // D: D-C
79 | };
80 |
81 | // Assertion
82 | assertArrayEquals(expectedMST, actualMST);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/minimumSpanningTree/prim/PrimTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.minimumSpanningTree.prim;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import org.junit.Test;
6 |
7 | public class PrimTest {
8 |
9 | @Test
10 | public void test_simpleGraph() {
11 | // Graph setup (Adjacency Matrix)
12 | // B
13 | // / \
14 | // 1 1
15 | // / \
16 | // A - 1 - C
17 | int[][] adjacencyMatrix = {
18 | {0, 1, 1}, // A: A-B, A-C
19 | {1, 0, 1}, // B: B-A, B-C
20 | {1, 1, 0} // C: C-A, C-B
21 | };
22 |
23 | Prim.Node[] nodes = {
24 | new Prim.Node("A", 0),
25 | new Prim.Node("B", 1),
26 | new Prim.Node("C", 2)
27 | };
28 |
29 | // Run Prim's algorithm
30 | int[][] actualMST = Prim.getPrimsMST(nodes, adjacencyMatrix);
31 |
32 | // Expected MST
33 | // A -1- B -1- C
34 | int[][] expectedMST = {
35 | {0, 1, Integer.MAX_VALUE}, // A: A-B
36 | {1, 0, 1}, // B: B-A, B-C
37 | {Integer.MAX_VALUE, 1, 0} // C: C-B
38 | };
39 |
40 | // Assertion
41 | assertArrayEquals(expectedMST, actualMST);
42 | }
43 |
44 | @Test
45 | public void test_complexGraph() {
46 | // Graph setup
47 | // A
48 | // / | \
49 | // 1 4 3
50 | /// | \
51 | //B --3-- D
52 | // \ | /
53 | // 2 4 1
54 | // \|/
55 | // C
56 | int[][] adjacencyMatrix = {
57 | {0, 1, 4, 3}, // A: A-B, A-C, A-D
58 | {1, 0, 2, 3}, // B: B-A, B-C, B-D
59 | {4, 2, 0, 1}, // C: C-A, C-B, C-D
60 | {3, 3, 1, 0} // D: D-A, D-B, D-C
61 | };
62 |
63 | Prim.Node[] nodes = {
64 | new Prim.Node("A", 0),
65 | new Prim.Node("B", 1),
66 | new Prim.Node("C", 2),
67 | new Prim.Node("D", 3)
68 | };
69 |
70 | // Run Prim's algorithm
71 | int[][] actualMST = Prim.getPrimsMST(nodes, adjacencyMatrix);
72 |
73 | // Expected MST
74 | // Based on the graph, assuming the MST is correctly computed
75 | int[][] expectedMST = {
76 | {0, 1, Integer.MAX_VALUE, Integer.MAX_VALUE}, // A: A-B
77 | {1, 0, 2, Integer.MAX_VALUE}, // B: B-A, B-C
78 | {Integer.MAX_VALUE, 2, 0, 1}, // C: C-B, C-D
79 | {Integer.MAX_VALUE, Integer.MAX_VALUE, 1, 0} // D: D-C
80 | };
81 |
82 | // Assertion
83 | assertArrayEquals(expectedMST, actualMST);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/orthogonalRangeSearching/oneDim/OrthogonalRangeSearchingTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.orthogonalRangeSearching.oneDim;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import org.junit.Test;
6 |
7 | import algorithms.orthogonalRangeSearching.RangeTreeNode;
8 |
9 | public class OrthogonalRangeSearchingTest {
10 | @Test
11 | public void test_orthogonalRangeSearching() {
12 |
13 | int[] firstInput = new int[]{7, 12, 25, 26, 40, 45};
14 | Object[] firstExpected = new Object[]{12, 25, 26, 40, 45};
15 | RangeTreeNode firstTree = OrthogonalRangeSearching.buildTree(firstInput, 0, firstInput.length - 1);
16 | Object[] firstResult = OrthogonalRangeSearching.search(firstTree, 10, 50);
17 | assertArrayEquals(firstExpected, firstResult);
18 |
19 | int[] secondInput = new int[]{-26, -7, -10, -40, -43};
20 | Object[] secondExpected = new Object[]{-26, -10, -7};
21 | RangeTreeNode secondTree = OrthogonalRangeSearching.buildTree(secondInput, 0, secondInput.length - 1);
22 | Object[] secondResult = OrthogonalRangeSearching.search(secondTree, -30, -2);
23 | assertArrayEquals(secondExpected, secondResult);
24 |
25 | int[] thirdInput = new int[]{26, 7, 12, 40, 45};
26 | Object[] thirdExpected = new Object[]{12, 26};
27 | RangeTreeNode thirdTree = OrthogonalRangeSearching.buildTree(thirdInput, 0, thirdInput.length - 1);
28 | Object[] thirdResult = OrthogonalRangeSearching.search(thirdTree, 10, 35);
29 | assertArrayEquals(thirdExpected, thirdResult);
30 |
31 | // for fourth input
32 | // static queries
33 | int[] fourthInput = new int[]{3, 19, 30, 49, 59, 70, 89, 100};
34 | Object[] fourthExpected = new Object[]{30, 49, 59, 70};
35 | RangeTreeNode fourthTree = OrthogonalRangeSearching.buildTree(fourthInput, 0, fourthInput.length - 1);
36 | Object[] fourthResult = OrthogonalRangeSearching.search(fourthTree, 20, 71);
37 | assertArrayEquals(fourthExpected, fourthResult);
38 |
39 | Object[] fifthExpected = new Object[]{49, 59, 70, 89, 100};
40 | Object[] fifthResult = OrthogonalRangeSearching.search(fourthTree, 31, 130);
41 | assertArrayEquals(fifthExpected, fifthResult);
42 |
43 | Object[] sixthExpected = new Object[]{59};
44 | Object[] sixthResult = OrthogonalRangeSearching.search(fourthTree, 59, 59);
45 | assertArrayEquals(sixthExpected, sixthResult);
46 |
47 | // dynamic updates then query
48 |
49 | OrthogonalRangeSearching.configureTree(fourthTree);
50 | fourthTree = OrthogonalRangeSearching.insert(fourthTree, 101);
51 | Object[] seventhExpected = new Object[]{49, 59, 70, 89, 100, 101};
52 | Object[] seventhResult = OrthogonalRangeSearching.search(fourthTree, 31, 130);
53 | assertArrayEquals(seventhExpected, seventhResult);
54 |
55 | fourthTree = OrthogonalRangeSearching.insert(fourthTree, 46);
56 | fourthTree = OrthogonalRangeSearching.insert(fourthTree, 32);
57 | Object[] eighthExpected = new Object[]{30, 32, 46, 49, 59, 70};
58 | Object[] eighthResult = OrthogonalRangeSearching.search(fourthTree, 20, 71);
59 | assertArrayEquals(eighthExpected, eighthResult);
60 |
61 | fourthTree = OrthogonalRangeSearching.delete(fourthTree, 32);
62 | fourthTree = OrthogonalRangeSearching.delete(fourthTree, 59);
63 | Object[] ninthExpected = new Object[]{30, 46, 49, 70};
64 | Object[] ninthResult = OrthogonalRangeSearching.search(fourthTree, 20, 72);
65 | assertArrayEquals(ninthExpected, ninthResult);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/orthogonalRangeSearching/twoDim/OrthogonalRangeSearchingTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.orthogonalRangeSearching.twoDim;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import java.util.ArrayList;
6 | import java.util.List;
7 |
8 | import org.junit.Test;
9 |
10 | import algorithms.orthogonalRangeSearching.RangeTreeNode;
11 |
12 | public class OrthogonalRangeSearchingTest {
13 | @Test
14 | public void test_orthogonalRangeSearching() {
15 |
16 | ArrayList firstInput = new ArrayList<>();
17 | firstInput.add(new Integer[]{4, 5});
18 | firstInput.add(new Integer[]{3, 3});
19 | firstInput.add(new Integer[]{2, 4});
20 | firstInput.add(new Integer[]{1, 2});
21 | firstInput.add(new Integer[]{5, 2});
22 | firstInput.add(new Integer[]{6, 3});
23 | firstInput.add(new Integer[]{7, 1});
24 | firstInput.add(new Integer[]{8, 4});
25 |
26 | ArrayList firstExpected = new ArrayList<>();
27 | firstExpected.add(new Integer[]{1, 2});
28 | firstExpected.add(new Integer[]{3, 3});
29 | firstExpected.add(new Integer[]{5, 2});
30 | firstExpected.add(new Integer[]{6, 3});
31 |
32 | RangeTreeNode firstTree = OrthogonalRangeSearching.buildXTree(firstInput,
33 | 0, firstInput.size() - 1);
34 | List firstResult = OrthogonalRangeSearching.search(firstTree, 1, 6, 1, 3);
35 | assertArrayEquals(firstExpected.toArray(), firstResult.toArray());
36 |
37 | ArrayList secondExpected = new ArrayList<>();
38 | secondExpected.add(new Integer[]{6, 3});
39 | secondExpected.add(new Integer[]{7, 1});
40 | secondExpected.add(new Integer[]{8, 4});
41 | List secondResult = OrthogonalRangeSearching.search(firstTree, 6, 9, 0, 4);
42 | assertArrayEquals(secondExpected.toArray(), secondResult.toArray());
43 |
44 | ArrayList thirdExpected = new ArrayList<>();
45 | List thirdResult = OrthogonalRangeSearching.search(firstTree, 6, 9, 2, 2);
46 | assertArrayEquals(thirdExpected.toArray(), thirdResult.toArray());
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/patternFinding/KmpTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.patternFinding;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.List;
6 |
7 | import org.junit.Assert;
8 | import org.junit.Test;
9 |
10 | /**
11 | * Test cases for {@link KMP}.
12 | */
13 | public class KmpTest {
14 | @Test
15 | public void test_findOccurrences_shouldReturnStartIndices() {
16 | String seq = "abclaabcabcabc";
17 | String pattern = "abc";
18 |
19 | List indices = KMP.findOccurrences(seq, pattern);
20 | List expected = Arrays.asList(0, 5, 8, 11);
21 | Assert.assertEquals(expected, indices);
22 | }
23 |
24 | @Test
25 | public void testEmptySequence_findOccurrences_shouldReturnStartIndices() {
26 | String seq = "";
27 | String pattern = "a";
28 |
29 | List indices = KMP.findOccurrences(seq, pattern);
30 | List expected = new ArrayList<>();
31 | Assert.assertEquals(expected, indices);
32 | }
33 |
34 | @Test
35 | public void testNoOccurence_findOccurrences_shouldReturnStartIndices() {
36 | String seq = "abcabcabc";
37 | String patternOne = "noway";
38 | String patternTwo = "cbc";
39 |
40 | List indicesOne = KMP.findOccurrences(seq, patternOne);
41 | List expectedOne = new ArrayList<>();
42 | Assert.assertEquals(expectedOne, indicesOne);
43 |
44 | List indicesTwo = KMP.findOccurrences(seq, patternTwo);
45 | List expectedTwo = new ArrayList<>();
46 | Assert.assertEquals(expectedTwo, indicesTwo);
47 | }
48 |
49 | @Test
50 | public void testRepeatPatternOnly_findOccurrences_shouldReturnStartIndices() {
51 | String seq = "abcabcabcabcabc";
52 | String pattern = "abc";
53 |
54 | List indices = KMP.findOccurrences(seq, pattern);
55 | List expected = Arrays.asList(0, 3, 6, 9, 12);
56 | Assert.assertEquals(expected, indices);
57 | }
58 |
59 | @Test
60 | public void testAllSame_findOccurrences_shouldReturnStartIndices() {
61 | String seq = "aaaaaaaaaaaaa";
62 | String pattern = "aa";
63 |
64 | List indices = KMP.findOccurrences(seq, pattern);
65 | List expected = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
66 | Assert.assertEquals(expected, indices);
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/sorting/bubbleSort/BubbleSortTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.bubbleSort;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import java.util.Arrays;
6 |
7 | import org.junit.Test;
8 |
9 | /**
10 | * Test cases for {@link BubbleSort}.
11 | */
12 | public class BubbleSortTest {
13 | @Test
14 | public void test_bubbleSort_shouldReturnSortedArray() {
15 | int[] firstArray =
16 | new int[] {
17 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7,
18 | 85, 30
19 | };
20 | int[] firstResult = BubbleSort.sort(Arrays.copyOf(firstArray, firstArray.length));
21 |
22 | int[] secondArray =
23 | new int[] {
24 | 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,
25 | 9, 10
26 | };
27 | int[] secondResult = BubbleSort.sort(Arrays.copyOf(secondArray, secondArray.length));
28 |
29 | int[] thirdArray = new int[] {};
30 | int[] thirdResult = BubbleSort.sort(Arrays.copyOf(thirdArray, thirdArray.length));
31 |
32 | int[] fourthArray = new int[] {1};
33 | int[] fourthResult = BubbleSort.sort(Arrays.copyOf(fourthArray, fourthArray.length));
34 |
35 | Arrays.sort(firstArray);
36 | Arrays.sort(secondArray);
37 | Arrays.sort(thirdArray);
38 | Arrays.sort(fourthArray);
39 |
40 | assertArrayEquals(firstResult, firstArray);
41 | assertArrayEquals(secondResult, secondArray);
42 | assertArrayEquals(thirdResult, thirdArray);
43 | assertArrayEquals(fourthResult, fourthArray);
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/sorting/countingSort/CountingSortTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.countingSort;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import java.util.Arrays;
6 |
7 | import org.junit.Test;
8 |
9 | /**
10 | * Test cases for {@link CountingSort}.
11 | */
12 | public class CountingSortTest {
13 |
14 | @Test
15 | public void countingSort_emptyArray_shouldReturnEmptyArray() {
16 | int[] emptyArray = new int[10];
17 | int[] result = CountingSort.sort(emptyArray);
18 | assertArrayEquals(emptyArray, result);
19 | }
20 |
21 | @Test
22 | public void test_countingSort_shouldReturnSortedArray() {
23 | int[] firstArray =
24 | new int[] {
25 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7,
26 | 85, 30
27 | };
28 | int[] firstResult = CountingSort.sort(firstArray);
29 |
30 | int[] secondArray =
31 | new int[] {
32 | 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,
33 | 9, 10
34 | };
35 | int[] secondResult = CountingSort.sort(secondArray);
36 |
37 | int[] thirdArray = new int[] {};
38 | int[] thirdResult = CountingSort.sort(thirdArray);
39 |
40 | Arrays.sort(firstArray); // get expected result
41 | Arrays.sort(secondArray); // get expected result
42 | Arrays.sort(thirdArray); // get expected result
43 |
44 | assertArrayEquals(firstResult, firstArray);
45 | assertArrayEquals(secondResult, secondArray);
46 | assertArrayEquals(thirdResult, thirdArray);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/sorting/cyclicSort/generalised/CyclicSortTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.cyclicSort.generalised;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import java.util.Arrays;
6 |
7 | import org.junit.Test;
8 |
9 | /**
10 | * Test cases for generalized {@link CyclicSort}.
11 | */
12 | public class CyclicSortTest {
13 | @Test
14 | public void cyclicSort_emptyArray_shouldReturnEmptyArray() {
15 | int[] emptyArray = new int[10];
16 | int[] expected = new int[10];
17 |
18 | CyclicSort.sort(emptyArray);
19 | assertArrayEquals(emptyArray, expected);
20 | }
21 |
22 | @Test
23 | public void cyclicSort_positiveInputs_shouldReturnSortedArray() {
24 | int[] array =
25 | new int[] {
26 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7,
27 | 85, 30
28 | };
29 | int[] expected = Arrays.copyOf(array, array.length);
30 | Arrays.sort(expected);
31 |
32 | CyclicSort.sort(array);
33 | assertArrayEquals(array, expected);
34 | }
35 |
36 | @Test
37 | public void cyclicSort_someNegativeInputs_shouldReturnSortedArray() {
38 | int[] array =
39 | new int[] {
40 | 2, 3, 4, 1, 2, -5, 6, 7, 10, -15, 20, 13, 15, 1, 2, 15, -12, 20, 21, 120, 11, 5,
41 | 7, -85, 30
42 | };
43 | int[] expected = Arrays.copyOf(array, array.length);
44 | Arrays.sort(expected);
45 |
46 | CyclicSort.sort(array);
47 | assertArrayEquals(array, expected);
48 | }
49 |
50 | @Test
51 | public void cyclicSort_alreadySorted_shouldReturnSortedArray() {
52 | int[] array =
53 | new int[] {1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15};
54 | int[] expected = Arrays.copyOf(array, array.length);
55 | Arrays.sort(expected);
56 |
57 | CyclicSort.sort(array);
58 | assertArrayEquals(array, expected);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/sorting/cyclicSort/simple/FindFirstMissingNonNegativeTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.cyclicSort.simple;
2 |
3 | import static org.junit.Assert.assertEquals;
4 |
5 | import org.junit.Test;
6 |
7 | /**
8 | * Test cases for simple {@link FindFirstMissingNonNegative}.
9 | */
10 | public class FindFirstMissingNonNegativeTest {
11 | @Test
12 | public void findMissing_randomArray_shouldReturnFirstMissing() {
13 | int[] arrayMissing3 = {0, 7, 1, 2, 4, 5, 6, 8}; // 3
14 | assertEquals(FindFirstMissingNonNegative.findMissing(arrayMissing3), 3);
15 | }
16 |
17 | @Test
18 | public void findMissing_duplicatesPresent_shouldReturnFirstMissing() {
19 | int[] arrayMissing5 = {1, 3, 2, 0, 2, 7, 4}; // 5
20 | assertEquals(FindFirstMissingNonNegative.findMissing(arrayMissing5), 5);
21 | }
22 |
23 | @Test
24 | public void findMissing_firstNthPresent_shouldReturnFirstMissing() {
25 | int[] arrayMissing7 = {0, 1, 2, 3, 4, 5, 6}; // 7
26 | assertEquals(FindFirstMissingNonNegative.findMissing(arrayMissing7), 7);
27 | }
28 |
29 | @Test
30 | public void findMissing_allNegatives_shouldReturnZero() {
31 | int[] arrayAllNegatives = {-5, -10, -25, -500, -1}; // 0
32 | assertEquals(FindFirstMissingNonNegative.findMissing(arrayAllNegatives), 0);
33 | }
34 |
35 | @Test
36 | public void findMissing_allPositives_shouldReturnZero() {
37 | int[] arrayAllPositives = {5, 10, 25, 500, 1}; // 0
38 | assertEquals(FindFirstMissingNonNegative.findMissing(arrayAllPositives), 0);
39 | }
40 |
41 | @Test
42 | public void findMissing_empty_shouldReturnZero() {
43 | int[] empty = {};
44 | assertEquals(FindFirstMissingNonNegative.findMissing(empty), 0);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/sorting/cyclicSort/simple/SimpleCyclicSortTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.cyclicSort.simple;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import org.junit.Test;
6 |
7 | /**
8 | * Test cases for Simple {@link CyclicSort}.
9 | */
10 | public class SimpleCyclicSortTest {
11 | @Test
12 | public void test_cyclicSort_shouldReturnSortedArray() {
13 | // unsorted array of 16 integers, from 0 to 15
14 | int[] firstArray =
15 | new int[] {2, 5, 6, 4, 8, 15, 3, 1, 7, 12, 14, 0, 9, 13, 11, 10};
16 | int[] firstExpected =
17 | new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
18 |
19 | // already sorted, do nothing
20 | int[] secondArray =
21 | new int[] {0, 1, 2, 3, 4, 5, 6};
22 | int[] secondExpected =
23 | new int[] {0, 1, 2, 3, 4, 5, 6};
24 |
25 | // empty, do nothing
26 | int[] thirdArray = new int[] {};
27 | int[] thirdExpected = new int[] {};
28 |
29 | CyclicSort.sort(firstArray);
30 | CyclicSort.sort(secondArray);
31 | CyclicSort.sort(thirdArray);
32 |
33 | assertArrayEquals(firstArray, firstExpected);
34 | assertArrayEquals(secondArray, secondExpected);
35 | assertArrayEquals(thirdArray, thirdExpected);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/sorting/insertionSort/InsertionSortTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.insertionSort;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import java.util.Arrays;
6 |
7 | import org.junit.Test;
8 |
9 | /**
10 | * Test cases for {@link InsertionSort}.
11 | */
12 | public class InsertionSortTest {
13 |
14 | @Test
15 | public void test_insertionSort_shouldReturnSortedArray() {
16 | int[] firstArray =
17 | new int[] {
18 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7,
19 | 85, 30
20 | };
21 | int[] firstResult = InsertionSort.sort(Arrays.copyOf(firstArray, firstArray.length));
22 |
23 | int[] secondArray =
24 | new int[] {
25 | 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,
26 | 9, 10
27 | };
28 | int[] secondResult = InsertionSort.sort(Arrays.copyOf(secondArray, secondArray.length));
29 |
30 | int[] thirdArray = new int[] {};
31 | int[] thirdResult = InsertionSort.sort(Arrays.copyOf(thirdArray, thirdArray.length));
32 |
33 | int[] fourthArray = new int[] {1};
34 | int[] fourthResult = InsertionSort.sort(Arrays.copyOf(fourthArray, fourthArray.length));
35 |
36 | int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0};
37 | int[] fifthResult = InsertionSort.sort(Arrays.copyOf(fifthArray, fifthArray.length));
38 |
39 | Arrays.sort(firstArray); // get expected result
40 | Arrays.sort(secondArray); // get expected result
41 | Arrays.sort(thirdArray); // get expected result
42 | Arrays.sort(fourthArray); // get expected result
43 | Arrays.sort(fifthArray); // get expected result
44 |
45 | assertArrayEquals(firstResult, firstArray);
46 | assertArrayEquals(secondResult, secondArray);
47 | assertArrayEquals(thirdResult, thirdArray);
48 | assertArrayEquals(fourthResult, fourthArray);
49 | assertArrayEquals(fifthResult, fifthArray);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/sorting/mergeSort/iterative/MergeSortTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.mergeSort.iterative;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import java.util.Arrays;
6 |
7 | import org.junit.Test;
8 |
9 | /**
10 | * Test cases for iterative {@link MergeSort}.
11 | */
12 | public class MergeSortTest {
13 |
14 | @Test
15 | public void test_insertionSort_shouldReturnSortedArray() {
16 | int[] firstArray =
17 | new int[] {
18 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7,
19 | 85, 30
20 | };
21 | int[] firstResult = Arrays.copyOf(firstArray, firstArray.length);
22 | MergeSort.sort(firstResult);
23 |
24 | int[] secondArray =
25 | new int[] {
26 | 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,
27 | 9, 10
28 | };
29 | int[] secondResult = Arrays.copyOf(secondArray, secondArray.length);
30 | MergeSort.sort(secondResult);
31 |
32 | int[] thirdArray = new int[] {};
33 | int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length);
34 | MergeSort.sort(thirdResult);
35 |
36 | int[] fourthArray = new int[] {1};
37 | int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length);
38 | MergeSort.sort(fourthResult);
39 |
40 | int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0};
41 | int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length);
42 | MergeSort.sort(fifthResult);
43 |
44 | Arrays.sort(firstArray); // get expected result
45 | Arrays.sort(secondArray); // get expected result
46 | Arrays.sort(thirdArray); // get expected result
47 | Arrays.sort(fourthArray); // get expected result
48 | Arrays.sort(fifthArray); // get expected result
49 |
50 | assertArrayEquals(firstResult, firstArray);
51 | assertArrayEquals(secondResult, secondArray);
52 | assertArrayEquals(thirdResult, thirdArray);
53 | assertArrayEquals(fourthResult, fourthArray);
54 | assertArrayEquals(fifthResult, fifthArray);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/sorting/mergeSort/recursive/MergeSortTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.mergeSort.recursive;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import java.util.Arrays;
6 |
7 | import org.junit.Test;
8 |
9 |
10 | /**
11 | * Test cases for recursive {@link MergeSort}.
12 | */
13 | public class MergeSortTest {
14 |
15 | @Test
16 | public void test_insertionSort_shouldReturnSortedArray() {
17 | int[] firstArray =
18 | new int[] {
19 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7,
20 | 85, 30
21 | };
22 | int[] firstResult = Arrays.copyOf(firstArray, firstArray.length);
23 | MergeSort.sort(firstResult);
24 |
25 | int[] secondArray =
26 | new int[] {
27 | 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,
28 | 9, 10
29 | };
30 | int[] secondResult = Arrays.copyOf(secondArray, secondArray.length);
31 | MergeSort.sort(secondResult);
32 |
33 | int[] thirdArray = new int[] {};
34 | int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length);
35 | MergeSort.sort(thirdResult);
36 |
37 | int[] fourthArray = new int[] {1};
38 | int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length);
39 | MergeSort.sort(fourthResult);
40 |
41 | int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0};
42 | int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length);
43 | MergeSort.sort(fifthResult);
44 |
45 | Arrays.sort(firstArray); // get expected result
46 | Arrays.sort(secondArray); // get expected result
47 | Arrays.sort(thirdArray); // get expected result
48 | Arrays.sort(fourthArray); // get expected result
49 | Arrays.sort(fifthArray); // get expected result
50 |
51 | assertArrayEquals(firstResult, firstArray);
52 | assertArrayEquals(secondResult, secondArray);
53 | assertArrayEquals(thirdResult, thirdArray);
54 | assertArrayEquals(fourthResult, fourthArray);
55 | assertArrayEquals(fifthResult, fifthArray);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/sorting/quickSort/hoares/QuickSortTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.quickSort.hoares;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import java.util.Arrays;
6 |
7 | import org.junit.Test;
8 |
9 | /**
10 | * Test cases for {@link QuickSort} implemented using Hoares.
11 | */
12 | public class QuickSortTest {
13 |
14 | @Test
15 | public void test_selectionSort_shouldReturnSortedArray() {
16 | int[] firstArray =
17 | new int[] {22, 1, 6, 40, 32, 10, 18, 4, 50};
18 | int[] firstResult = Arrays.copyOf(firstArray, firstArray.length);
19 | QuickSort.sort(firstResult);
20 |
21 | int[] secondArray =
22 | new int[] {
23 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7,
24 | 85, 30
25 | };
26 | int[] secondResult = Arrays.copyOf(secondArray, secondArray.length);
27 | QuickSort.sort(secondResult);
28 |
29 | int[] thirdArray =
30 | new int[] {
31 | 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,
32 | 9, 10
33 | };
34 | int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length);
35 | QuickSort.sort(thirdResult);
36 |
37 | int[] fourthArray = new int[] {};
38 | int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length);
39 | QuickSort.sort(fourthResult);
40 |
41 | int[] fifthArray = new int[] {1};
42 | int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length);
43 | QuickSort.sort(fifthResult);
44 |
45 | int[] sixthArray = new int[] {5, 1, 1, 2, 0, 0};
46 | int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length);
47 | QuickSort.sort(sixthResult);
48 |
49 | int[] seventhArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
50 | int[] seventhResult = Arrays.copyOf(seventhArray, seventhArray.length);
51 | QuickSort.sort(seventhResult);
52 |
53 | int[] eighthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
54 | int[] eighthResult = Arrays.copyOf(eighthArray, eighthArray.length);
55 | QuickSort.sort(eighthResult);
56 |
57 | Arrays.sort(firstArray); // get expected result
58 | Arrays.sort(secondArray); // get expected result
59 | Arrays.sort(thirdArray); // get expected result
60 | Arrays.sort(fourthArray); // get expected result
61 | Arrays.sort(fifthArray); // get expected result
62 | Arrays.sort(sixthArray); // get expected result
63 | Arrays.sort(seventhArray); // get expected result
64 | Arrays.sort(eighthArray); // get expected result
65 |
66 | assertArrayEquals(firstResult, firstArray);
67 | assertArrayEquals(secondResult, secondArray);
68 | assertArrayEquals(thirdResult, thirdArray);
69 | assertArrayEquals(fourthResult, fourthArray);
70 | assertArrayEquals(fifthResult, fifthArray);
71 | assertArrayEquals(sixthResult, sixthArray);
72 | assertArrayEquals(seventhResult, seventhArray);
73 | assertArrayEquals(eighthResult, eighthArray);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/sorting/quickSort/lomuto/QuickSortTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.quickSort.lomuto;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import java.util.Arrays;
6 |
7 | import org.junit.Test;
8 |
9 | /**
10 | * Test cases for Lomuto {@link QuickSort}.
11 | */
12 | public class QuickSortTest {
13 |
14 | @Test
15 | public void test_selectionSort_shouldReturnSortedArray() {
16 | int[] firstArray =
17 | new int[] {
18 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7,
19 | 85, 30
20 | };
21 | int[] firstResult = Arrays.copyOf(firstArray, firstArray.length);
22 | QuickSort.sort(firstResult);
23 |
24 | int[] secondArray =
25 | new int[] {
26 | 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,
27 | 9, 10
28 | };
29 | int[] secondResult = Arrays.copyOf(secondArray, secondArray.length);
30 | QuickSort.sort(secondResult);
31 |
32 | int[] thirdArray = new int[] {};
33 | int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length);
34 | QuickSort.sort(thirdResult);
35 |
36 | int[] fourthArray = new int[] {1};
37 | int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length);
38 | QuickSort.sort(fourthResult);
39 |
40 | int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0};
41 | int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length);
42 | QuickSort.sort(fifthResult);
43 |
44 | int[] sixthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
45 | int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length);
46 | QuickSort.sort(sixthResult);
47 |
48 | Arrays.sort(firstArray); // get expected result
49 | Arrays.sort(secondArray); // get expected result
50 | Arrays.sort(thirdArray); // get expected result
51 | Arrays.sort(fourthArray); // get expected result
52 | Arrays.sort(fifthArray); // get expected result
53 | Arrays.sort(sixthArray); // get expected result
54 |
55 | assertArrayEquals(firstResult, firstArray);
56 | assertArrayEquals(secondResult, secondArray);
57 | assertArrayEquals(thirdResult, thirdArray);
58 | assertArrayEquals(fourthResult, fourthArray);
59 | assertArrayEquals(fifthResult, fifthArray);
60 | assertArrayEquals(sixthResult, sixthArray);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/sorting/quickSort/paranoid/QuickSortTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.quickSort.paranoid;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import java.util.Arrays;
6 |
7 | import org.junit.Test;
8 |
9 | /**
10 | * Test cases for paranoid {@link QuickSort}.
11 | */
12 | public class QuickSortTest {
13 |
14 | @Test
15 | public void test_selectionSort_shouldReturnSortedArray() {
16 | int[] firstArray =
17 | new int[] {
18 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7,
19 | 85, 30
20 | };
21 | int[] firstResult = Arrays.copyOf(firstArray, firstArray.length);
22 | QuickSort.sort(firstResult);
23 |
24 | int[] secondArray =
25 | new int[] {
26 | 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,
27 | 9, 10
28 | };
29 | int[] secondResult = Arrays.copyOf(secondArray, secondArray.length);
30 | QuickSort.sort(secondResult);
31 |
32 | int[] thirdArray = new int[] {};
33 | int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length);
34 | QuickSort.sort(thirdResult);
35 |
36 | int[] fourthArray = new int[] {1};
37 | int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length);
38 | QuickSort.sort(fourthResult);
39 |
40 | int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0};
41 | int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length);
42 | QuickSort.sort(fifthResult);
43 |
44 | int[] sixthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
45 | int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length);
46 | // testing for duplicate arrays of length >= 10, stack overflow is expected to happen
47 | try {
48 | QuickSort.sort(sixthResult);
49 | } catch (StackOverflowError e) {
50 | System.out.println("Stack overflow occurred for sixthResult");
51 | }
52 |
53 | Arrays.sort(firstArray); // get expected result
54 | Arrays.sort(secondArray); // get expected result
55 | Arrays.sort(thirdArray); // get expected result
56 | Arrays.sort(fourthArray); // get expected result
57 | Arrays.sort(fifthArray); // get expected result
58 |
59 | assertArrayEquals(firstResult, firstArray);
60 | assertArrayEquals(secondResult, secondArray);
61 | assertArrayEquals(thirdResult, thirdArray);
62 | assertArrayEquals(fourthResult, fourthArray);
63 | assertArrayEquals(fifthResult, fifthArray);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/sorting/quickSort/threeWayPartitioning/QuickSortTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.quickSort.threeWayPartitioning;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import java.util.Arrays;
6 |
7 | import org.junit.Test;
8 |
9 | /**
10 | * Test cases for {@link QuickSort} implemented using three way partitioning.
11 | */
12 | public class QuickSortTest {
13 |
14 | @Test
15 | public void test_selectionSort_shouldReturnSortedArray() {
16 | int[] firstArray =
17 | new int[] {
18 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7,
19 | 85, 30
20 | };
21 | int[] firstResult = Arrays.copyOf(firstArray, firstArray.length);
22 | QuickSort.sort(firstResult);
23 |
24 | int[] secondArray =
25 | new int[] {
26 | 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,
27 | 9, 10
28 | };
29 | int[] secondResult = Arrays.copyOf(secondArray, secondArray.length);
30 | QuickSort.sort(secondResult);
31 |
32 | int[] thirdArray = new int[] {};
33 | int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length);
34 | QuickSort.sort(thirdResult);
35 |
36 | int[] fourthArray = new int[] {1};
37 | int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length);
38 | QuickSort.sort(fourthResult);
39 |
40 | int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0};
41 | int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length);
42 | QuickSort.sort(fifthResult);
43 |
44 | int[] sixthArray = new int[] {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
45 | int[] sixthResult = Arrays.copyOf(sixthArray, sixthArray.length);
46 | QuickSort.sort(sixthResult);
47 |
48 | Arrays.sort(firstArray); // get expected result
49 | Arrays.sort(secondArray); // get expected result
50 | Arrays.sort(thirdArray); // get expected result
51 | Arrays.sort(fourthArray); // get expected result
52 | Arrays.sort(fifthArray); // get expected result
53 | Arrays.sort(sixthArray); // get expected result
54 |
55 | assertArrayEquals(firstResult, firstArray);
56 | assertArrayEquals(secondResult, secondArray);
57 | assertArrayEquals(thirdResult, thirdArray);
58 | assertArrayEquals(fourthResult, fourthArray);
59 | assertArrayEquals(fifthResult, fifthArray);
60 | assertArrayEquals(sixthResult, sixthArray);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/sorting/radixSort/RadixSortTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.radixSort;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import java.util.Arrays;
6 |
7 | import org.junit.Test;
8 |
9 | public class RadixSortTest {
10 | @Test
11 | public void test_radixSort_shouldReturnSortedArray() {
12 | int[] firstArray = new int[] {
13 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2,
14 | 15, 12, 20, 21, 120, 11, 5, 7, 85, 30
15 | };
16 | int[] firstResult = Arrays.copyOf(firstArray, firstArray.length);
17 | RadixSort.radixSort(firstResult);
18 |
19 | int[] secondArray = new int[] {
20 | 9, 1, 2, 8, 7, 3, 4, 6, 5, 5, 9, 8, 7, 6, 5, 4,
21 | 3, 2, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
22 | };
23 | int[] secondResult = Arrays.copyOf(secondArray, secondArray.length);
24 | RadixSort.radixSort(secondResult);
25 |
26 | int[] thirdArray = new int[] {};
27 | int[] thirdResult = Arrays.copyOf(thirdArray, thirdArray.length);
28 | RadixSort.radixSort(thirdResult);
29 |
30 | int[] fourthArray = new int[] {1};
31 | int[] fourthResult = Arrays.copyOf(fourthArray, fourthArray.length);
32 | RadixSort.radixSort(fourthResult);
33 |
34 | int[] fifthArray =
35 | new int[] {157394, 93495939, 495839239, 485384, 38439958, 3948585, 39585939, 6000999, 111111111, 98162};
36 | int[] fifthResult = Arrays.copyOf(fifthArray, fifthArray.length);
37 | RadixSort.radixSort(fifthResult);
38 |
39 | Arrays.sort(firstArray);
40 | Arrays.sort(secondArray);
41 | Arrays.sort(thirdArray);
42 | Arrays.sort(fourthArray);
43 | Arrays.sort(fifthArray);
44 |
45 | assertArrayEquals(firstResult, firstArray);
46 | assertArrayEquals(secondResult, secondArray);
47 | assertArrayEquals(thirdResult, thirdArray);
48 | assertArrayEquals(fourthResult, fourthArray);
49 | assertArrayEquals(fifthResult, fifthArray);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/java/algorithms/sorting/selectionSort/SelectionSortTest.java:
--------------------------------------------------------------------------------
1 | package algorithms.sorting.selectionSort;
2 |
3 | import static org.junit.Assert.assertArrayEquals;
4 |
5 | import java.util.Arrays;
6 |
7 | import org.junit.Test;
8 |
9 | /**
10 | * Test cases for {@link SelectionSort}.
11 | */
12 | public class SelectionSortTest {
13 |
14 | @Test
15 | public void test_selectionSort_shouldReturnSortedArray() {
16 | int[] firstArray =
17 | new int[] {
18 | 2, 3, 4, 1, 2, 5, 6, 7, 10, 15, 20, 13, 15, 1, 2, 15, 12, 20, 21, 120, 11, 5, 7,
19 | 85, 30
20 | };
21 | int[] firstResult = SelectionSort.sort(Arrays.copyOf(firstArray, firstArray.length));
22 |
23 | int[] secondArray =
24 | new int[] {
25 | 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,
26 | 9, 10
27 | };
28 | int[] secondResult = SelectionSort.sort(Arrays.copyOf(secondArray, secondArray.length));
29 |
30 | int[] thirdArray = new int[] {};
31 | int[] thirdResult = SelectionSort.sort(Arrays.copyOf(thirdArray, thirdArray.length));
32 |
33 | int[] fourthArray = new int[] {1};
34 | int[] fourthResult = SelectionSort.sort(Arrays.copyOf(fourthArray, fourthArray.length));
35 |
36 | int[] fifthArray = new int[] {5, 1, 1, 2, 0, 0};
37 | int[] fifthResult = SelectionSort.sort(Arrays.copyOf(fifthArray, fifthArray.length));
38 |
39 | Arrays.sort(firstArray); // get expected result
40 | Arrays.sort(secondArray); // get expected result
41 | Arrays.sort(thirdArray); // get expected result
42 | Arrays.sort(fourthArray); // get expected result
43 | Arrays.sort(fifthArray); // get expected result
44 |
45 | assertArrayEquals(firstResult, firstArray);
46 | assertArrayEquals(secondResult, secondArray);
47 | assertArrayEquals(thirdResult, thirdArray);
48 | assertArrayEquals(fourthResult, fourthArray);
49 | assertArrayEquals(fifthResult, fifthArray);
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/java/dataStructures/bTree/BTreeTest.java:
--------------------------------------------------------------------------------
1 | package dataStructures.bTree;
2 | import static org.junit.Assert.assertArrayEquals;
3 | import static org.junit.Assert.assertEquals;
4 |
5 | import org.junit.Test;
6 |
7 | public class BTreeTest {
8 | @Test
9 | public void testBTree() {
10 | BTree bTree = new BTree(3); // Specify the order of the B-tree
11 |
12 | int[] keys = {3, 7, 1, 8, 5, 12, 2, 9, 6, 10, 11, 4};
13 | for (int key : keys) {
14 | bTree.insert(key);
15 | }
16 |
17 | Object[] expectedLevelOrderTraversal1 = {5, 8, 1, 2, 3, 4, 6, 7, 9, 10, 11, 12};
18 | Object[] expectedPreOrderTraversal1 = {5, 8, 1, 2, 3, 4, 6, 7, 9, 10, 11, 12};
19 | assertArrayEquals(bTree.levelOrderTraversal(), expectedLevelOrderTraversal1);
20 | assertArrayEquals(bTree.preOrderTraversal(), expectedPreOrderTraversal1);
21 |
22 | assertEquals(true, bTree.search(8));
23 | assertEquals(true, bTree.search(4));
24 | assertEquals(true, bTree.search(7));
25 | assertEquals(false, bTree.search(13));
26 | assertEquals(false, bTree.search(0));
27 |
28 | bTree.delete(7);
29 | assertEquals(false, bTree.search(7));
30 |
31 | Object[] expectedLevelOrderTraversal2 = {4, 8, 1, 2, 3, 5, 6, 9, 10, 11, 12};
32 | Object[] expectedPreOrderTraversal2 = {4, 8, 1, 2, 3, 5, 6, 9, 10, 11, 12};
33 | assertArrayEquals(bTree.levelOrderTraversal(), expectedLevelOrderTraversal2);
34 | assertArrayEquals(bTree.preOrderTraversal(), expectedPreOrderTraversal2);
35 |
36 | bTree.delete(4);
37 | assertEquals(false, bTree.search(4));
38 |
39 | Object[] expectedLevelOrderTraversal3 = {3, 8, 1, 2, 5, 6, 9, 10, 11, 12};
40 | Object[] expectedPreOrderTraversal3 = {3, 8, 1, 2, 5, 6, 9, 10, 11, 12};
41 | assertArrayEquals(bTree.levelOrderTraversal(), expectedLevelOrderTraversal3);
42 | assertArrayEquals(bTree.preOrderTraversal(), expectedPreOrderTraversal3);
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/src/test/java/dataStructures/binarySearchTree/BinarySearchTreeTest.java:
--------------------------------------------------------------------------------
1 | package dataStructures.binarySearchTree;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | import org.junit.Test;
7 |
8 | public class BinarySearchTreeTest {
9 | @Test
10 | public void insert_shouldCorrectlyInsertNodes() {
11 | BinarySearchTree bst = new BinarySearchTree<>();
12 | bst.insert(5, "Five");
13 | bst.insert(3, "Three");
14 | bst.insert(7, "Seven");
15 | bst.insert(2, "Two");
16 | bst.insert(4, "Four");
17 |
18 | // Perform in-order traversal and get result
19 | List result = bst.getInorder();
20 |
21 | // Expected in-order traversal result
22 | List expected =
23 | Arrays.asList("Key: 2, Value: Two", "Key: 3, Value: Three", "Key: 4, Value: Four", "Key: 5, Value: Five",
24 | "Key: 7, Value: Seven"
25 | );
26 |
27 | assert result.equals(expected);
28 | }
29 |
30 | @Test
31 | public void delete_shouldCorrectlyHandleLeafNode() {
32 | BinarySearchTree bst = new BinarySearchTree<>();
33 | bst.insert(5, "Five");
34 | bst.insert(3, "Three");
35 | bst.insert(7, "Seven");
36 |
37 | bst.delete(7);
38 |
39 | // Perform in-order traversal and get result
40 | List result = bst.getInorder();
41 |
42 | // Expected in-order traversal result after deletion
43 | List expected = Arrays.asList("Key: 3, Value: Three", "Key: 5, Value: Five");
44 |
45 | assert result.equals(expected);
46 | }
47 |
48 | @Test
49 | public void delete_shouldCorrectlyHandleNodeWithOneChild() {
50 | BinarySearchTree bst = new BinarySearchTree<>();
51 | bst.insert(5, "Five");
52 | bst.insert(3, "Three");
53 | bst.insert(6, "Six");
54 | bst.insert(7, "Seven");
55 |
56 | bst.delete(6);
57 |
58 | // Perform in-order traversal and get result
59 | List result = bst.getInorder();
60 |
61 | // Expected in-order traversal result after deletion
62 | List expected = Arrays.asList("Key: 3, Value: Three", "Key: 5, Value: Five", "Key: 7, Value: Seven");
63 |
64 | assert result.equals(expected);
65 | }
66 |
67 | @Test
68 | public void delete_shouldCorrectlyHandleNodeWithTwoChildren() {
69 | BinarySearchTree bst = new BinarySearchTree<>();
70 | bst.insert(5, "Five");
71 | bst.insert(3, "Three");
72 | bst.insert(7, "Seven");
73 | bst.insert(2, "Two");
74 | bst.insert(4, "Four");
75 |
76 | bst.delete(3);
77 |
78 | // Perform in-order traversal and get result
79 | List result = bst.getInorder();
80 |
81 | // Expected in-order traversal result after deletion
82 | List expected =
83 | Arrays.asList("Key: 2, Value: Two", "Key: 4, Value: Four", "Key: 5, Value: Five", "Key: 7, Value: Seven");
84 |
85 | assert result.equals(expected);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/test/java/dataStructures/disjointSet/quickFind/DisjointSetTest.java:
--------------------------------------------------------------------------------
1 | package dataStructures.disjointSet.quickFind;
2 |
3 | import java.util.Arrays;
4 | import java.util.Collections;
5 | import java.util.List;
6 |
7 | import org.junit.Assert;
8 | import org.junit.Test;
9 |
10 | public class DisjointSetTest {
11 | @Test
12 | public void construct_shouldCorrectlyInitializeEmpty() {
13 | DisjointSet ds = new DisjointSet<>();
14 | Assert.assertEquals(ds.size(), 0);
15 | }
16 |
17 | @Test
18 | public void construct_shouldCorrectlyInitializeNonEmpty() {
19 | List lst = Arrays.asList("andre", "chang xian", "jun neng", "kai ting", "shu heng");
20 |
21 | DisjointSet ds = new DisjointSet<>(lst);
22 | Assert.assertEquals(ds.size(), 5);
23 |
24 | Assert.assertFalse(ds.find("andre", "kai ting"));
25 | }
26 |
27 | @Test
28 | public void construct_shouldCorrectlyInitializeNonEmptyArray() {
29 | String[] lst = new String[] { "andre", "chang xian", "jun neng", "kai ting", "shu heng" };
30 |
31 | DisjointSet ds = new DisjointSet<>(lst);
32 | Assert.assertEquals(ds.size(), 5);
33 |
34 | Assert.assertFalse(ds.find("andre", "kai ting"));
35 | }
36 |
37 | @Test
38 | public void find_shouldCorrectlyFindItself() {
39 | List lst = Arrays.asList("andre", "chang xian", "jun neng");
40 |
41 | DisjointSet ds = new DisjointSet<>(lst);
42 | Assert.assertTrue(ds.find("chang xian", "chang xian"));
43 | }
44 |
45 | @Test
46 | public void union_shouldCorrectlyUpdate() {
47 | List lst = Arrays.asList("andre", "chang xian", "jun neng", "kai ting", "shu heng");
48 |
49 | DisjointSet ds = new DisjointSet<>(lst);
50 |
51 | Assert.assertFalse(ds.find("andre", "kai ting"));
52 |
53 | ds.union("andre", "kai ting");
54 | Assert.assertTrue(ds.find("andre", "kai ting"));
55 | Assert.assertFalse(ds.find("andre", "chang xian"));
56 | Assert.assertFalse(ds.find("andre", "shu heng"));
57 | Assert.assertFalse(ds.find("jun neng", "kai ting"));
58 | }
59 |
60 | @Test
61 | public void retrieve_shouldCorrectlyRetrieveComponents() {
62 | List lst = Arrays.asList("andre", "chang xian", "jun neng", "kai ting", "shu heng", "seth", "gilbert");
63 |
64 | DisjointSet ds = new DisjointSet<>(lst);
65 | ds.union("andre", "kai ting");
66 | ds.union("chang xian", "jun neng");
67 | ds.union("jun neng", "gilbert");
68 | ds.union("chang xian", "seth");
69 |
70 | List resultA = ds.retrieveFromSameComponent("kai ting");
71 | Collections.sort(resultA);
72 | List expectedA = Arrays.asList("andre", "kai ting");
73 | Collections.sort(expectedA);
74 | Assert.assertEquals(expectedA, resultA);
75 |
76 | List resultB = ds.retrieveFromSameComponent("gilbert");
77 | Collections.sort(resultB);
78 | List expectedB = Arrays.asList("chang xian", "jun neng", "seth", "gilbert");
79 | Collections.sort(expectedB);
80 | Assert.assertEquals(expectedB, resultB);
81 |
82 | List resultC = ds.retrieveFromSameComponent("shu heng");
83 | Collections.sort(resultC);
84 | List expectedC = Arrays.asList("shu heng");
85 | Collections.sort(expectedC);
86 | Assert.assertEquals(expectedC, resultC);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/test/java/dataStructures/disjointSet/weightedUnion/DisjointSetTest.java:
--------------------------------------------------------------------------------
1 | package dataStructures.disjointSet.weightedUnion;
2 |
3 | import java.util.Arrays;
4 | import java.util.Collections;
5 | import java.util.List;
6 |
7 | import org.junit.Assert;
8 | import org.junit.Test;
9 |
10 | public class DisjointSetTest {
11 | @Test
12 | public void construct_shouldCorrectlyInitializeEmpty() {
13 | DisjointSet ds = new DisjointSet<>();
14 | Assert.assertEquals(ds.size(), 0);
15 | }
16 |
17 | @Test
18 | public void construct_shouldCorrectlyInitializeNonEmpty() {
19 | List lst = Arrays.asList("andre", "chang xian", "jun neng", "kai ting", "shu heng");
20 |
21 | DisjointSet ds = new DisjointSet<>(lst);
22 | Assert.assertEquals(ds.size(), 5);
23 |
24 | Assert.assertFalse(ds.find("andre", "kai ting"));
25 | }
26 |
27 | @Test
28 | public void construct_shouldCorrectlyInitializeNonEmptyArray() {
29 | String[] lst = new String[] { "andre", "chang xian", "jun neng", "kai ting", "shu heng" };
30 |
31 | DisjointSet ds = new DisjointSet<>(lst);
32 | Assert.assertEquals(ds.size(), 5);
33 |
34 | Assert.assertFalse(ds.find("andre", "kai ting"));
35 | }
36 |
37 | @Test
38 | public void find_shouldCorrectlyFindItself() {
39 | List lst = Arrays.asList("andre", "chang xian", "jun neng");
40 |
41 | DisjointSet ds = new DisjointSet<>(lst);
42 | Assert.assertTrue(ds.find("chang xian", "chang xian"));
43 | }
44 |
45 | @Test
46 | public void union_shouldCorrectlyUpdate() {
47 | List lst = Arrays.asList("andre", "chang xian", "jun neng", "kai ting", "shu heng");
48 |
49 | DisjointSet ds = new DisjointSet<>(lst);
50 |
51 | Assert.assertFalse(ds.find("andre", "kai ting"));
52 |
53 | ds.union("andre", "kai ting");
54 | Assert.assertTrue(ds.find("andre", "kai ting"));
55 | Assert.assertFalse(ds.find("andre", "chang xian"));
56 | Assert.assertFalse(ds.find("andre", "shu heng"));
57 | Assert.assertFalse(ds.find("jun neng", "kai ting"));
58 | }
59 |
60 | @Test
61 | public void retrieve_shouldCorrectlyRetrieveComponents() {
62 | List lst = Arrays.asList("andre", "chang xian", "jun neng", "kai ting", "shu heng", "seth", "gilbert");
63 |
64 | DisjointSet ds = new DisjointSet<>(lst);
65 | ds.union("andre", "kai ting");
66 | ds.union("chang xian", "jun neng");
67 | ds.union("jun neng", "gilbert");
68 | ds.union("chang xian", "seth");
69 |
70 | List resultA = ds.retrieveFromSameComponent("kai ting");
71 | Collections.sort(resultA);
72 | List expectedA = Arrays.asList("andre", "kai ting");
73 | Collections.sort(expectedA);
74 | Assert.assertEquals(expectedA, resultA);
75 |
76 | List resultB = ds.retrieveFromSameComponent("gilbert");
77 | Collections.sort(resultB);
78 | List expectedB = Arrays.asList("chang xian", "jun neng", "seth", "gilbert");
79 | Collections.sort(expectedB);
80 | Assert.assertEquals(expectedB, resultB);
81 |
82 | List resultC = ds.retrieveFromSameComponent("shu heng");
83 | Collections.sort(resultC);
84 | List expectedC = Arrays.asList("shu heng");
85 | Collections.sort(expectedC);
86 | Assert.assertEquals(expectedC, resultC);
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/src/test/java/dataStructures/hashSet/chaining/HashSetTest.java:
--------------------------------------------------------------------------------
1 | package dataStructures.hashSet.chaining;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertFalse;
5 | import static org.junit.Assert.assertTrue;
6 |
7 | import java.util.List;
8 | import java.util.stream.Collectors;
9 | import java.util.stream.Stream;
10 |
11 | import org.junit.Test;
12 |
13 | /**
14 | * Test cases for {@link HashSet} implemented using Chaining to resolve collisions.
15 | */
16 | public class HashSetTest {
17 | @Test
18 | public void testAdd_noDuplicates_shouldReturnTrue() {
19 | HashSet hashSet = new HashSet<>();
20 | assertTrue(hashSet.add("Hello"));
21 | assertTrue(hashSet.add("World"));
22 | }
23 |
24 | @Test
25 | public void testAdd_withDuplicates_shouldReturnFalse() {
26 | HashSet hashSet = new HashSet<>();
27 | assertTrue(hashSet.add("Hello"));
28 | assertTrue(hashSet.add("World"));
29 | assertFalse(hashSet.add("Hello")); // Adding duplicate element should return false
30 | }
31 |
32 | @Test
33 | public void testContains() {
34 | HashSet hashSet = new HashSet<>();
35 | hashSet.add("Hello");
36 | hashSet.add("World");
37 | assertTrue(hashSet.contains("Hello"));
38 | assertTrue(hashSet.contains("World"));
39 | assertFalse(hashSet.contains("Universe")); // Element not in set
40 | }
41 |
42 | @Test
43 | public void testRemove() {
44 | HashSet hashSet = new HashSet<>();
45 | hashSet.add("Hello");
46 | hashSet.add("World");
47 | assertTrue(hashSet.remove("Hello"));
48 | assertFalse(hashSet.contains("Hello")); // Element should be removed
49 | assertFalse(hashSet.remove("Universe")); // Removing non-existent element should return false
50 | }
51 |
52 | @Test
53 | public void testSize() {
54 | HashSet hashSet = new HashSet<>();
55 | assertEquals(0, hashSet.size()); // Initial size should be 0
56 | hashSet.add("Hello");
57 | assertEquals(1, hashSet.size()); // Size after adding one element
58 | hashSet.add("World");
59 | assertEquals(2, hashSet.size()); // Size after adding two elements
60 | hashSet.remove("Hello");
61 | assertEquals(1, hashSet.size()); // Size after removing one element
62 | }
63 |
64 | @Test
65 | public void testIsEmpty() {
66 | HashSet hashSet = new HashSet<>();
67 | assertTrue(hashSet.isEmpty()); // Initial set should be empty
68 | hashSet.add("Hello");
69 | assertFalse(hashSet.isEmpty()); // Set should not be empty after adding an element
70 | hashSet.remove("Hello");
71 | assertTrue(hashSet.isEmpty()); // Set should be empty after removing the only element
72 | }
73 |
74 | @Test
75 | public void test_hashSet_shouldNotHaveDuplicates() {
76 | HashSet hashSet = new HashSet<>();
77 | hashSet.add("Hello");
78 | hashSet.add("World");
79 | hashSet.add("Hello");
80 | hashSet.add("World");
81 | List firstList = Stream.of("Hello", "World").sorted().collect(Collectors.toList());
82 | List firstResult = hashSet.toList().stream().sorted().collect(Collectors.toList());
83 |
84 | assertEquals(firstList, firstResult);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/test/java/dataStructures/linkedList/LinkedListTest.java:
--------------------------------------------------------------------------------
1 | package dataStructures.linkedList;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 |
6 | import dataStructures.linkedList.LinkedList.Node;
7 |
8 | /**
9 | * Test cases for {@link LinkedList}.
10 | */
11 | public class LinkedListTest {
12 | @Test
13 | public void testInsert() {
14 | LinkedList ll = new LinkedList<>();
15 | Assert.assertNull(ll.toString());
16 |
17 | ll.insertFront(2);
18 | ll.insertEnd(3);
19 | ll.insertEnd(5);
20 | ll.insertFront(1);
21 | Assert.assertEquals("1 2 3 5 ", ll.toString());
22 |
23 | ll.insert(0, 0);
24 | ll.insert(4, 4);
25 | ll.insert(7, 6);
26 | ll.insert(6, 6);
27 | Assert.assertEquals("0 1 2 3 4 5 6 7 ", ll.toString());
28 | }
29 |
30 | @Test
31 | public void testSearchAndGet() {
32 | LinkedList ll = new LinkedList<>();
33 | ll.insertFront(2);
34 | ll.insertEnd(3);
35 | ll.insertEnd(5);
36 | ll.insertFront(1);
37 | ll.insert(0, 0);
38 | ll.insert(4, 4);
39 | ll.insert(7, 6);
40 | ll.insert(6, 6);
41 |
42 | Node test1 = ll.get(4);
43 | Assert.assertEquals("4", test1.toString());
44 |
45 | Node test2 = ll.get(3);
46 | Assert.assertEquals("3", test2.toString());
47 |
48 | Integer test3 = ll.search(4);
49 | Assert.assertEquals("4", test3.toString());
50 |
51 | Integer test4 = ll.search(3);
52 | Assert.assertEquals("3", test4.toString());
53 |
54 | Assert.assertEquals("0 1 2 3 4 5 6 7 ", ll.toString());
55 | }
56 |
57 | @Test
58 | public void testRemove() {
59 |
60 | LinkedList ll = new LinkedList<>();
61 | ll.insertFront(2);
62 | ll.insertEnd(3);
63 | ll.insertEnd(5);
64 | ll.insertFront(1);
65 | ll.insert(0, 0);
66 | ll.insert(4, 4);
67 | ll.insert(7, 6);
68 | ll.insert(6, 6);
69 |
70 | ll.remove(5);
71 | ll.delete(6);
72 | ll.pop();
73 | ll.poll();
74 |
75 | String expected = "1 2 3 4 ";
76 | Assert.assertEquals(expected, ll.toString());
77 | }
78 |
79 | @Test
80 | public void testReverse() {
81 | LinkedList ll = new LinkedList<>();
82 | ll.reverse();
83 | Assert.assertNull(ll.toString());
84 |
85 | ll.insertFront(3);
86 | ll.reverse();
87 | Assert.assertEquals("3 ", ll.toString());
88 |
89 | ll.insertFront(2);
90 | ll.insertFront(1);
91 | ll.insertEnd(4);
92 | ll.reverse();
93 | Assert.assertEquals("4 3 2 1 ", ll.toString());
94 | }
95 |
96 | @Test
97 | public void testSort() {
98 | LinkedList ll = new LinkedList<>();
99 | ll.insertFront(5);
100 | ll.insertEnd(2);
101 | ll.insertFront(7);
102 | ll.insertEnd(3);
103 | ll.insertEnd(4);
104 | ll.insertEnd(3);
105 | ll.sort();
106 |
107 | Assert.assertEquals("2 3 3 4 5 7 ", ll.toString());
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/test/java/dataStructures/queue/MonotonicQueueTest.java:
--------------------------------------------------------------------------------
1 | package dataStructures.queue;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 |
6 | /**
7 | * This class implements tests for the monotonic queue.
8 | */
9 | public class MonotonicQueueTest {
10 | @Test
11 | public void testEmpty() {
12 | MonotonicQueue q = new MonotonicQueue<>();
13 | Assert.assertTrue(q.isEmpty());
14 | Assert.assertNull(q.max());
15 | Assert.assertNull(q.pop());
16 | }
17 |
18 | @Test
19 | public void testMax() {
20 | MonotonicQueue q = new MonotonicQueue<>();
21 | q.push(2);
22 | Assert.assertEquals("2", q.max().toString());
23 | q.push(7);
24 | Assert.assertEquals("7", q.max().toString());
25 | q.push(1);
26 | Assert.assertEquals("7", q.max().toString());
27 | q.push(7);
28 | Assert.assertEquals("7", q.max().toString());
29 | q.push(5);
30 | Assert.assertEquals("7", q.max().toString());
31 | q.push(4);
32 | Assert.assertEquals("7", q.max().toString());
33 | q.push(3);
34 | q.push(2);
35 | q.push(5);
36 | Assert.assertEquals("7", q.max().toString());
37 | }
38 |
39 | @Test
40 | public void testPop() {
41 | MonotonicQueue q = new MonotonicQueue<>();
42 | q.push(2);
43 | q.push(7);
44 | q.push(1);
45 | q.push(7);
46 | q.push(5);
47 | q.push(4);
48 | q.push(3);
49 | q.push(2);
50 | q.push(5);
51 | q.push(2);
52 |
53 | Assert.assertEquals("7", q.pop().toString());
54 | Assert.assertEquals("7", q.pop().toString());
55 | Assert.assertEquals("5", q.pop().toString());
56 | q.pop();
57 | Assert.assertEquals("2", q.pop().toString());
58 | Assert.assertNull(q.pop());
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/test/java/dataStructures/segmentTree/SegmentTreeTest.java:
--------------------------------------------------------------------------------
1 | package dataStructures.segmentTree;
2 | import static org.junit.Assert.assertEquals;
3 |
4 | import org.junit.Test;
5 |
6 | public class SegmentTreeTest {
7 | @Test
8 | public void construct_shouldConstructSegmentTree() {
9 | int[] arr1 = new int[] {7, 77, 37, 67, 33, 73, 13, 2, 7, 17, 87, 53};
10 | SegmentTree tree1 = new SegmentTree(arr1);
11 | assertEquals(arr1[1] + arr1[2] + arr1[3], tree1.query(1, 3));
12 | assertEquals(arr1[4] + arr1[5] + arr1[6] + arr1[7], tree1.query(4, 7));
13 | int sum1 = 0;
14 | for (int i = 0; i < arr1.length; i++) {
15 | sum1 += arr1[i];
16 | }
17 | assertEquals(sum1, tree1.query(0, arr1.length - 1));
18 |
19 |
20 | int[] arr2 = new int[] {7, -77, 37, 67, -33, 0, 73, -13, 2, -7, 17, 0, -87, 53, 0}; // some negatives and 0s
21 | SegmentTree tree2 = new SegmentTree(arr1);
22 | assertEquals(arr1[1] + arr1[2] + arr1[3], tree2.query(1, 3));
23 | assertEquals(arr1[4] + arr1[5] + arr1[6] + arr1[7], tree2.query(4, 7));
24 | int sum2 = 0;
25 | for (int i = 0; i < arr1.length; i++) {
26 | sum2 += arr1[i];
27 | }
28 | assertEquals(sum2, tree2.query(0, arr1.length - 1));
29 | }
30 |
31 | @Test
32 | public void update_shouldUpdateSegmentTree() {
33 | int[] arr = new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
34 | SegmentTree tree = new SegmentTree(arr);
35 | assertEquals(55, tree.query(0, 10));
36 | tree.update(5, 55);
37 | assertEquals(105, tree.query(0, 10));
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/test/java/dataStructures/segmentTree/arrayRepresentation/SegmentTreeTest.java:
--------------------------------------------------------------------------------
1 | package dataStructures.segmentTree.arrayRepresentation;
2 | import static org.junit.Assert.assertEquals;
3 |
4 | import org.junit.Test;
5 |
6 | /**
7 | * This file is essentially duplicated from the parent.
8 | */
9 | public class SegmentTreeTest {
10 | @Test
11 | public void construct_shouldConstructSegmentTree() {
12 | int[] arr1 = new int[] {7, 77, 37, 67, 33, 73, 13, 2, 7, 17, 87, 53};
13 | SegmentTree tree1 = new SegmentTree(arr1);
14 | assertEquals(arr1[1] + arr1[2] + arr1[3], tree1.query(1, 3));
15 | assertEquals(arr1[4] + arr1[5] + arr1[6] + arr1[7], tree1.query(4, 7));
16 | int sum1 = 0;
17 | for (int i = 0; i < arr1.length; i++) {
18 | sum1 += arr1[i];
19 | }
20 | assertEquals(sum1, tree1.query(0, arr1.length - 1));
21 |
22 |
23 | int[] arr2 = new int[] {7, -77, 37, 67, -33, 0, 73, -13, 2, -7, 17, 0, -87, 53, 0}; // some negatives and 0s
24 | SegmentTree tree2 = new SegmentTree(arr1);
25 | assertEquals(arr1[1] + arr1[2] + arr1[3], tree2.query(1, 3));
26 | assertEquals(arr1[4] + arr1[5] + arr1[6] + arr1[7], tree2.query(4, 7));
27 | int sum2 = 0;
28 | for (int i = 0; i < arr1.length; i++) {
29 | sum2 += arr1[i];
30 | }
31 | assertEquals(sum2, tree2.query(0, arr1.length - 1));
32 | }
33 |
34 | @Test
35 | public void update_shouldUpdateSegmentTree() {
36 | int[] arr = new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
37 | SegmentTree tree = new SegmentTree(arr);
38 | assertEquals(55, tree.query(0, 10));
39 | tree.update(5, 55);
40 | assertEquals(105, tree.query(0, 10));
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/test/java/dataStructures/stack/StackTest.java:
--------------------------------------------------------------------------------
1 | package dataStructures.stack;
2 |
3 | import org.junit.Assert;
4 | import org.junit.Test;
5 |
6 | /**
7 | * This class implements the test for the stack.
8 | */
9 | public class StackTest {
10 | @Test
11 | public void testEmpty() {
12 | Stack stk = new Stack<>();
13 | Assert.assertNull(stk.pop());
14 | Assert.assertNull(stk.peek());
15 | }
16 |
17 | @Test
18 | public void testPopAndPeek() {
19 | Stack stk = new Stack<>(1, 2, 3);
20 | Assert.assertEquals("3", stk.peek().toString());
21 | Assert.assertEquals("3", stk.pop().toString());
22 | Assert.assertEquals("2", stk.peek().toString());
23 | }
24 |
25 | @Test
26 | public void testPush() {
27 | Stack stk = new Stack<>();
28 | stk.push(1);
29 | Assert.assertEquals("1", stk.peek().toString());
30 | stk.push(2);
31 | stk.push(3);
32 | Assert.assertEquals("3", stk.peek().toString());
33 | }
34 | }
35 |
--------------------------------------------------------------------------------