Next Greater Element
Overview
Next Greater Element problems involve finding the first element to the right (or left) that is greater (or smaller) than the current element. These problems are efficiently solved using monotonic stack, which maintains elements in sorted order.
The key insight: use a stack to store elements in decreasing order. When a larger element is found, it becomes the "next greater" for elements in the stack.
Next greater element problems are particularly useful for:
- Finding next/previous greater/smaller elements
- Circular array variations
- Range queries
- Stock span problems
- Building relationships between elements
Pattern 1: Next Greater Element I
Find next greater element for each element in nums1 within nums2.
function nextGreaterElement(nums1: number[], nums2: number[]): number[] {
const nextGreater = new Map<number, number>();
const stack: number[] = [];
// Build next greater map for nums2
for (const num of nums2) {
while (stack.length > 0 && num > stack[stack.length - 1]) {
nextGreater.set(stack.pop()!, num);
}
stack.push(num);
}
// Query for nums1
return nums1.map(num => nextGreater.get(num) || -1);
}Time: O(n + m), Space: O(n + m)
Explanation: Stack maintains decreasing order. When larger element found, it's next greater for popped elements.
Pattern 2: Next Greater Element II (Circular)
Find next greater element in circular array.
function nextGreaterElements(nums: number[]): number[] {
const n = nums.length;
const result = new Array(n).fill(-1);
const stack: number[] = [];
// Process array twice for circular
for (let i = 0; i < n * 2; i++) {
const index = i % n;
while (stack.length > 0 && nums[index] > nums[stack[stack.length - 1]]) {
const top = stack.pop()!;
result[top] = nums[index];
}
if (i < n) {
stack.push(index);
}
}
return result;
}Time: O(n), Space: O(n)
Key insight: Process array twice to handle circular nature.
Pattern 3: Next Greater Element III
Find smallest integer greater than n with same digits.
function nextGreaterElementIII(n: number): number {
const digits = n.toString().split('').map(Number);
// Find first decreasing digit from right
let i = digits.length - 2;
while (i >= 0 && digits[i] >= digits[i + 1]) {
i--;
}
if (i < 0) return -1; // No greater number possible
// Find smallest digit greater than digits[i] from right
let j = digits.length - 1;
while (digits[j] <= digits[i]) {
j--;
}
// Swap
[digits[i], digits[j]] = [digits[j], digits[i]];
// Reverse suffix
let left = i + 1;
let right = digits.length - 1;
while (left < right) {
[digits[left], digits[right]] = [digits[right], digits[left]];
left++;
right--;
}
const result = parseInt(digits.join(''));
return result > 2147483647 ? -1 : result;
}Time: O(d) where d is number of digits, Space: O(d)
Algorithm: Similar to next permutation.
Pattern 4: Next Smaller Element
Find next smaller element for each position.
function nextSmallerElement(nums: number[]): number[] {
const result = new Array(nums.length).fill(-1);
const stack: number[] = [];
for (let i = 0; i < nums.length; i++) {
// Pop while current element is smaller than stack top
while (stack.length > 0 && nums[i] < nums[stack[stack.length - 1]]) {
const index = stack.pop()!;
result[index] = nums[i];
}
stack.push(i);
}
return result;
}Time: O(n), Space: O(n)
Pattern 5: Previous Greater Element
Find previous greater element for each position.
function previousGreaterElement(nums: number[]): number[] {
const result = new Array(nums.length).fill(-1);
const stack: number[] = [];
// Process from right to left
for (let i = nums.length - 1; i >= 0; i--) {
// Pop while current element is greater than stack top
while (stack.length > 0 && nums[i] > nums[stack[stack.length - 1]]) {
const index = stack.pop()!;
result[index] = nums[i];
}
stack.push(i);
}
return result;
}Time: O(n), Space: O(n)
Pattern 6: Previous Smaller Element
Find previous smaller element for each position.
function previousSmallerElement(nums: number[]): number[] {
const result = new Array(nums.length).fill(-1);
const stack: number[] = [];
// Process from right to left
for (let i = nums.length - 1; i >= 0; i--) {
// Pop while current element is smaller than stack top
while (stack.length > 0 && nums[i] < nums[stack[stack.length - 1]]) {
const index = stack.pop()!;
result[index] = nums[i];
}
stack.push(i);
}
return result;
}Time: O(n), Space: O(n)
Pattern 7: Daily Temperatures
Find days until warmer temperature.
function dailyTemperatures(temperatures: number[]): number[] {
const result = new Array(temperatures.length).fill(0);
const stack: number[] = [];
for (let i = 0; i < temperatures.length; i++) {
while (stack.length > 0 && temperatures[i] > temperatures[stack[stack.length - 1]]) {
const index = stack.pop()!;
result[index] = i - index;
}
stack.push(i);
}
return result;
}Time: O(n), Space: O(n)
Variation: Return indices instead of days.
Pattern 8: Next Greater Node in Linked List
Find next greater node value for each node.
class ListNode {
val: number;
next: ListNode | null;
constructor(val: number) {
this.val = val;
this.next = null;
}
}
function nextLargerNodes(head: ListNode | null): number[] {
const values: number[] = [];
let current = head;
// Convert to array
while (current) {
values.push(current.val);
current = current.next;
}
const result = new Array(values.length).fill(0);
const stack: number[] = [];
for (let i = 0; i < values.length; i++) {
while (stack.length > 0 && values[i] > values[stack[stack.length - 1]]) {
const index = stack.pop()!;
result[index] = values[i];
}
stack.push(i);
}
return result;
}Time: O(n), Space: O(n)
Pattern 9: Buildings with Ocean View
Find buildings that have ocean view (no taller building to right).
function findBuildings(heights: number[]): number[] {
const result: number[] = [];
let maxHeight = 0;
// Process from right to left
for (let i = heights.length - 1; i >= 0; i--) {
if (heights[i] > maxHeight) {
result.push(i);
maxHeight = heights[i];
}
}
return result.reverse();
}
// Using stack
function findBuildingsStack(heights: number[]): number[] {
const stack: number[] = [];
for (let i = 0; i < heights.length; i++) {
// Remove buildings blocked by current
while (stack.length > 0 && heights[stack[stack.length - 1]] <= heights[i]) {
stack.pop();
}
stack.push(i);
}
return stack;
}Time: O(n), Space: O(n)
Pattern 10: Next Greater Element with Distance
Find next greater element and its distance.
function nextGreaterElementWithDistance(nums: number[]): Array<[number, number]> {
const result: Array<[number, number]> = [];
const stack: number[] = [];
for (let i = 0; i < nums.length; i++) {
while (stack.length > 0 && nums[i] > nums[stack[stack.length - 1]]) {
const index = stack.pop()!;
result[index] = [nums[i], i - index];
}
stack.push(i);
}
// Remaining elements have no greater element
while (stack.length > 0) {
const index = stack.pop()!;
result[index] = [-1, -1];
}
return result;
}Time: O(n), Space: O(n)
Pattern 11: Count Next Greater Elements
Count how many next greater elements exist.
function countNextGreaterElements(nums: number[]): number[] {
const result = new Array(nums.length).fill(0);
const stack: number[] = [];
for (let i = 0; i < nums.length; i++) {
let count = 0;
// Count and remove elements smaller than current
while (stack.length > 0 && nums[i] > nums[stack[stack.length - 1]]) {
stack.pop();
count++;
}
result[i] = count;
stack.push(i);
}
return result;
}Time: O(n), Space: O(n)
Pattern 12: Next Greater Frequency Element
Find next element with greater frequency.
function nextGreaterFrequency(nums: number[]): number[] {
// Count frequencies
const freq = new Map<number, number>();
for (const num of nums) {
freq.set(num, (freq.get(num) || 0) + 1);
}
const result = new Array(nums.length).fill(-1);
const stack: number[] = [];
for (let i = 0; i < nums.length; i++) {
const currentFreq = freq.get(nums[i])!;
while (stack.length > 0) {
const topIndex = stack[stack.length - 1];
const topFreq = freq.get(nums[topIndex])!;
if (currentFreq > topFreq) {
result[topIndex] = nums[i];
stack.pop();
} else {
break;
}
}
stack.push(i);
}
return result;
}Time: O(n), Space: O(n)
When to Use Next Greater Element Techniques
Use next greater element techniques when:
- Problem asks for next/previous greater/smaller element
- Need to find relationships between elements
- Circular array variations
- Range queries with constraints
- Stock span or similar problems
- Building relationships in sequences
Key insight: Monotonic stack maintains order, enabling efficient lookups.
Template (Next Greater Element)
function nextGreaterElementTemplate(nums: number[]): number[] {
const result = new Array(nums.length).fill(-1);
const stack: number[] = []; // Store indices
for (let i = 0; i < nums.length; i++) {
// Pop while current element is greater than stack top
while (stack.length > 0 && nums[i] > nums[stack[stack.length - 1]]) {
const index = stack.pop()!;
result[index] = nums[i];
}
stack.push(i);
}
return result;
}Template (Next Smaller Element)
function nextSmallerElementTemplate(nums: number[]): number[] {
const result = new Array(nums.length).fill(-1);
const stack: number[] = [];
for (let i = 0; i < nums.length; i++) {
// Pop while current element is smaller than stack top
while (stack.length > 0 && nums[i] < nums[stack[stack.length - 1]]) {
const index = stack.pop()!;
result[index] = nums[i];
}
stack.push(i);
}
return result;
}Time and Space Complexity Summary
- Next greater element: O(n) time, O(n) space
- Circular array: O(n) time, O(n) space
- Previous greater: O(n) time, O(n) space
- Daily temperatures: O(n) time, O(n) space
- Next greater frequency: O(n) time, O(n) space
Practice Tips
- Choose direction - Left to right for next, right to left for previous
- Store indices - Usually store indices, not values
- Monotonic order - Maintain decreasing stack for next greater
- Circular arrays - Process array twice
- Handle remaining - Set -1 for elements without greater element
Common Mistakes
- Wrong order - Using increasing when need decreasing
- Index confusion - Storing values instead of indices
- Not handling remaining - Forgetting to set -1 for unprocessed
- Circular arrays - Not processing twice
- Boundary errors - Off-by-one in index calculations
Related Concepts
- Monotonic Stack - Core technique
- Stack - Data structure used
- Circular Array - Common variation
- Two Pointers - Alternative for some problems
Next greater element problems are common in interviews. Master monotonic stack technique, and you'll efficiently solve these relationship-finding problems in coding interviews.