Skip to content

Commit 7a26aec

Browse files
Next Greater Element with Monotonic Stack
1 parent 30ee311 commit 7a26aec

File tree

3 files changed

+239
-0
lines changed

3 files changed

+239
-0
lines changed
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package stack.next_greater_element;
2+
3+
import java.util.ArrayDeque;
4+
import java.util.Deque;
5+
6+
/**
7+
* Implements a solution to the "Next Greater Element" problem using a Monotonic Stack.
8+
*
9+
* <p>A monotonic stack is a stack whose elements are always in a sorted order
10+
* (either increasing or decreasing). This data structure is highly efficient for
11+
* problems involving finding the next or previous greater/smaller element in an array.
12+
*
13+
* <p>This implementation finds the next greater element for each element in an array
14+
* by iterating from right to left and maintaining a monotonically decreasing stack.
15+
*
16+
* <p><b>Complexity:</b>
17+
* <ul>
18+
* <li>Time: O(N), where N is the number of elements in the array. Each element is
19+
* pushed and popped from the stack at most once.</li>
20+
* <li>Space: O(N), for storing the stack and the answer array.</li>
21+
* </ul>
22+
*/
23+
public class MonotonicStack {
24+
public final int[] answer; // Public for easy access in test cases
25+
private final int[] array;
26+
27+
/**
28+
* Initializes the data structure with the input array.
29+
*
30+
* @param array The input array for which to find the next greater elements.
31+
*/
32+
public MonotonicStack(int[] array) {
33+
this.array = array;
34+
this.answer = new int[array.length];
35+
}
36+
37+
/**
38+
* Populates the {@code answer} array with the next greater element for each
39+
* element in the input array.
40+
*
41+
* <p>The algorithm iterates through the input array from right to left. A stack
42+
* is used to keep track of elements that could be the "next greater" for
43+
* elements to their left.
44+
*
45+
* <p>For each element {@code array[i]}:
46+
* <ol>
47+
* <li>While the stack is not empty and the element at the top of the stack is
48+
* less than or equal to {@code array[i]}, pop from the stack. This removes
49+
* elements that can never be the next greater element for anything to the left.</li>
50+
* <li>After popping, if the stack is empty, it means there is no greater element
51+
* to the right. Otherwise, the element at the top of the stack is the
52+
* next greater element.</li>
53+
* <li>Push the current element {@code array[i]} onto the stack to be a potential
54+
* next greater element for items to its left.</li>
55+
* </ol>
56+
*/
57+
public void findNextGreaterElement() {
58+
// Using Deque is the recommended modern approach for stack implementations.
59+
Deque<Integer> stack = new ArrayDeque<>();
60+
61+
for (int i = array.length - 1; i >= 0; i--) {
62+
// Pop elements from the stack that are smaller than or equal to the current element.
63+
while (!stack.isEmpty() && stack.peek() <= array[i]) {
64+
stack.pop();
65+
}
66+
67+
// If the stack is empty, no greater element exists to the right.
68+
// Otherwise, the top of the stack is the next greater element.
69+
answer[i] = stack.isEmpty() ? -1 : stack.peek();
70+
71+
// Push the current element onto the stack for future comparisons.
72+
stack.push(array[i]);
73+
}
74+
}
75+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Provides solutions for the "Next Greater Element" problem using a Monotonic Stack.
3+
*
4+
* <p>The "Next Greater Element" problem involves finding the first element to the
5+
* right of a given element in an array that is strictly greater. If no such
6+
* element exists, a default value (e.g., -1) is used.
7+
*
8+
* <h3>Core Implementation Strategy: Monotonic Stack</h3>
9+
*
10+
* <p>This package utilizes a <strong>Monotonic Stack</strong>, a highly efficient
11+
* data structure for this category of problems. A monotonic stack maintains its
12+
* elements in a sorted order (either increasing or decreasing).
13+
*
14+
* <p>The general algorithm is as follows:
15+
* <ol>
16+
* <li>Iterate through the input array, typically from right to left.</li>
17+
* <li>For each element, pop elements from the stack that are less than or
18+
* equal to the current element. This ensures the stack's monotonic property
19+
* is maintained.</li>
20+
* <li>The element now at the top of the stack is the "next greater element" for
21+
* the current array element. If the stack is empty, no greater element exists.</li>
22+
* <li>Push the current element onto the stack.</li>
23+
* </ol>
24+
*
25+
* <p>This approach guarantees that each element is pushed and popped at most once,
26+
* resulting in an optimal time complexity of O(N).
27+
*
28+
* @version 1.0
29+
* @see stack.next_greater_element.MonotonicStack
30+
* @since 1.0
31+
*/
32+
package stack.next_greater_element;
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package stack.next_greater_element;
2+
3+
import org.junit.jupiter.api.DisplayName;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
7+
8+
/**
9+
* Test suite for the {@link MonotonicStack} class.
10+
*/
11+
class MonotonicStackTest {
12+
13+
@Test
14+
@DisplayName("Should find the next greater element in a general case array")
15+
void findNextGreaterElement_generalCase() {
16+
// Arrange
17+
int[] array = {2, 1, 5, 6, 2, 3};
18+
int[] expected = {5, 5, 6, -1, 3, -1};
19+
MonotonicStack monotonicStack = new MonotonicStack(array);
20+
21+
// Act
22+
monotonicStack.findNextGreaterElement();
23+
24+
// Assert
25+
assertArrayEquals(expected, monotonicStack.answer, "Should correctly find next greater elements");
26+
}
27+
28+
@Test
29+
@DisplayName("Should return -1 for all elements in a strictly decreasing array")
30+
void findNextGreaterElement_strictlyDecreasingArray() {
31+
// Arrange
32+
int[] array = {5, 4, 3, 2, 1};
33+
int[] expected = {-1, -1, -1, -1, -1};
34+
MonotonicStack monotonicStack = new MonotonicStack(array);
35+
36+
// Act
37+
monotonicStack.findNextGreaterElement();
38+
39+
// Assert
40+
assertArrayEquals(expected, monotonicStack.answer, "All elements should have -1 as next greater");
41+
}
42+
43+
@Test
44+
@DisplayName("Should find the correct next greater element in a strictly increasing array")
45+
void findNextGreaterElement_strictlyIncreasingArray() {
46+
// Arrange
47+
int[] array = {1, 2, 3, 4, 5};
48+
int[] expected = {2, 3, 4, 5, -1};
49+
MonotonicStack monotonicStack = new MonotonicStack(array);
50+
51+
// Act
52+
monotonicStack.findNextGreaterElement();
53+
54+
// Assert
55+
assertArrayEquals(expected, monotonicStack.answer, "Each element's next greater should be its right neighbor");
56+
}
57+
58+
@Test
59+
@DisplayName("Should handle an array with duplicate values")
60+
void findNextGreaterElement_withDuplicates() {
61+
// Arrange
62+
int[] array = {4, 5, 2, 2, 5};
63+
int[] expected = {5, -1, 5, 5, -1};
64+
MonotonicStack monotonicStack = new MonotonicStack(array);
65+
66+
// Act
67+
monotonicStack.findNextGreaterElement();
68+
69+
// Assert
70+
assertArrayEquals(expected, monotonicStack.answer, "Should handle duplicates correctly");
71+
}
72+
73+
@Test
74+
@DisplayName("Should handle an array with duplicate values")
75+
void findNextGreaterElement_withDuplicates_multiple() {
76+
// Arrange
77+
int[] array = {4, 12, 5, 3, 1, 2, 5, 3, 1, 2, 4, 6};
78+
int[] expected = {12, -1, 6, 5, 2, 5, 6, 4, 2, 4, 6, -1};
79+
MonotonicStack monotonicStack = new MonotonicStack(array);
80+
81+
// Act
82+
monotonicStack.findNextGreaterElement();
83+
84+
// Assert
85+
assertArrayEquals(expected, monotonicStack.answer, "Should handle duplicates correctly");
86+
}
87+
88+
@Test
89+
@DisplayName("Should handle an array where all elements are the same")
90+
void findNextGreaterElement_allSameElements() {
91+
// Arrange
92+
int[] array = {7, 7, 7, 7};
93+
int[] expected = {-1, -1, -1, -1};
94+
MonotonicStack monotonicStack = new MonotonicStack(array);
95+
96+
// Act
97+
monotonicStack.findNextGreaterElement();
98+
99+
// Assert
100+
assertArrayEquals(expected, monotonicStack.answer, "All elements should be -1 when all are identical");
101+
}
102+
103+
@Test
104+
@DisplayName("Should handle a single-element array")
105+
void findNextGreaterElement_singleElementArray() {
106+
// Arrange
107+
int[] array = {100};
108+
int[] expected = {-1};
109+
MonotonicStack monotonicStack = new MonotonicStack(array);
110+
111+
// Act
112+
monotonicStack.findNextGreaterElement();
113+
114+
// Assert
115+
assertArrayEquals(expected, monotonicStack.answer, "A single element has no next greater element");
116+
}
117+
118+
@Test
119+
@DisplayName("Should handle an empty array without errors")
120+
void findNextGreaterElement_emptyArray() {
121+
// Arrange
122+
int[] array = {};
123+
int[] expected = {};
124+
MonotonicStack monotonicStack = new MonotonicStack(array);
125+
126+
// Act
127+
monotonicStack.findNextGreaterElement();
128+
129+
// Assert
130+
assertArrayEquals(expected, monotonicStack.answer, "An empty input should result in an empty output");
131+
}
132+
}

0 commit comments

Comments
 (0)