Given an array arr[] of integers, for each element arr[i], find the smallest element arr[j] such that j < i and arr[j] > arr[i]. If no such element exists, return -1 for that position.
Examples:
Input: arr[] = [3, 2, 5, 4, 1]
Output: [-1, 3, -1, 5, 2]
Explanation:
For 3, no greater element to the left, so -1.
For 2, the smallest greater element to the left is 3.
For 5, no greater element to the left, so -1.
For 4, the smallest greater element to the left is 5.
For 1, the smallest greater element to the left is 2.Input: arr[] = [1,2,3]
Output: [-1, -1, -1]
Explanation: No element has a greater element to its left, so the answer for every element is -1.
Table of Content
[Naive Solution] - Using Nested Loops - O(n^2) Time and O(1) Space
For every element, check all previously seen elements and choose the smallest one that is strictly greater than the current element.
- Initialize the result array with -1.
- Traverse the array from left to right.
- For each element, scan all previous elements and keep the smallest value that is greater than the current element.
- Store the found value in the result array and return it after processing all elements.
#include <climits>
#include <iostream>
#include <vector>
using namespace std;
vector<int> greaterElement(const vector<int> &arr) {
int n = arr.size();
vector<int> result(n, -1);
// Outer loop: Iterate through each element
for (int i = 0; i < n; i++) {
int smallestGreater = INT_MAX;
// Inner loop: Scan all elements to the left
for (int j = 0; j < i; j++){
if (arr[j] > arr[i] && arr[j] < smallestGreater)
{
smallestGreater = arr[j];
}
}
// If we found a valid element, update the result array
if (smallestGreater != INT_MAX) {
result[i] = smallestGreater;
}
}
return result;
}
int main() {
vector<int> arr = {3, 2, 5, 4, 1};
int n = arr.size();
vector<int> ans = greaterElement(arr);
cout << "[";
for (int i = 0; i < n; i++) {
cout << ans[i];
if (i != n - 1)
cout << ", ";
}
cout << "]";
cout << endl;
return 0;
}
import java.util.ArrayList;
public class GFG {
public static ArrayList<Integer> greaterElement(int[] arr) {
int n = arr.length;
ArrayList<Integer> result = new ArrayList<>();
// Initialize result with -1
for (int i = 0; i < n; i++) {
result.add(-1);
}
// Outer loop: Iterate through each element
for (int i = 0; i < n; i++) {
int smallestGreater = Integer.MAX_VALUE;
// Inner loop: Scan all elements to the left
for (int j = 0; j < i; j++) {
if (arr[j] > arr[i] && arr[j] < smallestGreater) {
smallestGreater = arr[j];
}
}
// If we found a valid element, update the result
if (smallestGreater != Integer.MAX_VALUE) {
result.set(i, smallestGreater);
}
}
return result;
}
public static void main(String[] args) {
int[] arr = {3, 2, 5, 4, 1};
ArrayList<Integer> ans = greaterElement(arr);
System.out.println(ans);
}
}
def greaterElement(arr):
n = len(arr)
result = [-1] * n
# Outer loop: Iterate through each element
for i in range(n):
smallestGreater = float('inf')
# Inner loop: Scan all elements to the left
for j in range(i):
if arr[j] > arr[i] and arr[j] < smallestGreater:
smallestGreater = arr[j]
# If we found a valid element, update the result array
if smallestGreater!= float('inf'):
result[i] = smallestGreater
return result
if __name__ == '__main__':
arr = [3, 2, 5, 4, 1]
ans = greaterElement(arr)
print('[' + ', '.join(map(str, ans)) + ']')
using System;
using System.Collections.Generic;
class GFG {
static List<int> greaterElement(int[] arr) {
int n = arr.Length;
List<int> result = new List<int>();
// Initialize result with -1
for (int i = 0; i < n; i++) {
result.Add(-1);
}
// Outer loop: Iterate through each element
for (int i = 0; i < n; i++) {
int smallestGreater = int.MaxValue;
// Inner loop: Scan all elements to the left
for (int j = 0; j < i; j++) {
if (arr[j] > arr[i] && arr[j] < smallestGreater) {
smallestGreater = arr[j];
}
}
// If we found a valid element, update the result
if (smallestGreater != int.MaxValue) {
result[i] = smallestGreater;
}
}
return result;
}
static void Main() {
int[] arr = { 3, 2, 5, 4, 1 };
List<int> ans = greaterElement(arr);
Console.WriteLine("[" + string.Join(", ", ans) + "]");
}
}
function greaterElement(arr) {
const n = arr.length;
const result = Array(n).fill(-1);
// Outer loop: Iterate through each element
for (let i = 0; i < n; i++) {
let smallestGreater = Number.MAX_SAFE_INTEGER;
// Inner loop: Scan all elements to the left
for (let j = 0; j < i; j++) {
if (arr[j] > arr[i] && arr[j] < smallestGreater) {
smallestGreater = arr[j];
}
}
// If we found a valid element, update the result array
if (smallestGreater!== Number.MAX_SAFE_INTEGER) {
result[i] = smallestGreater;
}
}
return result;
}
// Driver code
const arr = [3, 2, 5, 4, 1];
const ans = greaterElement(arr);
console.log('[' + ans.join(', ') + ']');
[Expected Approach] - Using Hash Set
Store the previously seen elements in sorted order so that the smallest element greater than the current value can be found efficiently.
- Initialize an empty ordered data structure to store the previously visited elements.
- Traverse the array from left to right.
- For each element, find the smallest previously visited element that is strictly greater than it.
- If such an element exists, add it to the answer; otherwise, add -1.
- Insert the current element into the ordered data structure and continue processing the remaining elements.
Note: Languages like C++, Java, and C# provide built-in ordered data structures. Python and JavaScript do not have a built-in ordered set, so we can implement one using an AVL Tree.
#include <iostream>
#include <vector>
#include <set>
using namespace std;
vector<int> greaterElement(vector<int>& arr) {
// Set stores elements in sorted order
set<int> st;
// Vector to store the final answer
vector<int> ans;
for (int num : arr) {
// Find first element strictly greater
auto it = st.upper_bound(num);
// If no greater element exists
if (it == st.end()) {
ans.push_back(-1);
} else {
// Store greater element
ans.push_back(*it);
}
// Insert current element
st.insert(num);
}
return ans;
}
int main() {
vector<int> arr = {3, 2, 5, 4, 1};
vector<int> ans = greaterElement(arr);
cout << "[";
for (int i = 0; i < ans.size(); i++) {
cout << ans[i];
if (i != ans.size() - 1)
cout << ", ";
}
cout << "]" << endl;
return 0;
}
import java.util.*;
public class GFG {
public static ArrayList<Integer> greaterElement(int[] arr) {
// TreeSet stores elements in sorted order
TreeSet<Integer> st = new TreeSet<>();
// ArrayList to store final answer
ArrayList<Integer> ans = new ArrayList<>();
for (int num : arr) {
// Find first element strictly greater
Integer temp = st.higher(num);
// If no greater element exists
if (temp == null) {
ans.add(-1);
} else {
// Store greater element
ans.add(temp);
}
// Insert current element
st.add(num);
}
return ans;
}
public static void main(String[] args) {
int[] arr = {3, 2, 5, 4, 1};
ArrayList<Integer> ans = greaterElement(arr);
System.out.println(ans);
}
}
# Structure of AVL Node
class Node:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
self.height = 1
# Returns height of the subtree rooted at node
def getHeight(node):
if not node:
return 0
return node.height
# Returns balance factor = left height - right height
def getBalance(node):
if not node:
return 0
return getHeight(node.left) - getHeight(node.right)
# Performs right rotation
def rightRotate(y):
x = y.left
t2 = x.right
x.right = y
y.left = t2
# Update heights after rotation
y.height = 1 + max(getHeight(y.left), getHeight(y.right))
x.height = 1 + max(getHeight(x.left), getHeight(x.right))
return x
# Performs left rotation
def leftRotate(x):
y = x.right
t2 = y.left
y.left = x
x.right = t2
# Update heights after rotation
x.height = 1 + max(getHeight(x.left), getHeight(x.right))
y.height = 1 + max(getHeight(y.left), getHeight(y.right))
return y
# Inserts key into AVL tree and balances it if needed
def insert(node, key):
# Normal BST insertion
if not node:
return Node(key)
if key < node.key:
node.left = insert(node.left, key)
elif key > node.key:
node.right = insert(node.right, key)
else:
return node
# Update height of current node
node.height = 1 + max(getHeight(node.left),
getHeight(node.right))
# Check if tree became unbalanced
balance = getBalance(node)
# Left Left Case
if balance > 1 and key < node.left.key:
return rightRotate(node)
# Right Right Case
if balance < -1 and key > node.right.key:
return leftRotate(node)
# Left Right Case
if balance > 1 and key > node.left.key:
node.left = leftRotate(node.left)
return rightRotate(node)
# Right Left Case
if balance < -1 and key < node.right.key:
node.right = rightRotate(node.right)
return leftRotate(node)
return node
# Finds the smallest value strictly greater than key
def getStrictlyGreater(node, key):
ans = -1
while node:
if node.key > key:
# Current node can be the answer
ans = node.key
node = node.left
else:
# Look for larger values
node = node.right
return ans
def greaterElement(arr):
root = None
res = []
for x in arr:
# Query before inserting current element
res.append(getStrictlyGreater(root, x))
# Insert current element into AVL tree
root = insert(root, x)
return res
if __name__ == "__main__":
arr = [3, 2, 5, 4, 1]
ans = greaterElement(arr)
print(ans)
using System;
using System.Collections.Generic;
public class GFG {
public static List<int> greaterElement(int[] arr) {
// Stores all previously seen elements in sorted order
SortedSet<int> seen = new SortedSet<int>();
List<int> res = new List<int>(arr.Length);
foreach (int val in arr) {
// Get all elements strictly greater than val
var larger = seen.GetViewBetween(val + 1, int.MaxValue);
// The first element in the view is the answer
using (var iter = larger.GetEnumerator()) {
if (iter.MoveNext()) {
res.Add(iter.Current);
} else {
res.Add(-1);
}
}
// Insert current element into the set
seen.Add(val);
}
return res;
}
public static void Main() {
int[] arr = { 3, 2, 5, 4, 1 };
List<int> ans = greaterElement(arr);
Console.WriteLine("[" + string.Join(", ", ans) + "]");
}
}
// Structure of AVL Node
class Node {
constructor(key) {
this.key = key;
this.left = null;
this.right = null;
this.height = 1;
}
}
// Returns height of the subtree rooted at node
function getHeight(node) {
return node === null ? 0 : node.height;
}
// Returns balance factor = left height - right height
function getBalance(node) {
if (node === null) return 0;
return getHeight(node.left) - getHeight(node.right);
}
// Performs right rotation
function rightRotate(y) {
let x = y.left;
let t2 = x.right;
x.right = y;
y.left = t2;
// Update heights after rotation
y.height = 1 + Math.max(getHeight(y.left), getHeight(y.right));
x.height = 1 + Math.max(getHeight(x.left), getHeight(x.right));
return x;
}
// Performs left rotation
function leftRotate(x) {
let y = x.right;
let t2 = y.left;
y.left = x;
x.right = t2;
// Update heights after rotation
x.height = 1 + Math.max(getHeight(x.left), getHeight(x.right));
y.height = 1 + Math.max(getHeight(y.left), getHeight(y.right));
return y;
}
// Inserts key into AVL tree and balances it if needed
function insert(node, key) {
// Normal BST insertion
if (node === null) {
return new Node(key);
}
if (key < node.key) {
node.left = insert(node.left, key);
} else if (key > node.key) {
node.right = insert(node.right, key);
} else {
return node;
}
// Update height of current node
node.height = 1 + Math.max(getHeight(node.left), getHeight(node.right));
// Check if tree became unbalanced
let balance = getBalance(node);
// Left Left Case
if (balance > 1 && key < node.left.key) {
return rightRotate(node);
}
// Right Right Case
if (balance < -1 && key > node.right.key) {
return leftRotate(node);
}
// Left Right Case
if (balance > 1 && key > node.left.key) {
node.left = leftRotate(node.left);
return rightRotate(node);
}
// Right Left Case
if (balance < -1 && key < node.right.key) {
node.right = rightRotate(node.right);
return leftRotate(node);
}
return node;
}
// Finds the smallest value strictly greater than key
function getStrictlyGreater(node, key) {
let ans = -1;
while (node !== null) {
if (node.key > key) {
// Current node can be the answer
ans = node.key;
node = node.left;
} else {
// Look for larger values
node = node.right;
}
}
return ans;
}
function greaterElement(arr) {
let root = null;
let res = [];
for (let x of arr) {
// Query before inserting current element
res.push(getStrictlyGreater(root, x));
// Insert current element into AVL tree
root = insert(root, x);
}
return res;
}
// Driver code
let arr = [3, 2, 5, 4, 1];
let ans = greaterElement(arr);
console.log('[' + ans.join(', ') + ']');
Time Complexity: Traversing the array takes O(n) time, and for each element, searching and inserting into the ordered data structure take O(log n) time. Therefore, the overall time complexity is O(n*log n).
Auxiliary Space: The ordered data structure stores up to n previously visited elements in the worst case, resulting in O(n) auxiliary space.
[Alternate Approach] - Using Segment Tree
Store previously visited elements in a segment tree, allowing the smallest previously visited element greater than the current element to be found efficiently.
- Perform coordinate compression to map the array values to a smaller index range.
- Build a segment tree over the compressed indices, where each index stores the corresponding array value if it has been seen.
- Traverse the array from left to right.
- For each element, query the segment tree over the range of values greater than the current element to find the smallest previously visited greater element.
- Store the result, then update the segment tree by marking the current element as visited.
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
class SegmentTree {
vector<int> tree;
int n;
public:
SegmentTree(int size) {
n = size;
tree.assign(4 * n, INT_MAX);
}
// Updates the value at a compressed index
void update(int node, int start, int end, int idx, int val) {
if (start == end) {
tree[node] = val;
return;
}
int mid = start + (end - start) / 2;
if (idx <= mid)
update(2 * node, start, mid, idx, val);
else
update(2 * node + 1, mid + 1, end, idx, val);
// Store minimum value in the current range
tree[node] = min(tree[2 * node], tree[2 * node + 1]);
}
// Returns the minimum value in the given range
int query(int node, int start, int end, int l, int r) {
if (r < start || end < l)
return INT_MAX;
if (l <= start && end <= r)
return tree[node];
int mid = start + (end - start) / 2;
return min(query(2 * node, start, mid, l, r),
query(2 * node + 1, mid + 1, end, l, r));
}
};
vector<int> greaterElement(vector<int>& arr) {
int n = arr.size();
if (n == 0)
return {};
// Create sorted list of distinct values
vector<int> sorted_unique = arr;
sort(sorted_unique.begin(), sorted_unique.end());
sorted_unique.erase(unique(sorted_unique.begin(), sorted_unique.end()), sorted_unique.end());
int U = sorted_unique.size();
SegmentTree st(U);
vector<int> ans;
// Process each element from left to right
for (int num : arr) {
// Find compressed index of current element
int idx = lower_bound(sorted_unique.begin(), sorted_unique.end(), num)
- sorted_unique.begin();
// Query for the smallest greater value
int res = INT_MAX;
if (idx + 1 < U) {
res = st.query(1, 0, U - 1, idx + 1, U - 1);
}
// Store the answer
if (res == INT_MAX)
ans.push_back(-1);
else
ans.push_back(res);
// Insert current value into the segment tree
st.update(1, 0, U - 1, idx, num);
}
return ans;
}
int main() {
vector<int> arr = {3, 2, 5, 4, 1};
vector<int> ans = greaterElement(arr);
cout << "[";
for (size_t i = 0; i < ans.size(); i++) {
cout << ans[i] << (i != ans.size() - 1 ? ", " : "");
}
cout << "]" << endl;
return 0;
}
import java.util.*;
public class GFG {
static class SegmentTree {
int[] tree;
int n;
SegmentTree(int size) {
this.n = size;
this.tree = new int[4 * n];
Arrays.fill(tree, Integer.MAX_VALUE);
}
// Updates the value at a compressed index
void update(int node, int start, int end, int idx, int val) {
if (start == end) {
tree[node] = val;
return;
}
int mid = start + (end - start) / 2;
if (idx <= mid)
update(2 * node, start, mid, idx, val);
else
update(2 * node + 1, mid + 1, end, idx, val);
// Store minimum value in the current range
tree[node] = Math.min(tree[2 * node], tree[2 * node + 1]);
}
// Returns the minimum value in the given range
int query(int node, int start, int end, int l, int r) {
if (r < start || end < l)
return Integer.MAX_VALUE;
if (l <= start && end <= r)
return tree[node];
int mid = start + (end - start) / 2;
return Math.min(query(2 * node, start, mid, l, r),
query(2 * node + 1, mid + 1, end, l, r));
}
}
public static ArrayList<Integer> greaterElement(int[] arr) {
int n = arr.length;
if (n == 0)
return new ArrayList<>();
// Create sorted list of distinct values
int[] sortedUnique = Arrays.stream(arr).distinct().sorted().toArray();
int U = sortedUnique.length;
SegmentTree st = new SegmentTree(U);
ArrayList<Integer> ans = new ArrayList<>();
// Process each element from left to right
for (int num : arr) {
// Find compressed index of current element
int idx = Arrays.binarySearch(sortedUnique, num);
// Query for the smallest greater value
int res = Integer.MAX_VALUE;
if (idx + 1 < U) {
res = st.query(1, 0, U - 1, idx + 1, U - 1);
}
// Store the answer
if (res == Integer.MAX_VALUE)
ans.add(-1);
else
ans.add(res);
// Insert current value into the segment tree
st.update(1, 0, U - 1, idx, num);
}
return ans;
}
public static void main(String[] args) {
int[] arr = {3, 2, 5, 4, 1};
System.out.println(greaterElement(arr));
}
}
import bisect
class SegmentTree:
def __init__(self, size):
self.n = size
self.tree = [float('inf')] * (4 * size)
# Updates the value at a compressed index
def update(self, node, start, end, idx, val):
if start == end:
self.tree[node] = val
return
mid = start + (end - start) // 2
if idx <= mid:
self.update(2 * node, start, mid, idx, val)
else:
self.update(2 * node + 1, mid + 1, end, idx, val)
# Store minimum value in the current range
self.tree[node] = min(self.tree[2 * node], self.tree[2 * node + 1])
# Returns the minimum value in the given range
def query(self, node, start, end, l, r):
if r < start or end < l:
return float('inf')
if l <= start and end <= r:
return self.tree[node]
mid = start + (end - start) // 2
return min(self.query(2 * node, start, mid, l, r),
self.query(2 * node + 1, mid + 1, end, l, r))
def greater_element(arr):
if not arr:
return []
# Create sorted list of distinct values
sorted_unique = sorted(set(arr))
U = len(sorted_unique)
st = SegmentTree(U)
ans = []
# Process each element from left to right
for num in arr:
# Find compressed index of current element
idx = bisect.bisect_left(sorted_unique, num)
# Query for the smallest greater value
res = float('inf')
if idx + 1 < U:
res = st.query(1, 0, U - 1, idx + 1, U - 1)
# Store the answer
if res == float('inf'):
ans.append(-1)
else:
ans.append(res)
# Insert current value into the segment tree
st.update(1, 0, U - 1, idx, num)
return ans
if __name__ == "__main__":
arr = [3, 2, 5, 4, 1]
print(greater_element(arr))
using System;
using System.Collections.Generic;
using System.Linq;
class GFG {
class SegmentTree {
private int[] tree;
private int n;
public SegmentTree(int size) {
n = size;
tree = new int[4 * n];
Array.Fill(tree, int.MaxValue);
}
// Updates the value at a compressed index
public void Update(int node, int start, int end, int idx, int val) {
if (start == end) {
tree[node] = val;
return;
}
int mid = start + (end - start) / 2;
if (idx <= mid)
Update(2 * node, start, mid, idx, val);
else
Update(2 * node + 1, mid + 1, end, idx, val);
// Store minimum value in the current range
tree[node] = Math.Min(tree[2 * node], tree[2 * node + 1]);
}
// Returns the minimum value in the given range
public int Query(int node, int start, int end, int l, int r) {
if (r < start || end < l)
return int.MaxValue;
if (l <= start && end <= r)
return tree[node];
int mid = start + (end - start) / 2;
return Math.Min(Query(2 * node, start, mid, l, r),
Query(2 * node + 1, mid + 1, end, l, r));
}
}
static List<int> greaterElement(int[] arr) {
if (arr.Length == 0)
return new List<int>();
// Create sorted list of distinct values
int[] sortedUnique = arr.Distinct().OrderBy(x => x).ToArray();
int U = sortedUnique.Length;
SegmentTree st = new SegmentTree(U);
List<int> ans = new List<int>();
// Process each element from left to right
foreach (int num in arr) {
// Find compressed index of current element
int idx = Array.BinarySearch(sortedUnique, num);
// Query for the smallest greater value
int res = int.MaxValue;
if (idx + 1 < U) {
res = st.Query(1, 0, U - 1, idx + 1, U - 1);
}
// Store the answer
if (res == int.MaxValue)
ans.Add(-1);
else
ans.Add(res);
// Insert current value into the segment tree
st.Update(1, 0, U - 1, idx, num);
}
return ans;
}
static void Main() {
int[] arr = { 3, 2, 5, 4, 1 };
List<int> ans = greaterElement(arr);
Console.WriteLine("[" + string.Join(", ", ans) + "]");
}
}
class SegmentTree {
constructor(size) {
this.n = size;
this.tree = new Array(4 * size).fill(Infinity);
}
// Updates the value at a compressed index
update(node, start, end, idx, val) {
if (start === end) {
this.tree[node] = val;
return;
}
const mid = start + Math.floor((end - start) / 2);
if (idx <= mid) {
this.update(2 * node, start, mid, idx, val);
} else {
this.update(2 * node + 1, mid + 1, end, idx, val);
}
// Store minimum value in the current range
this.tree[node] = Math.min(this.tree[2 * node], this.tree[2 * node + 1]);
}
// Returns the minimum value in the given range
query(node, start, end, l, r) {
if (r < start || end < l) return Infinity;
if (l <= start && end <= r) return this.tree[node];
const mid = start + Math.floor((end - start) / 2);
return Math.min(
this.query(2 * node, start, mid, l, r),
this.query(2 * node + 1, mid + 1, end, l, r)
);
}
}
function binarySearch(arr, target) {
let left = 0, right = arr.length - 1;
while (left <= right) {
const mid = left + Math.floor((right - left) / 2);
if (arr[mid] === target) return mid;
if (arr[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
return -1;
}
function greaterElement(arr) {
if (arr.length === 0) return [];
// Create sorted list of distinct values
const sortedUnique = Array.from(new Set(arr)).sort((a, b) => a - b);
const U = sortedUnique.length;
const st = new SegmentTree(U);
const ans = [];
// Process each element from left to right
for (let num of arr) {
// Find compressed index of current element
const idx = binarySearch(sortedUnique, num);
// Query for the smallest greater value
let res = Infinity;
if (idx + 1 < U) {
res = st.query(1, 0, U - 1, idx + 1, U - 1);
}
// Store the answer
if (res === Infinity)
ans.push(-1);
else
ans.push(res);
// Insert current value into the segment tree
st.update(1, 0, U - 1, idx, num);
}
return ans;
}
// Driver code
const arr = [3, 2, 5, 4, 1];
console.log(greaterElement(arr));
Time Complexity: Traversing the array takes O(n) time. For each element, coordinate compression lookup, segment tree query, and update each take O(log n) time, giving an overall time complexity of O(n*log n).
Auxiliary Space: The segment tree and the compressed array together require O(n) auxiliary space.