diff --git a/src/main/java/com/thealgorithms/stacks/NearestElement.java b/src/main/java/com/thealgorithms/stacks/NearestElement.java new file mode 100644 index 000000000000..a4a0854840b8 --- /dev/null +++ b/src/main/java/com/thealgorithms/stacks/NearestElement.java @@ -0,0 +1,102 @@ +package com.thealgorithms.stacks; + +import java.util.Stack; + +/** + * Implements classic stack-based algorithms to find nearest elements. + * + * Algorithms included: + * 1. Nearest Greater to Right + * 2. Nearest Greater to Left + * 3. Nearest Smaller to Right + * 4. Nearest Smaller to Left + */ +public final class NearestElement { + // Private constructor to prevent instantiation + private NearestElement() { + } + + /** Finds the nearest greater element to the right for each element in the array. */ + public static int[] nearestGreaterToRight(int[] arr) { + if (arr == null) { + throw new IllegalArgumentException("Input array cannot be null"); + } + + int n = arr.length; + int[] result = new int[n]; + Stack stack = new Stack<>(); + + for (int i = n - 1; i >= 0; i--) { + while (!stack.isEmpty() && stack.peek() <= arr[i]) { + stack.pop(); + } + result[i] = stack.isEmpty() ? -1 : stack.peek(); + stack.push(arr[i]); + } + + return result; + } + + /** Finds the nearest greater element to the left for each element in the array. */ + public static int[] nearestGreaterToLeft(int[] arr) { + if (arr == null) { + throw new IllegalArgumentException("Input array cannot be null"); + } + + int n = arr.length; + int[] result = new int[n]; + Stack stack = new Stack<>(); + + for (int i = 0; i < n; i++) { + while (!stack.isEmpty() && stack.peek() <= arr[i]) { + stack.pop(); + } + result[i] = stack.isEmpty() ? -1 : stack.peek(); + stack.push(arr[i]); + } + + return result; + } + + /** Finds the nearest smaller element to the right for each element in the array. */ + public static int[] nearestSmallerToRight(int[] arr) { + if (arr == null) { + throw new IllegalArgumentException("Input array cannot be null"); + } + + int n = arr.length; + int[] result = new int[n]; + Stack stack = new Stack<>(); + + for (int i = n - 1; i >= 0; i--) { + while (!stack.isEmpty() && stack.peek() >= arr[i]) { + stack.pop(); + } + result[i] = stack.isEmpty() ? -1 : stack.peek(); + stack.push(arr[i]); + } + + return result; + } + + /** Finds the nearest smaller element to the left for each element in the array. */ + public static int[] nearestSmallerToLeft(int[] arr) { + if (arr == null) { + throw new IllegalArgumentException("Input array cannot be null"); + } + + int n = arr.length; + int[] result = new int[n]; + Stack stack = new Stack<>(); + + for (int i = 0; i < n; i++) { + while (!stack.isEmpty() && stack.peek() >= arr[i]) { + stack.pop(); + } + result[i] = stack.isEmpty() ? -1 : stack.peek(); + stack.push(arr[i]); + } + + return result; + } +} diff --git a/src/test/java/com/thealgorithms/stacks/NearestElementTest.java b/src/test/java/com/thealgorithms/stacks/NearestElementTest.java new file mode 100644 index 000000000000..817163cd1a8a --- /dev/null +++ b/src/test/java/com/thealgorithms/stacks/NearestElementTest.java @@ -0,0 +1,61 @@ +package com.thealgorithms.stacks; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +class NearestElementTest { + + @ParameterizedTest + @MethodSource("provideGreaterToRightTestCases") + void testNearestGreaterToRight(int[] input, int[] expected) { + assertArrayEquals(expected, NearestElement.nearestGreaterToRight(input)); + } + + static Stream provideGreaterToRightTestCases() { + return Stream.of(Arguments.of(new int[] {4, 5, 2, 10, 8}, new int[] {5, 10, 10, -1, -1}), Arguments.of(new int[] {5}, new int[] {-1}), Arguments.of(new int[] {}, new int[] {})); + } + + @ParameterizedTest + @MethodSource("provideGreaterToLeftTestCases") + void testNearestGreaterToLeft(int[] input, int[] expected) { + assertArrayEquals(expected, NearestElement.nearestGreaterToLeft(input)); + } + + static Stream provideGreaterToLeftTestCases() { + return Stream.of(Arguments.of(new int[] {4, 5, 2, 10, 8}, new int[] {-1, -1, 5, -1, 10}), Arguments.of(new int[] {5}, new int[] {-1}), Arguments.of(new int[] {}, new int[] {})); + } + + @ParameterizedTest + @MethodSource("provideSmallerToRightTestCases") + void testNearestSmallerToRight(int[] input, int[] expected) { + assertArrayEquals(expected, NearestElement.nearestSmallerToRight(input)); + } + + static Stream provideSmallerToRightTestCases() { + return Stream.of(Arguments.of(new int[] {4, 5, 2, 10, 8}, new int[] {2, 2, -1, 8, -1}), Arguments.of(new int[] {5}, new int[] {-1}), Arguments.of(new int[] {}, new int[] {})); + } + + @ParameterizedTest + @MethodSource("provideSmallerToLeftTestCases") + void testNearestSmallerToLeft(int[] input, int[] expected) { + assertArrayEquals(expected, NearestElement.nearestSmallerToLeft(input)); + } + + static Stream provideSmallerToLeftTestCases() { + return Stream.of(Arguments.of(new int[] {4, 5, 2, 10, 8}, new int[] {-1, 4, -1, 2, 2}), Arguments.of(new int[] {5}, new int[] {-1}), Arguments.of(new int[] {}, new int[] {})); + } + + @Test + void testNullInput() { + assertThrows(IllegalArgumentException.class, () -> NearestElement.nearestGreaterToRight(null)); + assertThrows(IllegalArgumentException.class, () -> NearestElement.nearestGreaterToLeft(null)); + assertThrows(IllegalArgumentException.class, () -> NearestElement.nearestSmallerToRight(null)); + assertThrows(IllegalArgumentException.class, () -> NearestElement.nearestSmallerToLeft(null)); + } +}