diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..6435d71
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,20 @@
+/*
+ * This file was generated by the Gradle 'init' task.
+ *
+ * The settings file is used to specify which projects to include in your build.
+ * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/8.5/userguide/building_swift_projects.html in the Gradle documentation.
+ * This project uses @Incubating APIs which are subject to change.
+ */
+
+pluginManagement {
+ repositories {
+ gradlePluginPortal()
+ }
+}
+
+plugins {
+ // Apply the foojay-resolver plugin to allow automatic download of JDKs
+ id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
+}
+
+rootProject.name = 'QuickStart-Challenges'
diff --git a/src/main/java/com/shortthirdman/quickstart/codesignal/CountDistinctSlices.java b/src/main/java/com/shortthirdman/quickstart/codesignal/CountDistinctSlices.java
new file mode 100644
index 0000000..cdeaeb3
--- /dev/null
+++ b/src/main/java/com/shortthirdman/quickstart/codesignal/CountDistinctSlices.java
@@ -0,0 +1,75 @@
+package com.shortthirdman.quickstart.codesignal;
+
+import java.util.Arrays;
+
+/**
+ * An integer M and a non-empty array A consisting of N non-negative integers are given.
+ * All integers in array A are less than or equal to M
.
+ * A pair of integers (P, Q)
, such that 0 ≤ P ≤ Q < N
, is called a slice of array A
.
+ *
+ * The slice consists of the elements A[P], A[P + 1], ..., A[Q]. A distinct slice is a slice consisting of only unique numbers. That is, no individual number occurs more than once in the slice.
+ *
+ * For example, consider integer M = 6 and array A such that:
+ *
+ * A[0] = 3
+ * A[1] = 4
+ * A[2] = 5
+ * A[3] = 5
+ * A[4] = 2
+ *
+ * There are exactly nine distinct slices:
+ * (0, 0), (0, 1), (0, 2), (1, 1), (1, 2), (2, 2), (3, 3), (3, 4) and (4, 4).
+ *
+ * The goal is to calculate the number of distinct slices.
+ *
+ * @author ShortThirdMan
+ */
+public class CountDistinctSlices {
+
+ /**
+ * Given an integer M and a non-empty array A consisting of N integers, returns the number of distinct slices.
+ *
+ * If the number of distinct slices is greater than 1,000,000,000, the function should return 1,000,000,000.
+ * @param M integer
+ * @param A non-empty array
+ * @return number of distinct slices
+ */
+ public int calculateDistinctSlices(int M, int[] A) {
+ int N = A.length;
+ if (N < 1 || N > 100000) {
+ throw new IllegalArgumentException("Array length should be between 1 and 100000");
+ }
+
+ if (M < 0 || M > 100000) {
+ throw new IllegalArgumentException("Integer M should be between 0 and 100000");
+ }
+
+ boolean isNotInRange = Arrays.stream(A).boxed().anyMatch(n -> n < 0 || n > M);
+ if (isNotInRange) {
+ throw new IllegalArgumentException("Each element in array should be between 0 and " + M);
+ }
+
+ int[] lastSeen = new int[M + 1]; // To track the last occurrence of each element
+ int left = 0;
+ long distinctSlices = 0;
+ final int MOD = 1_000_000_007;
+
+ // Traverse with the right pointer
+ for (int right = 0; right < N; right++) {
+ // If the current element has appeared before in the window, move 'left'
+ lastSeen[A[right]]++;
+
+ // Move the 'left' pointer until the slice [left, right] is distinct
+ while (lastSeen[A[right]] > 1) {
+ lastSeen[A[left]]--;
+ left++;
+ }
+
+ // Add the number of distinct slices ending at 'right'
+ distinctSlices = (distinctSlices + (right - left + 1)) % MOD;
+ }
+
+ // Return the result (casting to int)
+ return (int) distinctSlices;
+ }
+}
diff --git a/src/main/java/com/shortthirdman/quickstart/codility/BagOfFruits.java b/src/main/java/com/shortthirdman/quickstart/codility/BagOfFruits.java
new file mode 100644
index 0000000..bd5e893
--- /dev/null
+++ b/src/main/java/com/shortthirdman/quickstart/codility/BagOfFruits.java
@@ -0,0 +1,39 @@
+package com.shortthirdman.quickstart.codility;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class BagOfFruits {
+
+ public List countFruits(List fruits, long threshold) {
+ Map countingMap = fruits.stream()
+ .collect(Collectors.groupingBy(Function.identity(),
+ Collectors.counting()));
+
+ return countingMap.entrySet()
+ .stream()
+ .filter(a -> a.getValue() > threshold)
+ .map(Map.Entry::getKey)
+ .collect(Collectors.toList());
+ }
+
+ public List countFruits(List fruits, int threshold) {
+ Map countingMap = new HashMap<>();
+ for (String fruit : fruits) {
+ countingMap.put(fruit, countingMap.getOrDefault(fruit, 0) + 1);
+ }
+
+ List result = new ArrayList<>();
+ for (Map.Entry fruitCountMap : countingMap.entrySet()) {
+ if (fruitCountMap.getValue() > threshold) {
+ result.add(fruitCountMap.getKey());
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/src/main/java/com/shortthirdman/quickstart/codility/GenerateAllSubstrings.java b/src/main/java/com/shortthirdman/quickstart/codility/GenerateAllSubstrings.java
new file mode 100644
index 0000000..28f9ec5
--- /dev/null
+++ b/src/main/java/com/shortthirdman/quickstart/codility/GenerateAllSubstrings.java
@@ -0,0 +1,48 @@
+package com.shortthirdman.quickstart.codility;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class GenerateAllSubstrings {
+
+ /**
+ * @param inputString the input string
+ * @param curr the current string
+ * @param startPos the start position
+ * @param glRes the resultant list
+ * @return List of substrings
+ */
+ public List generateSubStrings(String inputString, String curr, int startPos, List glRes) {
+ if (startPos >= inputString.length()) {
+ List currList = new ArrayList<>();
+ currList.add(curr);
+ return currList;
+ }
+
+ List subStringChars = generateSubStrings(inputString, "" + inputString.charAt(startPos), startPos + 1, glRes);
+
+ List result = new ArrayList<>();
+ result.add("" + inputString.charAt(startPos));
+
+ if (startPos < inputString.length() - 1) {
+ for (String sub : subStringChars) {
+ result.add(inputString.charAt(startPos) + sub);
+ }
+ }
+
+ glRes.addAll(result);
+ return result;
+ }
+
+// public void generateSubstrings(String inputString, int startPos, List res){
+// if (startPos >= inputString.length()) {
+// return;
+// }
+//
+// for (int j = startPos; j < inputString.length(); j++) {
+// res.add(inputString.substring(startPos, j + 1));
+// }
+//
+// generateSubstrings(inputString, startPos + 1, res);
+// }
+}
diff --git a/src/main/java/com/shortthirdman/quickstart/common/Employee.java b/src/main/java/com/shortthirdman/quickstart/common/Employee.java
index f9bd37d..f7bed89 100644
--- a/src/main/java/com/shortthirdman/quickstart/common/Employee.java
+++ b/src/main/java/com/shortthirdman/quickstart/common/Employee.java
@@ -4,11 +4,9 @@
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
-import lombok.ToString;
@Data
@Builder
-@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
diff --git a/src/main/java/com/shortthirdman/quickstart/common/EmployeeRecord.java b/src/main/java/com/shortthirdman/quickstart/common/EmployeeRecord.java
new file mode 100644
index 0000000..cbaef34
--- /dev/null
+++ b/src/main/java/com/shortthirdman/quickstart/common/EmployeeRecord.java
@@ -0,0 +1,16 @@
+package com.shortthirdman.quickstart.common;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class EmployeeRecord {
+
+ private int id;
+ private String name;
+}
diff --git a/src/main/java/com/shortthirdman/quickstart/common/Pair.java b/src/main/java/com/shortthirdman/quickstart/common/Pair.java
new file mode 100644
index 0000000..bd1e515
--- /dev/null
+++ b/src/main/java/com/shortthirdman/quickstart/common/Pair.java
@@ -0,0 +1,17 @@
+package com.shortthirdman.quickstart.common;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class Pair {
+
+ private F first;
+ private S second;
+
+ public Pair(F first, S second) {
+ this.first = first;
+ this.second = second;
+ }
+}
diff --git a/src/main/java/com/shortthirdman/quickstart/common/Transaction.java b/src/main/java/com/shortthirdman/quickstart/common/Transaction.java
new file mode 100644
index 0000000..ae6cf5d
--- /dev/null
+++ b/src/main/java/com/shortthirdman/quickstart/common/Transaction.java
@@ -0,0 +1,16 @@
+package com.shortthirdman.quickstart.common;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class Transaction {
+
+ private String date;
+ private long amount;
+}
diff --git a/src/main/java/com/shortthirdman/quickstart/common/TreeNode.java b/src/main/java/com/shortthirdman/quickstart/common/TreeNode.java
new file mode 100644
index 0000000..9672177
--- /dev/null
+++ b/src/main/java/com/shortthirdman/quickstart/common/TreeNode.java
@@ -0,0 +1,26 @@
+package com.shortthirdman.quickstart.common;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class TreeNode {
+
+ public int value;
+ public TreeNode left;
+ public TreeNode right;
+
+ public TreeNode() {
+ }
+
+ public TreeNode(int value) {
+ this.value = value;
+ }
+
+ public TreeNode(int value, TreeNode left, TreeNode right) {
+ this.value = value;
+ this.left = left;
+ this.right = right;
+ }
+}
diff --git a/src/main/java/com/shortthirdman/quickstart/hackerrank/BinaryTree.java b/src/main/java/com/shortthirdman/quickstart/hackerrank/BinaryTree.java
new file mode 100644
index 0000000..7d8d00c
--- /dev/null
+++ b/src/main/java/com/shortthirdman/quickstart/hackerrank/BinaryTree.java
@@ -0,0 +1,196 @@
+package com.shortthirdman.quickstart.hackerrank;
+
+import com.shortthirdman.quickstart.common.TreeNode;
+
+import java.util.*;
+
+/**
+ * Binary Tree
+ * @author ShortThirdMan
+ */
+public class BinaryTree {
+
+ /**
+ * Given a binary tree, find its minimum depth.
+ *
+ * The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node.
+ * A leaf is a node with no children.
+ *
+ * @param root the node with its leaves
+ * @return the minimum depth
+ * @see Minimum Depth of Binary Tree
+ */
+ public int minDepth(TreeNode root) {
+
+ if (root == null) {
+ return 0; // Base case: if the tree is empty
+ }
+
+ // If the node is a leaf node
+ if (root.getLeft() == null && root.getRight() == null) {
+ return 1; // Count this node
+ }
+
+ // If the left child is null, we only consider the right child
+ if (root.getLeft() == null) {
+ return 1 + minDepth(root.getRight());
+ }
+
+ // If the right child is null, we only consider the left child
+ if (root.getRight() == null) {
+ return 1 + minDepth(root.getLeft());
+ }
+
+ // If both children are present, return the minimum depth of both subtrees
+ return 1 + Math.min(minDepth(root.getLeft()), minDepth(root.getRight()));
+ }
+
+ /**
+ * Given the root of a binary tree, return its maximum depth.
+ *
+ * A binary tree's maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.
+ *
+ * @param root the node with its leaves
+ * @return the maximum depth
+ * @see Minimum Depth of Binary Tree
+ */
+ public int maxDepth(TreeNode root) {
+ if (root == null) {
+ return 0;
+ }
+
+ // Calculate depth recursively for left and right subtrees
+ return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
+ }
+
+ /**
+ * Given a binary tree, determine if it is height-balanced.
+ *
+ * @param root the node with its leaves
+ * @return true if height is balanced, false otherwise
+ * @see Balanced Binary Tree
+ */
+ public boolean isBalanced(TreeNode root) {
+ if (root == null) {
+ return true; // An empty tree is balanced
+ }
+
+ // Stack to simulate recursion
+ Deque stack = new ArrayDeque<>();
+ // Map to store the height of each node
+ Map heights = new HashMap<>();
+
+ stack.push(root);
+
+ while (!stack.isEmpty()) {
+ TreeNode node = stack.peek();
+
+ // If the node has not been processed yet, push its children onto the stack
+ if (!heights.containsKey(node)) {
+ if (node.right != null) {
+ stack.push(node.right);
+ }
+ if (node.left != null) {
+ stack.push(node.left);
+ }
+ // Mark the node as processed by adding it to the heights map
+ heights.put(node, 0);
+ } else {
+ // If the node has been processed, calculate its height
+ stack.pop(); // Remove the node from the stack
+
+ int leftHeight = heights.getOrDefault(node.left, 0);
+ int rightHeight = heights.getOrDefault(node.right, 0);
+
+ // Check if the current node is balanced
+ if (Math.abs(leftHeight - rightHeight) > 1) {
+ return false; // Tree is unbalanced
+ }
+
+ // Calculate the height of the current node
+ int height = Math.max(leftHeight, rightHeight) + 1;
+ heights.put(node, height);
+ }
+ }
+
+ return true; // If all nodes are balanced
+ }
+
+ /**
+ * @param root the root tree node
+ * @return the depth of the tree
+ */
+ public int minimumDepth(TreeNode root) {
+ if (root == null) return 0;
+
+ Queue queue = new LinkedList<>();
+ queue.add(root);
+ int depth = 1; // Start with depth 1 since we start from the root
+
+ while (!queue.isEmpty()) {
+ int levelSize = queue.size();
+
+ for (int i = 0; i < levelSize; i++) {
+ TreeNode node = queue.poll();
+
+ // Check for null node (just in case)
+ if (node == null) continue;
+
+ // Check if we have reached a leaf node
+ if (node.getLeft() == null && node.getRight() == null) {
+ return depth; // Return the current depth when we find the first leaf
+ }
+
+ // Add children to the queue
+ if (node.left != null) {
+ queue.add(node.left);
+ }
+ if (node.right != null) {
+ queue.add(node.right);
+ }
+ }
+ depth++; // Increase the depth for the next level
+ }
+
+ return depth; // In case of an empty tree, though not reached due to checks
+ }
+
+ /**
+ * Given the root of a binary tree, check whether it is a mirror of itself (i.e., symmetric around its center).
+ *
+ * @param root the node with its leaves
+ * @return true if tree is symmetric, otherwise false
+ * @see Symmetric Tree
+ */
+ public boolean isSymmetric(TreeNode root) {
+ if (root == null) {
+ return true; // An empty tree is symmetric
+ }
+
+ Stack stack = new Stack<>();
+ stack.push(root.left);
+ stack.push(root.right);
+
+ while (!stack.isEmpty()) {
+ TreeNode right = stack.pop();
+ TreeNode left = stack.pop();
+
+ // Both nodes are null, continue checking
+ if (left == null && right == null) {
+ continue;
+ }
+
+ // If one is null or their values don't match, it's not symmetric
+ if (left == null || right == null || left.value != right.value) {
+ return false;
+ }
+
+ // Push children in the order that ensures symmetry
+ stack.push(left.left);
+ stack.push(right.right);
+ stack.push(left.right);
+ stack.push(right.left);
+ }
+ return true; // If we finish checking without issues, it's symmetric
+ }
+}
diff --git a/src/main/java/com/shortthirdman/quickstart/hackerrank/DominantStringAnalyzer.java b/src/main/java/com/shortthirdman/quickstart/hackerrank/DominantStringAnalyzer.java
new file mode 100644
index 0000000..ba0576e
--- /dev/null
+++ b/src/main/java/com/shortthirdman/quickstart/hackerrank/DominantStringAnalyzer.java
@@ -0,0 +1,55 @@
+package com.shortthirdman.quickstart.hackerrank;
+
+import java.util.Arrays;
+import java.util.stream.IntStream;
+
+/**
+ * A team of data analysts at Amazon is working to identify data patterns.
+ * During their analysis, they found a category of string they call dominant string:
+ *
+ * - It has an even length.
+ *
+ * - The string contains at least one character with a frequency that matches exactly half of the length of the string.
+ *
+ * Given a string s
of length n
, determine the number of its distinct substrings that are dominant strings.
+ *
+ * @author ShortThirdMan
+ */
+public class DominantStringAnalyzer {
+
+ /**
+ * @param s the input text
+ * @return the count or number of dominant string
+ */
+ public long getDominantStringCount(String s) {
+ long count = 0;
+ int n = s.length();
+ var lowercaseText = s.toLowerCase();
+
+ count = IntStream.range(2, n + 1)
+ .filter(len -> len % 2 == 0) // Only consider even lengths
+ .mapToLong(len ->
+ IntStream.range(0, n - len + 1)
+ .mapToObj(i -> lowercaseText.substring(i, i + len))
+ .filter(substring -> isDominant(substring, len))
+ .count()
+ ).sum();
+
+ return count;
+ }
+
+ private boolean isDominant(String substring, int len) {
+ int[] frequency = new int[10];
+ substring.chars()
+ .forEach(c -> {
+ // Ensure the character is between 'a' and 'z' before updating frequency
+ if (c >= 'a' && c <= 'z') {
+ frequency[c - 'a']++;
+ }
+ });
+
+ int halfLength = len / 2;
+ return Arrays.stream(frequency)
+ .anyMatch(f -> f == halfLength);
+ }
+}
diff --git a/src/main/java/com/shortthirdman/quickstart/hackerrank/OptimizingBoxWeights.java b/src/main/java/com/shortthirdman/quickstart/hackerrank/OptimizingBoxWeights.java
new file mode 100644
index 0000000..5fb20d6
--- /dev/null
+++ b/src/main/java/com/shortthirdman/quickstart/hackerrank/OptimizingBoxWeights.java
@@ -0,0 +1,68 @@
+package com.shortthirdman.quickstart.hackerrank;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Stack;
+
+/**
+ *
+ * An Amazon Fulfillment Associate has a set of items that need to be packed into two boxes.
+ * Given an integer array of the item weights (arr
) to be packed, divide the item weights into two subsets,
+ * A
and B
, for packing into the associated boxes, while respecting the following conditions:
+ *
+ * - The intersection of A and B is null.
+ * - The union A and B is equal to the original array.
+ * - The number of elements in subset A is minimal.
+ * - The sum of A's weights is greater than the sum of B's weights.
+ *
+ *
+ * Return the subset A in increasing order where the sum of A's weights is greater than the sum of B's weights.
+ * If more than one subset A exists, return the one with the maximal total weight.
+ *
+ * @author ShortThirdMan
+ */
+public class OptimizingBoxWeights {
+
+ /**
+ * @param weights the input array
+ * @return the list of
+ */
+ public List minimalHeaviestSetA(List weights) {
+ Collections.sort(weights);
+ Stack stack = new Stack<>();
+ int totalRemainingSum = 0;
+
+ for (Integer integer : weights) {
+ stack.push(integer);
+ totalRemainingSum += integer;
+ }
+
+ List setA = new ArrayList<>();
+ int setASum = 0;
+ while (!stack.isEmpty()) {
+ int next = stack.pop();
+ setA.addFirst(next);
+ setASum+= next;
+ totalRemainingSum-= next;
+
+ // Stop When Sum of List A is greater than remaining
+ if (setASum > totalRemainingSum) {
+ while (!stack.isEmpty() && setASum - setA.getFirst() + stack.peek() > totalRemainingSum + setA.getFirst() - stack.peek()) {
+ int lastItem = setA.getFirst();
+ Integer newMin = stack.pop();
+ setA.removeFirst();
+ setA.addFirst(newMin);
+
+ setASum += (newMin - lastItem);
+ totalRemainingSum += (lastItem - newMin);
+ }
+ break;
+ }
+ }
+
+ Collections.sort(setA);
+
+ return setA;
+ }
+}
diff --git a/src/main/java/com/shortthirdman/quickstart/hackerrank/ReversePolishNotation.java b/src/main/java/com/shortthirdman/quickstart/hackerrank/ReversePolishNotation.java
new file mode 100644
index 0000000..24417d3
--- /dev/null
+++ b/src/main/java/com/shortthirdman/quickstart/hackerrank/ReversePolishNotation.java
@@ -0,0 +1,82 @@
+package com.shortthirdman.quickstart.hackerrank;
+
+import java.util.Stack;
+
+/**
+ * Evaluate the value of an arithmetic expression in Reverse Polish Notation.
+ * Valid operators are +, -, *, /
. Each operand may be an integer or another expression.
+ *
+ * @author ShortThirdMan
+ */
+public class ReversePolishNotation {
+
+ public int evaluateRPN(String[] tokens) {
+ int returnValue = 0;
+ String operators = "+-*/";
+
+ Stack stack = new Stack<>();
+ for (String t : tokens) {
+ if (!operators.contains(t)) { //push to stack if it is a number
+ stack.push(t);
+ } else {
+ // pop numbers from stack if it is an operator
+ int a = Integer.parseInt(stack.pop());
+ int b = Integer.parseInt(stack.pop());
+ switch (t) {
+ case "+":
+ stack.push(String.valueOf(a + b));
+ break;
+ case "-":
+ stack.push(String.valueOf(b - a));
+ break;
+ case "*":
+ stack.push(String.valueOf(a * b));
+ break;
+ case "/":
+ stack.push(String.valueOf(b / a));
+ break;
+ }
+ }
+ }
+
+ returnValue = Integer.parseInt(stack.pop());
+
+ return returnValue;
+ }
+
+ public int computeRPN(String[] tokens) {
+ Stack stack = new Stack<>();
+ String operators = "+-*/";
+
+ for (String token : tokens) {
+ if (!operators.contains(token)) {
+ stack.push(Integer.parseInt(token)); // Push numbers to the stack
+ } else {
+ // Pop the last two numbers for the operation
+ int b = stack.pop();
+ int a = stack.pop();
+ switch (token) {
+ case "+":
+ stack.push(a + b);
+ break;
+ case "-":
+ stack.push(a - b);
+ break;
+ case "*":
+ stack.push(a * b);
+ break;
+ case "/":
+ if (b == 0) {
+ throw new ArithmeticException("Division by zero");
+ }
+ stack.push(a / b);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid operator: " + token);
+ }
+ }
+ }
+
+ return stack.pop(); // Return the final result
+ }
+}
diff --git a/src/main/java/com/shortthirdman/quickstart/hackerrank/StoneWall.java b/src/main/java/com/shortthirdman/quickstart/hackerrank/StoneWall.java
new file mode 100644
index 0000000..c94e210
--- /dev/null
+++ b/src/main/java/com/shortthirdman/quickstart/hackerrank/StoneWall.java
@@ -0,0 +1,37 @@
+package com.shortthirdman.quickstart.hackerrank;
+
+/**
+ * You are going to build a stone wall. The wall should be straight and N
meters long, and its thickness should be constant;
+ * however, it should have different heights in different places. The height of the wall is specified by an array H
of N positive integers.
+ * H[i]
is the height of the wall from i to i+1
meters to the right of its left end.
+ *
+ * In particular, H[0]
is the height of the wall's left end and H[N−1]
is the height of the wall's right end.
+ * The wall should be built of cuboid stone blocks (that is, all sides of such blocks are rectangular).
+ *
+ * @author ShortThirdMan
+ */
+public class StoneWall {
+
+ /**
+ * Compute the minimum number of blocks needed to build the wall.
+ * @param H array of positive integers specifying height of wall
+ * @return Minimum number of blocks needed to build
+ */
+ public int coverManhattanSkyline(int[] H) {
+ int N = H.length;
+ int totalBlocks = 0;
+
+ if (N < 1 || N > 100000) {
+ throw new IllegalArgumentException("Number of height-blocks should be in range [1,100000]");
+ }
+
+ for (int height : H) {
+ if (height <= 0) {
+ throw new IllegalArgumentException("Height values must be positive");
+ }
+ totalBlocks += height;
+ }
+
+ return totalBlocks;
+ }
+}
diff --git a/src/main/java/com/shortthirdman/quickstart/hackerrank/StreamApiFactory.java b/src/main/java/com/shortthirdman/quickstart/hackerrank/StreamApiFactory.java
new file mode 100644
index 0000000..cb757ee
--- /dev/null
+++ b/src/main/java/com/shortthirdman/quickstart/hackerrank/StreamApiFactory.java
@@ -0,0 +1,165 @@
+package com.shortthirdman.quickstart.hackerrank;
+
+import com.shortthirdman.quickstart.common.EmployeeRecord;
+import com.shortthirdman.quickstart.common.Transaction;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+public class StreamApiFactory {
+
+ public String concatenateEvenLengthWords(List words) {
+ String result = null;
+ if (words == null || words.isEmpty()) {
+ throw new IllegalArgumentException("words cannot be null or empty");
+ }
+
+ result = words.stream()
+ .filter(x -> x.length() % 2 == 0)
+ .limit(2)
+ .collect(Collectors.joining());
+ return result;
+ }
+
+ /**
+ * @param sentence the sentence or group of words
+ * @return map of character frequency
+ */
+ public Map countCharacterFrequency(String sentence) {
+ Map charFreq = null;
+
+ if (sentence == null || sentence.isEmpty()) {
+ throw new IllegalArgumentException("sentence cannot be null or empty");
+ }
+
+ charFreq = Arrays.stream(sentence.split("")).collect(
+ Collectors.groupingBy(
+ Function.identity(), // or x -> x
+ Collectors.counting()
+ )
+ );
+
+ return charFreq;
+ }
+
+ /**
+ * @param transactions the list of transactions
+ * @return
+ */
+ public Map getDailyTransactionSums(List transactions) {
+ Map dailyTotals = null;
+
+ if (transactions == null || transactions.isEmpty()) {
+ throw new IllegalArgumentException("transactions cannot be null or empty");
+ }
+
+ dailyTotals = transactions.stream()
+ .collect(Collectors.groupingBy(
+ Transaction::getDate,
+ TreeMap::new,
+ Collectors.summingLong(Transaction::getAmount)
+ ));
+
+ return dailyTotals;
+ }
+
+ /**
+ * @param employees the list of employees with identifier and name
+ * @return
+ */
+ public Map> toSortedMapWithDuplicates(List employees) {
+ Map> employeeMap = null;
+
+ if (employees == null || employees.isEmpty()) {
+ throw new IllegalArgumentException("employees cannot be null or empty");
+ }
+
+ employeeMap = employees.stream()
+ .collect(Collectors.groupingBy(
+ EmployeeRecord::getId, // Key extractor
+ TreeMap::new, // Use TreeMap to maintain sorted order
+ Collectors.toList() // Collect values into a list
+ ));
+
+ return employeeMap;
+ }
+
+ public Map countStringsByFirstCharacter(List words) {
+ Map frequency = null;
+
+ if (words == null || words.isEmpty()) {
+ throw new IllegalArgumentException("words cannot be null or empty");
+ }
+
+ frequency = words.stream()
+ .collect(Collectors.groupingBy(
+ str -> str.charAt(0),
+ Collectors.counting()
+ ));
+
+ return frequency;
+ }
+
+ /**
+ * Given two arrays of integers, merge them, sort them, and then
+ * filter out any numbers greater than a specified threshold.
+ *
+ * @param arr1 the first input array
+ * @param arr2 the second input array
+ * @param threshold the threshold value
+ * @return the list of concatenated array of integers
+ */
+ public List mergeSortAndFilterByThreshold(int[] arr1, int[] arr2, int threshold) {
+ Stream intStream = null;
+ if (arr1 == null || arr2 == null || arr1.length == 0 || arr2.length == 0) {
+ throw new IllegalArgumentException("arr1 and arr2 cannot be null or empty");
+ }
+
+ intStream = IntStream.concat(Arrays.stream(arr1), Arrays.stream(arr2))
+ .boxed()
+ .sorted(Comparator.naturalOrder())
+ .filter(x -> x > threshold);
+
+ return intStream.collect(Collectors.toList());
+ }
+
+ /**
+ * @param numbers the list of numbers
+ * @return
+ */
+ public Map> partitionByPrime(List numbers) {
+ Map> partitioned = null;
+ if (numbers == null || numbers.isEmpty()) {
+ throw new IllegalArgumentException("numbers cannot be null or empty");
+ }
+
+ partitioned = numbers.stream()
+ .collect(Collectors.partitioningBy(this::isPrime));
+
+ return partitioned;
+ }
+
+ private boolean isPrime(int number) {
+ return number > 1 && IntStream.range(2, (int) Math.sqrt(number) + 1).noneMatch(i -> number % i == 0);
+ }
+
+ /**
+ * Count the total number of distinct words (case-insensitive) across multiple sentences.
+ * @param sentences the input list of group of words
+ * @return
+ */
+ public long countTotalUniqueWords(List sentences) {
+ if (sentences == null || sentences.isEmpty()) {
+ throw new IllegalArgumentException("sentences cannot be null or empty");
+ }
+ var uniqueWords = sentences.stream()
+ .map(x -> x.toLowerCase().split(" "))
+ .flatMap(Arrays::stream)
+ .distinct()
+ .count();
+ return uniqueWords;
+ }
+}
diff --git a/src/main/java/com/shortthirdman/quickstart/leetcode/MinimumBinaryFlips.java b/src/main/java/com/shortthirdman/quickstart/leetcode/MinimumBinaryFlips.java
index c43e3ea..1eb0378 100644
--- a/src/main/java/com/shortthirdman/quickstart/leetcode/MinimumBinaryFlips.java
+++ b/src/main/java/com/shortthirdman/quickstart/leetcode/MinimumBinaryFlips.java
@@ -1,5 +1,7 @@
package com.shortthirdman.quickstart.leetcode;
+import java.util.Objects;
+
/**
* A password string, pwd, consists of binary characters (0s and 1s). A cyber-security expert is trying to determine the
* minimum number of changes required to make the password secure.
@@ -18,9 +20,13 @@ public class MinimumBinaryFlips {
* @param text the binary string
* @return the minimum number of flips to make the division possible
*/
- public Integer getMinFlips(String text) {
+ public int getMinFlips(String text) {
int minFlips = Integer.MAX_VALUE;
+ if (Objects.isNull(text)) {
+ throw new NullPointerException("Input text password can not be null");
+ }
+
int len = text.length();
int flipsToMakeAllZeros = flipsToMakeAllSame(text, '0');
@@ -41,11 +47,11 @@ public Integer getMinFlips(String text) {
minFlips = Math.min(minFlips, Math.min(flipsToZeroPart1 + flipsToOnePart2, flipsToOnePart1 + flipsToZeroPart2));
}
- return Integer.valueOf(minFlips);
+ return minFlips;
}
- public int flipsToMakeAllSame(String str, char targetChar) {
+ private int flipsToMakeAllSame(String str, char targetChar) {
int flips = 0;
for (char c : str.toCharArray()) {
if (c != targetChar) {
diff --git a/src/test/java/com/shortthirdman/quickstart/codesignal/CountDistinctSlicesTest.java b/src/test/java/com/shortthirdman/quickstart/codesignal/CountDistinctSlicesTest.java
new file mode 100644
index 0000000..cd76039
--- /dev/null
+++ b/src/test/java/com/shortthirdman/quickstart/codesignal/CountDistinctSlicesTest.java
@@ -0,0 +1,133 @@
+package com.shortthirdman.quickstart.codesignal;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class CountDistinctSlicesTest {
+
+ CountDistinctSlices app;
+
+ @BeforeEach
+ void setUp() {
+ app = new CountDistinctSlices();
+ }
+
+ // Testing the case with a small array where all elements are distinct
+ @Test
+ void testDistinctElements() {
+ int M = 5;
+ int[] A = {1, 2, 3};
+ int result = app.calculateDistinctSlices(M, A);
+ assertEquals(6, result); // The 6 distinct slices are: (0,0), (0,1), (0,2), (1,1), (1,2), (2,2)
+ }
+
+ // Testing the case where there are duplicate elements in the array
+ @Test
+ void testWithDuplicates() {
+ int M = 5;
+ int[] A = {1, 2, 2, 3};
+ int result = app.calculateDistinctSlices(M, A);
+ assertNotEquals(7, result); // The distinct slices are: (0,0), (0,1), (0,2), (1,1), (1,2), (2,2), (3,3)
+ }
+
+ // Edge case with the smallest possible array of length 1
+ @Test
+ void testSingleElementArray() {
+ int M = 1;
+ int[] A = {0};
+ int result = app.calculateDistinctSlices(M, A);
+ assertEquals(1, result); // Only one slice: (0,0)
+ }
+
+ // Case with all elements being the same
+ @Test
+ void testAllSameElements() {
+ int M = 3;
+ int[] A = {2, 2, 2, 2};
+
+ int result = app.calculateDistinctSlices(M, A);
+ assertEquals(4, result); // The distinct slices are: (0,0), (1,1), (2,2), (3,3)
+ }
+
+ // Edge case with an empty array (this should never happen due to validation)
+ @Test
+ void testEmptyArray() {
+ int M = 0;
+ int[] A = {};
+ assertThrows(IllegalArgumentException.class, () -> app.calculateDistinctSlices(M, A));
+ }
+
+ // Testing with a large input size (e.g., 100,000 elements)
+ @Test
+ void testLargeInput() {
+ int M = 100000;
+ int[] A = new int[100000];
+ for (int i = 0; i < 100000; i++) {
+ A[i] = i % 1000; // Repeating elements in a controlled pattern
+ }
+
+ int result = app.calculateDistinctSlices(M, A);
+ // Expected result is not easy to calculate by hand, but the test ensures it runs within time limits
+ assertTrue(result >= 0); // Just check that result is non-negative
+ }
+
+ // Edge case with maximum possible value for M
+ @Test
+ void testMaximumM() {
+ int M = 100000;
+ int[] A = {100000, 100000, 100000};
+
+ int result = app.calculateDistinctSlices(M, A);
+ assertEquals(3, result); // Only slices (0,0), (1,1), and (2,2) are distinct
+ }
+
+ // Testing the method with a single element at maximum value
+ @Test
+ void testSingleMaxElement() {
+ int M = 100000;
+ int[] A = {100000};
+
+ int result = app.calculateDistinctSlices(M, A);
+ assertEquals(1, result); // Only one slice (0,0)
+ }
+
+ // Testing for large range where M is much greater than the values in A
+ @Test
+ void testLargeMWithSmallerArray() {
+ int M = 100000;
+ int[] A = {1, 2, 3, 4};
+
+ int result = app.calculateDistinctSlices(M, A);
+ assertEquals(10, result); // The distinct slices are: (0,0), (0,1), (0,2), (0,3), (1,1), (1,2), (1,3), (2,2), (2,3), (3,3)
+ }
+
+ // Testing an array where the elements are in decreasing order
+ @Test
+ void testDecreasingOrder() {
+ int M = 5;
+ int[] A = {5, 4, 3, 2, 1};
+
+ int result = app.calculateDistinctSlices(M, A);
+ assertEquals(15, result); // The distinct slices are: (0,0), (0,1), (0,2), (0,3), (0,4), (1,1), (1,2), (1,3), (1,4), (2,2), (2,3), (2,4), (3,3), (3,4), (4,4)
+ }
+
+ // Testing invalid input for array elements exceeding the upper bound M
+ @Test
+ void testInvalidElementExceedingM() {
+ int M = 5;
+ int[] A = {1, 2, 6}; // Element 6 exceeds M
+
+ assertThrows(IllegalArgumentException.class, () -> app.calculateDistinctSlices(M, A));
+ }
+
+ // Testing invalid input for array length being zero
+ @Test
+ void testInvalidArrayLength() {
+ int M = 5;
+ int[] A = {}; // Empty array
+
+ assertThrows(IllegalArgumentException.class, () -> app.calculateDistinctSlices(M, A));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/shortthirdman/quickstart/codility/BagOfFruitsTest.java b/src/test/java/com/shortthirdman/quickstart/codility/BagOfFruitsTest.java
new file mode 100644
index 0000000..017e97f
--- /dev/null
+++ b/src/test/java/com/shortthirdman/quickstart/codility/BagOfFruitsTest.java
@@ -0,0 +1,46 @@
+package com.shortthirdman.quickstart.codility;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class BagOfFruitsTest {
+
+ BagOfFruits app;
+
+ @BeforeEach
+ void setUp() {
+ app = new BagOfFruits();
+ }
+
+ @AfterEach
+ void tearDown() {
+ }
+
+ @Test
+ void countFruits() {
+ List fruits = List.of("apple",
+ "apple", "apple", "apple", "apple", "apple",
+ "grapes", "grapes", "kiwi", "kiwi", "kiwi", "kiwi", "kiwi");
+ List expected = List.of("apple", "kiwi");
+ long threshold = 2;
+ List actual = app.countFruits(fruits, threshold);
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ void testCountFruits() {
+ List fruits = List.of("apple",
+ "apple","apple","apple", "apple", "apple",
+ "grapes", "grapes",
+ "kiwi","kiwi", "kiwi", "kiwi", "kiwi");
+ List expected = List.of("apple", "kiwi");
+ int threshold = 2;
+ List actual = app.countFruits(fruits, threshold);
+ assertEquals(expected, actual);
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/shortthirdman/quickstart/codility/GenerateAllSubstringsTest.java b/src/test/java/com/shortthirdman/quickstart/codility/GenerateAllSubstringsTest.java
new file mode 100644
index 0000000..ff43a90
--- /dev/null
+++ b/src/test/java/com/shortthirdman/quickstart/codility/GenerateAllSubstringsTest.java
@@ -0,0 +1,22 @@
+package com.shortthirdman.quickstart.codility;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class GenerateAllSubstringsTest {
+
+ @BeforeEach
+ void setUp() {
+ }
+
+ @AfterEach
+ void tearDown() {
+ }
+
+ @Test
+ void generateSubStrings() {
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/shortthirdman/quickstart/hackerrank/BinaryTreeTest.java b/src/test/java/com/shortthirdman/quickstart/hackerrank/BinaryTreeTest.java
new file mode 100644
index 0000000..97312be
--- /dev/null
+++ b/src/test/java/com/shortthirdman/quickstart/hackerrank/BinaryTreeTest.java
@@ -0,0 +1,518 @@
+package com.shortthirdman.quickstart.hackerrank;
+
+import com.shortthirdman.quickstart.common.TreeNode;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class BinaryTreeTest {
+
+ BinaryTree tree;
+
+ @BeforeEach
+ void setUp() {
+ tree = new BinaryTree();
+ }
+
+ @Test
+ void minDepth_SingleLeaf() {
+ TreeNode root = new TreeNode(1);
+ assertEquals(1, tree.minDepth(root), "Expected min depth for single node to be 1.");
+ }
+
+ @Test
+ void minDepth_TwoLevels() {
+ TreeNode root = new TreeNode(1, new TreeNode(2), null);
+ assertEquals(2, tree.minDepth(root), "Expected min depth for single child tree to be 2.");
+ }
+
+ @Test
+ void minDepth_ThreeLevels() {
+ TreeNode root = new TreeNode(1, new TreeNode(2, new TreeNode(3), null), new TreeNode(4));
+ assertEquals(2, tree.minDepth(root), "Expected min depth for balanced tree with three levels to be 2.");
+ }
+
+ @Test
+ void minDepth_BalancedTree() {
+ TreeNode root = new TreeNode(1, new TreeNode(2), new TreeNode(3));
+ assertEquals(2, tree.minDepth(root), "Expected min depth for balanced tree to be 2.");
+ }
+
+ // Negative test cases
+ @Test
+ void minDepth_EmptyTree() {
+ TreeNode root = null;
+ assertEquals(0, tree.minDepth(root), "Expected min depth for null tree to be 0.");
+ }
+
+ // Edge cases
+ @Test
+ void minDepth_LeftHeavyTree() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(2);
+ root.left.left = new TreeNode(3);
+ assertEquals(3, tree.minDepth(root));
+ }
+
+ @Test
+ void minDepth_RightHeavyTree() {
+ TreeNode root = new TreeNode(1);
+ root.right = new TreeNode(2);
+ root.right.right = new TreeNode(3);
+ assertEquals(3, tree.minDepth(root));
+ }
+
+ // Parameterized test cases
+ static Object[][] provideMinDepthCases() {
+ return new Object[][] {
+ {null, 0}, // empty tree
+ {new TreeNode(1), 1}, // single node
+ {new TreeNode(1, new TreeNode(2), null), 2}, // left child only
+ {new TreeNode(1, null, new TreeNode(3)), 2}, // right child only
+ {new TreeNode(1, new TreeNode(2), new TreeNode(3)), 2}, // balanced tree
+ {new TreeNode(1, new TreeNode(2, new TreeNode(4), null), new TreeNode(3)), 2} // left heavy
+ };
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideMinDepthCases")
+ void minDepth_Parameterized(TreeNode root, int expectedDepth) {
+ assertEquals(expectedDepth, tree.minDepth(root));
+ }
+
+ // Test for a large balanced tree
+ @Test
+ void minDepth_LargeBalancedTree() {
+ TreeNode root = createBalancedTree(1, 17); // height of 17, so depth should be 17
+ assertEquals(17, tree.minDepth(root));
+ }
+
+ // Test for a large skewed tree (left-heavy)
+ @Test
+ void minDepth_LargeLeftHeavyTree() {
+ TreeNode root = createLeftHeavyTree(100000); // 100000 nodes deep
+ assertEquals(100000, tree.minimumDepth(root));
+ }
+
+ // Helper method to create a balanced binary tree
+ private TreeNode createBalancedTree(int value, int height) {
+ if (height <= 0) return null;
+ TreeNode node = new TreeNode(value);
+ node.left = createBalancedTree(value, height - 1);
+ node.right = createBalancedTree(value, height - 1);
+ return node;
+ }
+
+ // Helper method to create a left-heavy tree
+ private TreeNode createLeftHeavyTree(int nodes) {
+ if (nodes <= 0) return null;
+ TreeNode root = new TreeNode(1);
+ TreeNode current = root;
+ for (int i = 2; i <= nodes; i++) {
+ current.left = new TreeNode(i);
+ current = current.left;
+ }
+ return root;
+ }
+
+ @Test
+ public void minDepth_ComplexTree() {
+ // Test case for a more complex tree
+ TreeNode root = new TreeNode(1,
+ new TreeNode(2,
+ new TreeNode(4,
+ new TreeNode(7),
+ null),
+ new TreeNode(5)),
+ new TreeNode(3));
+ assertNotEquals(3, tree.minDepth(root), "Expected min depth for complex tree not to be 3."); // Shortest path to leaf 3
+ assertEquals(2, tree.minDepth(root), "Expected min depth for complex tree to be 2.");
+ }
+
+ @Test
+ public void maxDepth_NullTree() {
+ // Test case for an empty tree
+ assertEquals(0, tree.maxDepth(null), "Expected max depth for null tree to be 0.");
+ }
+
+ @Test
+ public void maxDepth_LeafNode() {
+ // Test case for a single leaf node
+ TreeNode root = new TreeNode(1);
+ assertEquals(1, tree.maxDepth(root), "Expected max depth for single node to be 1.");
+ }
+
+ @Test
+ public void maxDepth_SingleChildTree() {
+ // Test case where the tree has one child
+ TreeNode root = new TreeNode(1, new TreeNode(2), null);
+ assertEquals(2, tree.maxDepth(root), "Expected max depth for single child tree to be 2.");
+ }
+
+ @Test
+ public void maxDepth_BalancedTree() {
+ // Test case for a balanced tree
+ TreeNode root = new TreeNode(1,
+ new TreeNode(2,
+ new TreeNode(4),
+ new TreeNode(5)),
+ new TreeNode(3));
+ assertEquals(3, tree.maxDepth(root), "Expected max depth for balanced tree to be 3.");
+ }
+
+ @Test
+ public void maxDepth_ComplexTree() {
+ // Test case for a more complex tree
+ TreeNode root = new TreeNode(1,
+ new TreeNode(2,
+ new TreeNode(4,
+ new TreeNode(7),
+ null),
+ new TreeNode(5)),
+ new TreeNode(3));
+ assertEquals(4, tree.maxDepth(root), "Expected max depth for complex tree to be 4.");
+ }
+
+ @Test
+ public void maxDepth_OnlyLeftChildren() {
+ // Test case for a tree with only left children
+ TreeNode root = new TreeNode(1,
+ new TreeNode(2,
+ new TreeNode(3,
+ new TreeNode(4),
+ null),
+ null),
+ null);
+ assertEquals(4, tree.maxDepth(root), "Expected max depth for left-only tree to be 4.");
+ }
+
+ @Test
+ public void maxDepth_OnlyRightChildren() {
+ // Test case for a tree with only right children
+ TreeNode root = new TreeNode(1,
+ null,
+ new TreeNode(2,
+ null,
+ new TreeNode(3,
+ null,
+ new TreeNode(4))));
+ assertEquals(4, tree.maxDepth(root), "Expected max depth for right-only tree to be 4.");
+ }
+
+ @ParameterizedTest
+ @MethodSource("provideTreesForParameterizedTest")
+ public void maxDepth_Parameterized(TreeNode root, int expectedDepth) {
+ assertEquals(expectedDepth, tree.maxDepth(root), "Expected max depth does not match.");
+ }
+
+ private static Object[][] provideTreesForParameterizedTest() {
+ return new Object[][] {
+ { null, 0 }, // Empty tree
+ { new TreeNode(1), 1 }, // Single node
+ { new TreeNode(1, new TreeNode(2), null), 2 }, // One left child
+ { new TreeNode(1, null, new TreeNode(2)), 2 }, // One right child
+ { new TreeNode(1,
+ new TreeNode(2, new TreeNode(3), null),
+ new TreeNode(4)), 3 }, // Mixed tree
+ { new TreeNode(1,
+ new TreeNode(2,
+ new TreeNode(3,
+ new TreeNode(4),
+ null),
+ null),
+ null), 4 } // All left children
+ };
+ }
+
+ @Test
+ void isSymmetric_testSymmetricTree() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(2);
+ root.right = new TreeNode(2);
+ root.left.left = new TreeNode(3);
+ root.left.right = new TreeNode(4);
+ root.right.left = new TreeNode(4);
+ root.right.right = new TreeNode(3);
+
+ assertTrue(tree.isSymmetric(root), "The tree should be symmetric.");
+ }
+
+ @Test
+ void isSymmetric_testAsymmetricTreeDifferentValues() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(2);
+ root.right = new TreeNode(2);
+ root.left.left = new TreeNode(3);
+ root.left.right = new TreeNode(5);
+ root.right.left = new TreeNode(4);
+ root.right.right = new TreeNode(3);
+
+ assertFalse(tree.isSymmetric(root), "The tree should not be symmetric.");
+ }
+
+ @Test
+ void isSymmetric_testAsymmetricTreeNulls() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(2);
+ root.right = new TreeNode(2);
+ root.left.right = new TreeNode(3);
+ root.right.right = new TreeNode(3);
+
+ assertFalse(tree.isSymmetric(root), "The tree should not be symmetric.");
+ }
+
+ @Test
+ void isSymmetric_testSingleNodeTree() {
+ TreeNode root = new TreeNode(1);
+
+ assertTrue(tree.isSymmetric(root), "A single node tree should be symmetric.");
+ }
+
+ @Test
+ void isSymmetric_testTwoLevelTreeSymmetric() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(2);
+ root.right = new TreeNode(2);
+
+ assertTrue(tree.isSymmetric(root), "The two-level tree should be symmetric.");
+ }
+
+ @Test
+ void isSymmetric_testTwoLevelTreeAsymmetric() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(2);
+ root.right = new TreeNode(3);
+
+ assertFalse(tree.isSymmetric(root), "The two-level tree should not be symmetric.");
+ }
+
+ @Test
+ void isSymmetric_testEmptyTree() {
+ assertTrue(tree.isSymmetric(null), "An empty tree should be symmetric.");
+ }
+
+ @Test
+ void isSymmetric_testLargeSymmetricTree() {
+ TreeNode root = generateBalancedSymmetricTree(1, 4); // Height 4
+ assertTrue(tree.isSymmetric(root), "A large balanced tree should be symmetric.");
+ }
+
+ @Test
+ void isSymmetric_testLargeAsymmetricTree() {
+ TreeNode root = generateBalancedAsymmetricTree(1, 4); // Height 4
+ assertFalse(tree.isSymmetric(root), "A large unbalanced tree should not be symmetric.");
+ }
+
+ // Helper method to create a balanced symmetric tree
+ private TreeNode generateBalancedSymmetricTree(int value, int height) {
+ if (height <= 0) {
+ return null;
+ }
+ TreeNode node = new TreeNode(value);
+ node.left = generateBalancedSymmetricTree(value + 1, height - 1);
+ node.right = generateBalancedSymmetricTree(value + 1, height - 1);
+ return node;
+ }
+
+ // Helper method to create a balanced asymmetric tree
+ private TreeNode generateBalancedAsymmetricTree(int value, int height) {
+ if (height <= 0) {
+ return null;
+ }
+ TreeNode node = new TreeNode(value);
+ node.left = generateBalancedAsymmetricTree(value + 1, height - 1);
+ node.right = new TreeNode(value + 1, generateBalancedAsymmetricTree(value + 1, height - 1), null);
+ return node;
+ }
+
+ @Test
+ void isSymmetric_testTreeWithNegativeValues() {
+ TreeNode root = new TreeNode(0);
+ root.left = new TreeNode(-1);
+ root.right = new TreeNode(-1);
+ root.left.left = new TreeNode(-2);
+ root.left.right = new TreeNode(-3);
+ root.right.left = new TreeNode(-3);
+ root.right.right = new TreeNode(-2);
+
+ assertTrue(tree.isSymmetric(root), "The tree with negative values should be symmetric.");
+ }
+
+ @Test
+ void isSymmetric_testTreeWithNegativeAndPositiveValues() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(-2);
+ root.right = new TreeNode(-2);
+ root.left.left = new TreeNode(-3);
+ root.left.right = new TreeNode(-4);
+ root.right.left = new TreeNode(-4);
+ root.right.right = new TreeNode(-3);
+
+ assertTrue(tree.isSymmetric(root), "The tree with mixed values should be symmetric.");
+ }
+
+ @Test
+ void isBalanced_testBalancedTree() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(2);
+ root.right = new TreeNode(2);
+ root.left.left = new TreeNode(3);
+ root.left.right = new TreeNode(4);
+ root.right.left = new TreeNode(4);
+ root.right.right = new TreeNode(3);
+
+ assertTrue(tree.isBalanced(root), "The tree should be balanced.");
+ }
+
+ @Test
+ void isBalanced_testUnbalancedTree() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(2);
+ root.left.left = new TreeNode(3);
+ root.left.left.left = new TreeNode(4);
+
+ assertFalse(tree.isBalanced(root), "The tree should not be balanced.");
+ }
+
+ @Test
+ void isBalanced_testAsymmetricTree() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(2);
+ root.left.left = new TreeNode(3);
+ root.left.left.left = new TreeNode(4);
+
+ assertFalse(tree.isBalanced(root), "The tree should not be balanced.");
+ }
+
+ @Test
+ void isBalanced_testEmptyTree() {
+ assertTrue(tree.isBalanced(null), "An empty tree should be considered balanced.");
+ }
+
+ @Test
+ void isBalanced_testSingleNodeTree() {
+ TreeNode root = new TreeNode(1);
+ assertTrue(tree.isBalanced(root), "A single node tree should be balanced.");
+ }
+
+ @Test
+ void isBalanced_testTwoLevelBalancedTree() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(2);
+ root.right = new TreeNode(2);
+ assertTrue(tree.isBalanced(root), "A two-level tree should be balanced.");
+ }
+
+ @Test
+ void isBalanced_testTwoLevelAsymmetricTree() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(2);
+ root.right = new TreeNode(3);
+ root.right.right = new TreeNode(4);
+
+ assertTrue(tree.isBalanced(root), "The two-level tree should be balanced.");
+ }
+
+ @Test
+ void isBalanced_testUnbalancedTwoLevelTree() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(2);
+ root.left.left = new TreeNode(3);
+
+ assertFalse(tree.isBalanced(root), "The two-level tree should not be balanced.");
+ }
+
+ @Test
+ void isBalanced_testLargeBalancedTree() {
+ TreeNode root = generateBalancedTree(1, 5); // Height 5
+ assertTrue(tree.isBalanced(root), "A large balanced tree should be balanced.");
+ }
+
+ @Test
+ void isBalanced_testLargeAsymmetricTree() {
+ TreeNode root = generateAsymmetricTree(1, 5); // Height 5
+ assertFalse(tree.isBalanced(root), "A large unbalanced tree should not be balanced.");
+ }
+
+ // Helper method to create a balanced tree
+ private TreeNode generateBalancedTree(int value, int height) {
+ if (height <= 0) {
+ return null;
+ }
+ TreeNode node = new TreeNode(value);
+ node.left = generateBalancedTree(value + 1, height - 1);
+ node.right = generateBalancedTree(value + 1, height - 1);
+ return node;
+ }
+
+ // Helper method to create an asymmetric tree
+ private TreeNode generateAsymmetricTree(int value, int height) {
+ if (height <= 0) {
+ return null;
+ }
+ TreeNode node = new TreeNode(value);
+ node.left = generateAsymmetricTree(value + 1, height - 1);
+ node.right = new TreeNode(value + 1);
+ return node;
+ }
+
+ @Test
+ void isBalanced_testTreeWithNegativeValues() {
+ TreeNode root = new TreeNode(0);
+ root.left = new TreeNode(-1);
+ root.right = new TreeNode(-1);
+ root.left.left = new TreeNode(-2);
+ root.left.right = new TreeNode(-3);
+ root.right.left = new TreeNode(-3);
+ root.right.right = new TreeNode(-2);
+
+ assertTrue(tree.isBalanced(root), "The tree with negative values should be balanced.");
+ }
+
+ @Test
+ void isBalanced_testTreeWithNegativeAndPositiveValues() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(-2);
+ root.right = new TreeNode(-2);
+ root.left.left = new TreeNode(-3);
+ root.left.right = new TreeNode(-4);
+ root.right.left = new TreeNode(-4);
+ root.right.right = new TreeNode(-3);
+
+ assertTrue(tree.isBalanced(root), "The tree with mixed values should be balanced.");
+ }
+
+ @Test
+ void isBalanced_testExample1() {
+ TreeNode root = new TreeNode(1);
+ root.left = new TreeNode(2);
+ root.right = new TreeNode(2);
+ root.left.left = new TreeNode(3);
+ root.left.right = new TreeNode(3);
+ root.left.left.left = new TreeNode(4);
+ root.left.left.right = new TreeNode(4);
+
+ assertFalse(tree.isBalanced(root), "The tree should not be balanced.");
+ }
+
+ @Test
+ void isBalanced_testExample2() {
+ assertTrue(tree.isBalanced(null), "An empty tree should be considered balanced.");
+ }
+
+ @Test
+ void isBalanced_testExample3() {
+ TreeNode root = new TreeNode(3);
+ root.left = new TreeNode(9);
+ root.right = new TreeNode(20);
+ root.right.left = new TreeNode(15);
+ root.right.right = new TreeNode(7);
+
+ assertTrue(tree.isBalanced(root), "The tree should be balanced.");
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/shortthirdman/quickstart/hackerrank/DominantStringAnalyzerTest.java b/src/test/java/com/shortthirdman/quickstart/hackerrank/DominantStringAnalyzerTest.java
new file mode 100644
index 0000000..bf181b6
--- /dev/null
+++ b/src/test/java/com/shortthirdman/quickstart/hackerrank/DominantStringAnalyzerTest.java
@@ -0,0 +1,88 @@
+package com.shortthirdman.quickstart.hackerrank;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+
+class DominantStringAnalyzerTest {
+
+ DominantStringAnalyzer app;
+
+ @BeforeEach
+ void setUp() {
+ app = new DominantStringAnalyzer();
+ }
+
+ @Test
+ void testEmptyString() {
+ assertEquals(0, app.getDominantStringCount(""));
+ }
+
+ @Test
+ void testSingleCharacter() {
+ assertEquals(0, app.getDominantStringCount("a"));
+ }
+
+ @Test
+ void testTwoSameCharacters() {
+ assertEquals(0, app.getDominantStringCount("aa"));
+ }
+
+ @Test
+ void testTwoDifferentCharacters() {
+ assertEquals(1, app.getDominantStringCount("ab"));
+ }
+
+ @Test
+ void testFourSameCharacters() {
+ assertEquals(0, app.getDominantStringCount("aaaa"));
+ }
+
+ @Test
+ void testTwoPairsDominantSubstrings() {
+ assertEquals(2, app.getDominantStringCount("aabb"));
+ }
+
+ @Test
+ void testMixedCaseInput() {
+ assertEquals(2, app.getDominantStringCount("AaBb"));
+ }
+
+ @Test
+ void testNonLetterCharactersIncluded() {
+ assertEquals(4, app.getDominantStringCount("a1a1"));
+ }
+
+ @Test
+ void testRepeatingPattern() {
+ assertEquals(9, app.getDominantStringCount("ababab"));
+ }
+
+ @Test
+ void testOddLengthInput() {
+ assertEquals(2, app.getDominantStringCount("abc"));
+ }
+
+ @Test
+ void testPalindromeEvenLength() {
+ assertEquals(3, app.getDominantStringCount("abba"));
+ }
+
+ @Test
+ void testAllUniqueCharactersEvenLength() {
+ assertEquals(3, app.getDominantStringCount("abcd"));
+ }
+
+ @Test
+ void testLongerStringWithMultipleDominantSubstrings() {
+ assertEquals(10, app.getDominantStringCount("aabbaabb"));
+ }
+
+ @Test
+ void testExactHalfMultipleCharacters() {
+ assertNotEquals(1, app.getDominantStringCount("aaabbb"));
+ assertEquals(3, app.getDominantStringCount("aaabbb"));
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/shortthirdman/quickstart/hackerrank/OptimizingBoxWeightsTest.java b/src/test/java/com/shortthirdman/quickstart/hackerrank/OptimizingBoxWeightsTest.java
new file mode 100644
index 0000000..ff72e2d
--- /dev/null
+++ b/src/test/java/com/shortthirdman/quickstart/hackerrank/OptimizingBoxWeightsTest.java
@@ -0,0 +1,123 @@
+package com.shortthirdman.quickstart.hackerrank;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class OptimizingBoxWeightsTest {
+
+ OptimizingBoxWeights app;
+
+ @BeforeEach
+ void setUp() {
+ app = new OptimizingBoxWeights();
+ }
+
+ @Test
+ void testEmptyList() {
+ List input = new ArrayList<>();
+ List result = app.minimalHeaviestSetA(input);
+ assertTrue(result.isEmpty(), "The result should be an empty list.");
+ }
+
+ // Test case when the input list has only one element
+ @Test
+ void testSingleElement() {
+ List input = new ArrayList<>(Collections.singletonList(5));
+ List result = app.minimalHeaviestSetA(input);
+ assertEquals(input, result, "The result should be the same as the input.");
+ }
+
+ // Test case where all elements in the list are the same
+ @Test
+ void testAllElementsEqual() {
+ List input = new ArrayList<>(Arrays.asList(3, 3, 3, 3, 3));
+ List result = app.minimalHeaviestSetA(input);
+ assertEquals(Arrays.asList(3, 3, 3), result, "The result should be a list of the first half of the elements.");
+ }
+
+ // Test case with multiple elements where Set A needs to be chosen carefully
+ @Test
+ void testNormalCase() {
+ List input = new ArrayList<>(Arrays.asList(10, 20, 15, 5, 30, 25));
+ List expected = Arrays.asList(25, 30);
+ List result = app.minimalHeaviestSetA(input);
+ assertEquals(expected, result, "The minimal heaviest set A should be {25, 30}.");
+ }
+
+ // Test case with a list of increasing integers
+ @Test
+ void testIncreasingOrder() {
+ List input = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9));
+ List expected = Arrays.asList(6, 8, 9);
+ List result = app.minimalHeaviestSetA(input);
+ assertEquals(expected, result, "The minimal heaviest set A should be {6, 8, 9}.");
+ }
+
+ // Test case with a list of decreasing integers
+ @Test
+ void testDecreasingOrder() {
+ List input = new ArrayList<>(Arrays.asList(9, 8, 7, 6, 5, 4, 3, 2, 1));
+ List expected = Arrays.asList(6, 8, 9);
+ List result = app.minimalHeaviestSetA(input);
+ assertEquals(expected, result, "The minimal heaviest set A should be {6, 8, 9}.");
+ }
+
+ // Test case with large numbers
+ @Test
+ void testLargeNumbers() {
+ List input = new ArrayList<>(Arrays.asList(1000000, 2000000, 1500000, 500000, 3000000, 2500000));
+ List expected = Arrays.asList(2500000, 3000000);
+ List result = app.minimalHeaviestSetA(input);
+ assertEquals(expected, result, "The minimal heaviest set A should be {2500000, 3000000}.");
+ }
+
+ // Test case with two elements
+ @Test
+ void testTwoElements() {
+ List input = new ArrayList<>(Arrays.asList(8, 6));
+ List expected = Collections.singletonList(8);
+ List result = app.minimalHeaviestSetA(input);
+ assertEquals(expected, result, "The minimal heaviest set A should be {8}.");
+ }
+
+ // Test case where the set is split right at the middle of the list
+ @Test
+ void testSplitMiddle() {
+ List input = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));
+ List expected = Arrays.asList(5, 6);
+ List result = app.minimalHeaviestSetA(input);
+ assertEquals(expected, result, "The minimal heaviest set A should be {5, 6}.");
+ }
+
+ // Test case when all elements are larger than half of total sum
+ @Test
+ void testAllElementsLarge() {
+ List input = new ArrayList<>(Arrays.asList(50, 60, 70, 80, 90));
+ List expected = Arrays.asList(50, 80, 90);
+ List result = app.minimalHeaviestSetA(input);
+ assertEquals(expected, result, "The minimal heaviest set A should be {50, 80, 90}.");
+ }
+
+ // Test case with larger input size (stress test case)
+ @Test
+ void testLargeInput() {
+ List input = new ArrayList<>();
+ for (int i = 1; i <= 1000; i++) {
+ input.add(i);
+ }
+
+ List result = app.minimalHeaviestSetA(input);
+ int sumA = result.stream().mapToInt(Integer::intValue).sum();
+ int sumRemaining = input.stream().mapToInt(Integer::intValue).sum() - sumA;
+
+ assertTrue(sumA > sumRemaining, "Sum of Set A should be greater than the sum of the remaining elements.");
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/shortthirdman/quickstart/hackerrank/ReversePolishNotationTest.java b/src/test/java/com/shortthirdman/quickstart/hackerrank/ReversePolishNotationTest.java
new file mode 100644
index 0000000..b4f09ac
--- /dev/null
+++ b/src/test/java/com/shortthirdman/quickstart/hackerrank/ReversePolishNotationTest.java
@@ -0,0 +1,194 @@
+package com.shortthirdman.quickstart.hackerrank;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.EmptyStackException;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class ReversePolishNotationTest {
+
+ ReversePolishNotation app;
+
+ @BeforeEach
+ void setUp() {
+ app = new ReversePolishNotation();
+ }
+
+ // Positive test cases
+ @Test
+ void testAddition() {
+ String[] tokens = {"2", "1", "+"};
+ assertEquals(3, app.evaluateRPN(tokens));
+ }
+
+ @Test
+ void testSubtraction() {
+ String[] tokens = {"5", "3", "-"};
+ assertEquals(2, app.evaluateRPN(tokens));
+ }
+
+ @Test
+ void testMultiplication() {
+ String[] tokens = {"4", "2", "*"};
+ assertEquals(8, app.evaluateRPN(tokens));
+ }
+
+ @Test
+ void testDivision() {
+ String[] tokens = {"6", "3", "/"};
+ assertEquals(2, app.evaluateRPN(tokens));
+ }
+
+ @Test
+ void testComplexExpression() {
+ String[] tokens = {"2", "3", "+", "4", "*"};
+ assertEquals(20, app.evaluateRPN(tokens)); // (2 + 3) * 4
+ }
+
+ @Test
+ void testMultipleOperators() {
+ String[] tokens = {"5", "1", "2", "+", "4", "*", "+"};
+ assertNotEquals(14, app.evaluateRPN(tokens)); // 5 + (1 + 2) * 4
+ assertEquals(17, app.evaluateRPN(tokens));
+ }
+
+ // Negative test cases
+ @Test
+ void testInvalidOperator() {
+ String[] tokens = {"2", "3", "@"}; // Invalid operator
+ assertThrows(IllegalArgumentException.class, () -> app.evaluateRPN(tokens));
+ }
+
+ @Test
+ void testInsufficientOperands() {
+ String[] tokens = {"1", "+"}; // Not enough operands for operator
+ assertThrows(EmptyStackException.class, () -> app.evaluateRPN(tokens));
+ }
+
+ @Test
+ void testDivisionByZero() {
+ String[] tokens = {"1", "0", "/"};
+ assertThrows(ArithmeticException.class, () -> app.evaluateRPN(tokens));
+ }
+
+ // Edge cases
+ @Test
+ void testSingleNumber() {
+ String[] tokens = {"42"};
+ assertEquals(42, app.evaluateRPN(tokens)); // Single operand
+ }
+
+ @Test
+ void testMultipleSameOperators() {
+ String[] tokens = {"5", "5", "+", "5", "-"};
+ assertEquals(5, app.evaluateRPN(tokens)); // (5 + 5) - 5
+ }
+
+ @Test
+ void testZeroDivision() {
+ String[] tokens = {"4", "0", "/"};
+ assertThrows(ArithmeticException.class, () -> app.evaluateRPN(tokens)); // Division by zero
+ }
+
+ @Test
+ void testNegativeResult() {
+ String[] tokens = {"3", "5", "-"};
+ assertEquals(-2, app.evaluateRPN(tokens)); // 3 - 5
+ }
+
+ @Test
+ void testComplexNegativeResult() {
+ String[] tokens = {"4", "2", "*", "10", "-"};
+ assertEquals(-2, app.evaluateRPN(tokens)); // (4 * 2) - 10
+ }
+
+ // Positive test cases
+ @Test
+ void shouldReturnSumForAddition() {
+ String[] tokens = {"2", "1", "+"};
+ assertEquals(3, app.computeRPN(tokens));
+ }
+
+ @Test
+ void shouldReturnDifferenceForSubtraction() {
+ String[] tokens = {"5", "3", "-"};
+ assertEquals(2, app.computeRPN(tokens));
+ }
+
+ @Test
+ void shouldReturnProductForMultiplication() {
+ String[] tokens = {"4", "2", "*"};
+ assertEquals(8, app.computeRPN(tokens));
+ }
+
+ @Test
+ void shouldReturnQuotientForDivision() {
+ String[] tokens = {"6", "3", "/"};
+ assertEquals(2, app.computeRPN(tokens));
+ }
+
+ @Test
+ void shouldEvaluateComplexExpressionCorrectly() {
+ String[] tokens = {"2", "3", "+", "4", "*"};
+ assertEquals(20, app.computeRPN(tokens)); // (2 + 3) * 4
+ }
+
+ @Test
+ void shouldHandleMultipleOperatorsCorrectly() {
+ String[] tokens = {"5", "1", "2", "+", "4", "*", "+"};
+ assertEquals(17, app.computeRPN(tokens)); // 5 + (1 + 2) * 4
+ assertNotEquals(14, app.computeRPN(tokens)); // 5 + (1 + 2) * 4
+ }
+
+ // Negative test cases
+ @Test
+ void shouldThrowExceptionForInvalidOperator() {
+ String[] tokens = {"2", "3", "@"}; // Invalid operator
+ assertThrows(IllegalArgumentException.class, () -> app.computeRPN(tokens));
+ }
+
+ @Test
+ void shouldThrowExceptionForInsufficientOperands() {
+ String[] tokens = {"1", "+"}; // Not enough operands for operator
+ assertThrows(EmptyStackException.class, () -> app.computeRPN(tokens));
+ }
+
+ @Test
+ void shouldThrowExceptionForDivisionByZero() {
+ String[] tokens = {"1", "0", "/"};
+ assertThrows(ArithmeticException.class, () -> app.computeRPN(tokens));
+ }
+
+ // Edge cases
+ @Test
+ void shouldReturnSingleNumber() {
+ String[] tokens = {"42"};
+ assertEquals(42, app.computeRPN(tokens)); // Single operand
+ }
+
+ @Test
+ void shouldEvaluateMultipleSameOperators() {
+ String[] tokens = {"5", "5", "+", "5", "-"};
+ assertEquals(5, app.computeRPN(tokens)); // (5 + 5) - 5
+ }
+
+ @Test
+ void shouldThrowExceptionForZeroDivision() {
+ String[] tokens = {"4", "0", "/"};
+ assertThrows(ArithmeticException.class, () -> app.computeRPN(tokens)); // Division by zero
+ }
+
+ @Test
+ void shouldReturnNegativeResultForSubtraction() {
+ String[] tokens = {"3", "5", "-"};
+ assertEquals(-2, app.computeRPN(tokens)); // 3 - 5
+ }
+
+ @Test
+ void shouldEvaluateComplexNegativeResult() {
+ String[] tokens = {"4", "2", "*", "10", "-"};
+ assertEquals(-2, app.computeRPN(tokens)); // (4 * 2) - 10
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/shortthirdman/quickstart/hackerrank/StoneWallTest.java b/src/test/java/com/shortthirdman/quickstart/hackerrank/StoneWallTest.java
new file mode 100644
index 0000000..fb014c4
--- /dev/null
+++ b/src/test/java/com/shortthirdman/quickstart/hackerrank/StoneWallTest.java
@@ -0,0 +1,76 @@
+package com.shortthirdman.quickstart.hackerrank;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class StoneWallTest {
+
+ StoneWall app;
+
+ @BeforeEach
+ void setUp() {
+ app = new StoneWall();
+ }
+
+ // Positive Test Cases
+ @Test
+ public void testSingleHeight() {
+ int[] heights = {5};
+ assertEquals(5, app.coverManhattanSkyline(heights));
+ }
+
+ @Test
+ public void testUniformHeight() {
+ int[] heights = {3, 3, 3};
+ assertEquals(9, app.coverManhattanSkyline(heights));
+ }
+
+ @Test
+ public void testIncreasingHeights() {
+ int[] heights = {1, 2, 3, 4};
+ assertEquals(10, app.coverManhattanSkyline(heights));
+ }
+
+ @Test
+ public void testDecreasingHeights() {
+ int[] heights = {4, 3, 2, 1};
+ assertEquals(10, app.coverManhattanSkyline(heights));
+ }
+
+ @Test
+ public void testVaryingHeights() {
+ int[] heights = {2, 1, 4, 3};
+ assertEquals(10, app.coverManhattanSkyline(heights));
+ }
+
+ @Test
+ public void testLargeHeights() {
+ int[] heights = {1000, 2000, 3000};
+ assertEquals(6000, app.coverManhattanSkyline(heights));
+ }
+
+ // Edge Test Cases
+ @Test
+ public void testEmptyInput() {
+ int[] heights = {};
+ Exception exception = assertThrows(IllegalArgumentException.class, () -> app.coverManhattanSkyline(heights));
+ assertEquals("Number of height-blocks should be in range [1,100000]", exception.getMessage());
+ }
+
+ // Negative Test Cases
+ @Test
+ public void testNegativeHeights() {
+ int[] heights = {-1, -2, -3};
+ Exception exception = assertThrows(IllegalArgumentException.class, () -> app.coverManhattanSkyline(heights));
+ assertEquals("Height values must be positive", exception.getMessage());
+ }
+
+ @Test
+ public void testZeroHeight() {
+ int[] heights = {0, 1, 2};
+ Exception exception = assertThrows(IllegalArgumentException.class, () -> app.coverManhattanSkyline(heights));
+ assertEquals("Height values must be positive", exception.getMessage());
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/shortthirdman/quickstart/hackerrank/StreamApiFactoryTest.java b/src/test/java/com/shortthirdman/quickstart/hackerrank/StreamApiFactoryTest.java
new file mode 100644
index 0000000..305b955
--- /dev/null
+++ b/src/test/java/com/shortthirdman/quickstart/hackerrank/StreamApiFactoryTest.java
@@ -0,0 +1,163 @@
+package com.shortthirdman.quickstart.hackerrank;
+
+import com.shortthirdman.quickstart.common.EmployeeRecord;
+import com.shortthirdman.quickstart.common.Transaction;
+import org.instancio.Instancio;
+import org.instancio.junit.InstancioExtension;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@ExtendWith(InstancioExtension.class)
+class StreamApiFactoryTest {
+
+ StreamApiFactory app;
+
+ @BeforeEach
+ void setUp() {
+ app = new StreamApiFactory();
+ }
+
+ @Test
+ void concatenateEvenLengthWords() {
+ List words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry");
+ String actualWords = app.concatenateEvenLengthWords(words);
+ assertNotEquals("bananacherrydateelderberry", actualWords);
+ assertEquals("bananacherry", actualWords);
+ assertEquals("bananacherry".length(), actualWords.length());
+
+ assertThrows(IllegalArgumentException.class, () -> app.concatenateEvenLengthWords(List.of()));
+ assertThrows(IllegalArgumentException.class, () -> app.concatenateEvenLengthWords(null));
+
+ words = Arrays.asList("a", "abc", "def");
+ assertEquals("", app.concatenateEvenLengthWords(words));
+
+ words = List.of("test");
+ assertEquals("test", app.concatenateEvenLengthWords(words));
+ }
+
+ @Test
+ void countCharacterFrequency() {
+ String str = "Now is the winter";
+ var actualCharacterFrequency = app.countCharacterFrequency(str);
+ var expectedCharacterFrequency = Map.ofEntries(
+ Map.entry(" ", 3L),
+ Map.entry("r", 1L),
+ Map.entry("s", 1L),
+ Map.entry("t", 2L),
+ Map.entry("e", 2L),
+ Map.entry("w", 2L),
+ Map.entry("h", 1L),
+ Map.entry("i", 2L),
+ Map.entry("n", 1L),
+ Map.entry("N", 1L),
+ Map.entry("o", 1L)
+ );
+ assertEquals(expectedCharacterFrequency.size(), actualCharacterFrequency.size());
+ assertEquals(expectedCharacterFrequency.keySet(), actualCharacterFrequency.keySet());
+ assertEquals(expectedCharacterFrequency, actualCharacterFrequency);
+ assertThrows(IllegalArgumentException.class, () -> app.countCharacterFrequency(null));
+ assertThrows(IllegalArgumentException.class, () -> app.countCharacterFrequency(""));
+ }
+
+ @Test
+ void getDailyTransactionSums() {
+ List transactions = Arrays.asList(
+ new Transaction("2022-01-01", 100),
+ new Transaction("2022-01-01", 200),
+ new Transaction("2022-01-02", 300)
+ );
+ Map actualDailyTotals = app.getDailyTransactionSums(transactions);
+ Map expectedDailyTotals = Map.ofEntries(
+ Map.entry("2022-01-01", 300L),
+ Map.entry("2022-01-02", 300L)
+ );
+ assertEquals(expectedDailyTotals, actualDailyTotals);
+
+ transactions = Instancio.createList(Transaction.class);
+ assertFalse(transactions.isEmpty());
+ }
+
+ @Test
+ void toSortedMapWithDuplicates() {
+ List employees = Arrays.asList(
+ new EmployeeRecord(101, "Alice"),
+ new EmployeeRecord(102, "Bob"),
+ new EmployeeRecord(101, "Charlie"),
+ new EmployeeRecord(103, "David"),
+ new EmployeeRecord(102, "Eve")
+ );
+ Map> expectedEmployeeMap = Map.ofEntries(
+ Map.entry(101, List.of(new EmployeeRecord(101, "Alice"), new EmployeeRecord(101, "Charlie"))),
+ Map.entry(102, List.of(new EmployeeRecord(102, "Bob"), new EmployeeRecord(102, "Eve"))),
+ Map.entry(103, List.of(new EmployeeRecord(103, "David")))
+ );
+ Map> actualEmployeeMap = app.toSortedMapWithDuplicates(employees);
+ assertEquals(expectedEmployeeMap.size(), actualEmployeeMap.size());
+ assertEquals(expectedEmployeeMap, actualEmployeeMap);
+ assertThrows(IllegalArgumentException.class, () -> app.toSortedMapWithDuplicates(null));
+ assertThrows(IllegalArgumentException.class, () -> app.toSortedMapWithDuplicates(List.of()));
+ }
+
+ @Test
+ void countStringsByFirstCharacter() {
+ List words = Arrays.asList("apple", "banana", "bear", "cat", "apple");
+ Map expectedFrequencies = Map.ofEntries(
+ Map.entry('a', 2L),
+ Map.entry('b', 2L),
+ Map.entry('c', 1L)
+ );
+ Map actualFrequencies = app.countStringsByFirstCharacter(words);
+ assertEquals(expectedFrequencies, actualFrequencies);
+ assertEquals(expectedFrequencies.size(), actualFrequencies.size());
+ assertThrows(IllegalArgumentException.class, () -> app.countStringsByFirstCharacter(null));
+ assertThrows(IllegalArgumentException.class, () -> app.countStringsByFirstCharacter(List.of()));
+ }
+
+ @Test
+ void mergeSortAndFilterByThreshold() {
+ int[] array1 = {1, 5, 3, 9, 7};
+ int[] array2 = {2, 4, 6, 8, 10};
+ int threshold = 7;
+ var actualIntegers = app.mergeSortAndFilterByThreshold(array1, array2, threshold);
+ var expectedIntegers = Arrays.asList(8, 9, 10);
+ assertEquals(expectedIntegers, actualIntegers);
+
+ assertThrows(IllegalArgumentException.class, () -> app.mergeSortAndFilterByThreshold(array1, null, 7));
+ assertThrows(IllegalArgumentException.class, () -> app.mergeSortAndFilterByThreshold(null, null, 7));
+ }
+
+ @Test
+ void partitionByPrime() {
+ var numbers = Arrays.asList(2, 3, 4, 5, 6, 7, 8, 9, 10);
+ var actualPartitionByPrime = app.partitionByPrime(numbers);
+ var expectedPartitionByPrime = Map.ofEntries(
+ Map.entry(true, List.of(2, 3, 5, 7)),
+ Map.entry(false, List.of(4, 6, 8, 9, 10))
+ );
+ assertEquals(expectedPartitionByPrime.size(), actualPartitionByPrime.size());
+ assertEquals(expectedPartitionByPrime.get(true).size(), actualPartitionByPrime.get(true).size());
+ assertEquals(expectedPartitionByPrime.get(false).size(), actualPartitionByPrime.get(false).size());
+ assertEquals(expectedPartitionByPrime, actualPartitionByPrime);
+ assertThrows(IllegalArgumentException.class, () -> app.partitionByPrime(null));
+ assertThrows(IllegalArgumentException.class, () -> app.partitionByPrime(List.of()));
+ }
+
+ @Test
+ void countTotalUniqueWords() {
+ var sentences = Arrays.asList(
+ "Java Stream API provides a fluent interface",
+ "It supports functional-style operations on streams",
+ "In this exercise, you need to count words"
+ );
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/shortthirdman/quickstart/leetcode/MinimumBinaryFlipsTest.java b/src/test/java/com/shortthirdman/quickstart/leetcode/MinimumBinaryFlipsTest.java
index 1aef8dc..3876b1a 100644
--- a/src/test/java/com/shortthirdman/quickstart/leetcode/MinimumBinaryFlipsTest.java
+++ b/src/test/java/com/shortthirdman/quickstart/leetcode/MinimumBinaryFlipsTest.java
@@ -1,10 +1,10 @@
package com.shortthirdman.quickstart.leetcode;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+
public class MinimumBinaryFlipsTest {
MinimumBinaryFlips app;
@@ -24,4 +24,85 @@ public void testGetMinFlips_defaultScenario() {
assertEquals(2, app.getMinFlips(secondPwd));
assertEquals(3, app.getMinFlips(thirdPwd));
}
+
+ // Positive Test Cases
+ @Test
+ void testAllZeros() {
+ assertEquals(0, app.getMinFlips("0000"));
+ }
+
+ @Test
+ void testAllOnes() {
+ assertEquals(0, app.getMinFlips("1111"));
+ }
+
+ @Test
+ void testAlternatingCharacters() {
+ assertEquals(3, app.getMinFlips("010101"));
+ assertNotEquals(2, app.getMinFlips("010101"));
+ }
+
+ @Test
+ void testMixedCharacters() {
+ assertEquals(0, app.getMinFlips("1100"));
+ assertNotEquals(1, app.getMinFlips("1100"));
+ }
+
+ @Test
+ void testShortString() {
+ assertEquals(0, app.getMinFlips("1")); // Edge case with a single character
+ }
+
+ @Test
+ void testTwoDifferentChars() {
+ assertEquals(1, app.getMinFlips("01"));
+ }
+
+ // Negative Test Cases
+ @Test
+ void testEmptyString() {
+ assertEquals(0, app.getMinFlips(""));
+ }
+
+ @Test
+ void testSingleCharacterZero() {
+ assertEquals(0, app.getMinFlips("0"));
+ }
+
+ @Test
+ void testSingleCharacterOne() {
+ assertEquals(0, app.getMinFlips("1"));
+ }
+
+ @Test
+ void testTwoSameChars() {
+ assertEquals(0, app.getMinFlips("00")); // Both are zeros
+ assertEquals(0, app.getMinFlips("11")); // Both are ones
+ }
+
+ // Edge Cases
+ @Test
+ void testLargeInput() {
+ StringBuilder largeInput = new StringBuilder();
+ for (int i = 0; i < 10000; i++) {
+ largeInput.append(i % 2 == 0 ? '0' : '1');
+ }
+ assertEquals(5000, app.getMinFlips(largeInput.toString()));
+ }
+
+ @Test
+ void testLongSameChars() {
+ String longZeros = "0".repeat(10000);
+ assertEquals(0, app.getMinFlips(longZeros));
+
+ String longOnes = "1".repeat(10000);
+ assertEquals(0, app.getMinFlips(longOnes));
+ }
+
+ // Exception Handling Test Cases
+ @Test
+ void testNullInput() {
+ Exception exception = assertThrows(NullPointerException.class, () -> app.getMinFlips(null));
+ assertEquals("Input text password can not be null", exception.getMessage());
+ }
}