Skip to content

Commit 47ed73c

Browse files
committed
feat: Add Nearest Greater and Smaller Element Algorithms
1 parent b4739f8 commit 47ed73c

File tree

2 files changed

+114
-65
lines changed

2 files changed

+114
-65
lines changed
Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,101 @@
11
package com.thealgorithms.stacks;
22

33
import java.util.Stack;
4-
import java.util.Arrays;
54

5+
/**
6+
* This class provides four algorithms to find the nearest greater or smaller elements
7+
* on either side of each element in an array using a stack.
8+
*
9+
* <p>These algorithms are fundamental examples of the monotonic stack approach, commonly
10+
* used in competitive programming and technical interviews. Each method runs in O(n)
11+
* time complexity and O(n) space complexity.
12+
*/
613
public final class NearestElement {
714

15+
// Private constructor to prevent instantiation
816
private NearestElement() {}
917

18+
/**
19+
* Finds the nearest greater element to the right for each element in the array.
20+
*
21+
* @param arr the input array
22+
* @return an array containing the nearest greater element to the right for each element
23+
*/
1024
public static int[] nearestGreaterToRight(int[] arr) {
1125
int n = arr.length;
1226
int[] result = new int[n];
13-
Stack<Integer> indexStack = new Stack<>();
27+
Stack<Integer> stack = new Stack<>();
28+
1429
for (int i = n - 1; i >= 0; i--) {
15-
while (!indexStack.isEmpty() && arr[i] >= arr[indexStack.peek()]) {
16-
indexStack.pop();
30+
while (!stack.isEmpty() && stack.peek() <= arr[i]) {
31+
stack.pop();
1732
}
18-
result[i] = indexStack.isEmpty() ? -1 : arr[indexStack.peek()];
19-
indexStack.push(i);
33+
result[i] = stack.isEmpty() ? -1 : stack.peek();
34+
stack.push(arr[i]);
2035
}
2136
return result;
2237
}
2338

39+
/**
40+
* Finds the nearest greater element to the left for each element in the array.
41+
*
42+
* @param arr the input array
43+
* @return an array containing the nearest greater element to the left for each element
44+
*/
2445
public static int[] nearestGreaterToLeft(int[] arr) {
2546
int n = arr.length;
2647
int[] result = new int[n];
27-
Stack<Integer> indexStack = new Stack<>();
48+
Stack<Integer> stack = new Stack<>();
49+
2850
for (int i = 0; i < n; i++) {
29-
while (!indexStack.isEmpty() && arr[i] >= arr[indexStack.peek()]) {
30-
indexStack.pop();
51+
while (!stack.isEmpty() && stack.peek() <= arr[i]) {
52+
stack.pop();
3153
}
32-
result[i] = indexStack.isEmpty() ? -1 : arr[indexStack.peek()];
33-
indexStack.push(i);
54+
result[i] = stack.isEmpty() ? -1 : stack.peek();
55+
stack.push(arr[i]);
3456
}
3557
return result;
3658
}
3759

60+
/**
61+
* Finds the nearest smaller element to the right for each element in the array.
62+
*
63+
* @param arr the input array
64+
* @return an array containing the nearest smaller element to the right for each element
65+
*/
3866
public static int[] nearestSmallerToRight(int[] arr) {
3967
int n = arr.length;
4068
int[] result = new int[n];
41-
Stack<Integer> indexStack = new Stack<>();
69+
Stack<Integer> stack = new Stack<>();
70+
4271
for (int i = n - 1; i >= 0; i--) {
43-
while (!indexStack.isEmpty() && arr[i] <= arr[indexStack.peek()]) {
44-
indexStack.pop();
72+
while (!stack.isEmpty() && stack.peek() >= arr[i]) {
73+
stack.pop();
4574
}
46-
result[i] = indexStack.isEmpty() ? -1 : arr[indexStack.peek()];
47-
indexStack.push(i);
75+
result[i] = stack.isEmpty() ? -1 : stack.peek();
76+
stack.push(arr[i]);
4877
}
4978
return result;
5079
}
5180

81+
/**
82+
* Finds the nearest smaller element to the left for each element in the array.
83+
*
84+
* @param arr the input array
85+
* @return an array containing the nearest smaller element to the left for each element
86+
*/
5287
public static int[] nearestSmallerToLeft(int[] arr) {
5388
int n = arr.length;
5489
int[] result = new int[n];
55-
Stack<Integer> indexStack = new Stack<>();
90+
Stack<Integer> stack = new Stack<>();
91+
5692
for (int i = 0; i < n; i++) {
57-
while (!indexStack.isEmpty() && arr[i] <= arr[indexStack.peek()]) {
58-
indexStack.pop();
93+
while (!stack.isEmpty() && stack.peek() >= arr[i]) {
94+
stack.pop();
5995
}
60-
result[i] = indexStack.isEmpty() ? -1 : arr[indexStack.peek()];
61-
indexStack.push(i);
96+
result[i] = stack.isEmpty() ? -1 : stack.peek();
97+
stack.push(arr[i]);
6298
}
6399
return result;
64100
}
65-
66-
public static void main(String[] args) {
67-
int[] sampleArray = {4, 5, 2, 10, 8};
68-
System.out.println("Nearest Greater to Right: " + Arrays.toString(nearestGreaterToRight(sampleArray)));
69-
System.out.println("Nearest Greater to Left: " + Arrays.toString(nearestGreaterToLeft(sampleArray)));
70-
System.out.println("Nearest Smaller to Right: " + Arrays.toString(nearestSmallerToRight(sampleArray)));
71-
System.out.println("Nearest Smaller to Left: " + Arrays.toString(nearestSmallerToLeft(sampleArray)));
72-
}
73101
}
Lines changed: 57 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,77 @@
11
package com.thealgorithms.stacks;
22

3-
import org.junit.jupiter.api.Test;
43
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
6+
import java.util.stream.Stream;
7+
import org.junit.jupiter.api.Test;
8+
import org.junit.jupiter.params.ParameterizedTest;
9+
import org.junit.jupiter.params.provider.Arguments;
10+
import org.junit.jupiter.params.provider.MethodSource;
511

6-
public class NearestElementTest {
12+
class NearestElementTest {
713

8-
@Test
9-
public void testNearestGreaterToRight() {
10-
int[] arr = {4, 5, 2, 10, 8};
11-
int[] expected = {5, 10, 10, -1, -1};
12-
assertArrayEquals(expected, NearestElement.nearestGreaterToRight(arr));
14+
@ParameterizedTest
15+
@MethodSource("provideGreaterToRightTestCases")
16+
void testNearestGreaterToRight(int[] input, int[] expected) {
17+
assertArrayEquals(expected, NearestElement.nearestGreaterToRight(input));
1318
}
1419

15-
@Test
16-
public void testNearestGreaterToLeft() {
17-
int[] arr = {4, 5, 2, 10, 8};
18-
int[] expected = {-1, -1, 5, -1, 10};
19-
assertArrayEquals(expected, NearestElement.nearestGreaterToLeft(arr));
20+
static Stream<Arguments> provideGreaterToRightTestCases() {
21+
return Stream.of(
22+
Arguments.of(new int[] {4, 5, 2, 10, 8}, new int[] {5, 10, 10, -1, -1}),
23+
Arguments.of(new int[] {5}, new int[] {-1}),
24+
Arguments.of(new int[] {}, new int[] {})
25+
);
2026
}
2127

22-
@Test
23-
public void testNearestSmallerToRight() {
24-
int[] arr = {4, 5, 2, 10, 8};
25-
int[] expected = {2, 2, -1, 8, -1};
26-
assertArrayEquals(expected, NearestElement.nearestSmallerToRight(arr));
28+
@ParameterizedTest
29+
@MethodSource("provideGreaterToLeftTestCases")
30+
void testNearestGreaterToLeft(int[] input, int[] expected) {
31+
assertArrayEquals(expected, NearestElement.nearestGreaterToLeft(input));
2732
}
2833

29-
@Test
30-
public void testNearestSmallerToLeft() {
31-
int[] arr = {4, 5, 2, 10, 8};
32-
int[] expected = {-1, 4, -1, 2, 2};
33-
assertArrayEquals(expected, NearestElement.nearestSmallerToLeft(arr));
34+
static Stream<Arguments> provideGreaterToLeftTestCases() {
35+
return Stream.of(
36+
Arguments.of(new int[] {4, 5, 2, 10, 8}, new int[] {-1, -1, 5, -1, 10}),
37+
Arguments.of(new int[] {5}, new int[] {-1}),
38+
Arguments.of(new int[] {}, new int[] {})
39+
);
3440
}
3541

36-
@Test
37-
void testEmptyArray() {
38-
int[] arr = {};
39-
assertArrayEquals(new int[]{}, NearestElement.nearestGreaterToRight(arr));
40-
assertArrayEquals(new int[]{}, NearestElement.nearestGreaterToLeft(arr));
42+
@ParameterizedTest
43+
@MethodSource("provideSmallerToRightTestCases")
44+
void testNearestSmallerToRight(int[] input, int[] expected) {
45+
assertArrayEquals(expected, NearestElement.nearestSmallerToRight(input));
4146
}
4247

43-
@Test
44-
void testAllEqualElements() {
45-
int[] arr = {5, 5, 5, 5};
46-
assertArrayEquals(new int[]{-1, -1, -1, -1}, NearestElement.nearestGreaterToRight(arr));
47-
assertArrayEquals(new int[]{-1, -1, -1, -1}, NearestElement.nearestGreaterToLeft(arr));
48+
static Stream<Arguments> provideSmallerToRightTestCases() {
49+
return Stream.of(
50+
Arguments.of(new int[] {4, 5, 2, 10, 8}, new int[] {2, 2, -1, 8, -1}),
51+
Arguments.of(new int[] {5}, new int[] {-1}),
52+
Arguments.of(new int[] {}, new int[] {})
53+
);
54+
}
55+
56+
@ParameterizedTest
57+
@MethodSource("provideSmallerToLeftTestCases")
58+
void testNearestSmallerToLeft(int[] input, int[] expected) {
59+
assertArrayEquals(expected, NearestElement.nearestSmallerToLeft(input));
60+
}
61+
62+
static Stream<Arguments> provideSmallerToLeftTestCases() {
63+
return Stream.of(
64+
Arguments.of(new int[] {4, 5, 2, 10, 8}, new int[] {-1, 4, -1, 2, 2}),
65+
Arguments.of(new int[] {5}, new int[] {-1}),
66+
Arguments.of(new int[] {}, new int[] {})
67+
);
4868
}
4969

5070
@Test
51-
void testPrivateConstructor() throws Exception {
52-
var constructor = NearestElement.class.getDeclaredConstructor();
53-
constructor.setAccessible(true);
54-
constructor.newInstance();
71+
void testNullInput() {
72+
assertThrows(IllegalArgumentException.class, () -> NearestElement.nearestGreaterToRight(null));
73+
assertThrows(IllegalArgumentException.class, () -> NearestElement.nearestGreaterToLeft(null));
74+
assertThrows(IllegalArgumentException.class, () -> NearestElement.nearestSmallerToRight(null));
75+
assertThrows(IllegalArgumentException.class, () -> NearestElement.nearestSmallerToLeft(null));
5576
}
5677
}

0 commit comments

Comments
 (0)