Nearest Smaller Element (NSE)

Problem

Given an array, find the nearest smaller element G[i] for every element A[i] in the array such that the element has an index smaller than i. (link)

More formally,

    G[i] for an element A[i] = an element A[j] such that 
    j is maximum possible AND 
    j < i AND
    A[j] < A[i]

Elements for which no smaller element exist, consider next smaller element as -1.

Input Format

The only argument given is integer array A.

Output Format

Return the integar array G such that G[i] contains nearest smaller number than A[i].If no such element occurs G[i] should be -1.

For Example

Input 1:
    A = [4, 5, 2, 10, 8]
Output 1:
    G = [-1, 4, -1, 2, 2]
Explaination 1:
    index 1: No element less than 4 in left of 4, G[1] = -1
    index 2: A[1] is only element less than A[2], G[2] = A[1]
    index 3: No element less than 2 in left of 2, G[3] = -1
    index 4: A[3] is nearest element which is less than A[4], G[4] = A[3]
    index 4: A[3] is nearest element which is less than A[5], G[5] = A[3]

Input 2:
    A = [3, 2, 1]
Output 2:
    [-1, -1, -1]
Explaination 2:
    index 1: No element less than 3 in left of 3, G[1] = -1
    index 2: No element less than 2 in left of 2, G[2] = -1
    index 3: No element less than 1 in left of 1, G[3] = -1

Solution

Brute Force Approach

To find the nearest smaller element for element at index i, we need to traverse back until we find an element that is smaller than current one. We then store it in our answer and move on to the next index i+1 immediately.

Technically we use two nested for loops to achieve this.

Time - O(n*n)

Space - O(1)

public class Solution {
    public ArrayList<Integer> prevSmaller(ArrayList<Integer> A) {
        Stack<Integer> stack = new Stack<>();

        ArrayList<Integer> nse = new ArrayList<>();

        for(int i=0; i<A.size(); i++){
            int ans = -1;
            for(int j=i-1; j>=0; j--){
                if(A.get(j)<A.get(i)){
                    ans = A.get(j);
                    break;
                }
            }
            nse.add(ans);
        }

        return nse;
    }
}

Optimal Approach

The problem of finding the nearest smaller element (NSE) is indeed related to the Next Greater Element (NGE) problem.

  1. Monotonic Stack Approach:

    • In both NGE and NSE problems, we maintain a stack to keep track of elements.

    • However, in NGE, we store elements in decreasing order (so that we can find the next greater element).

    • In NSE, we store elements in increasing order (to find the nearest smaller element).

  2. Finding the Nearest Smaller Element (NSE):

    • Given an array, we want to find, for each element, the nearest element to its left that is smaller than itself.

    • We’ll traverse the array from left to right.

    • For each element:

      • If the stack is empty, push the current element onto the stack.

      • If the top element of the stack is smaller than the current element, it’s the NSE for the current element. Pop it from the stack and record it.

      • If the top element is greater, keep popping until you find a smaller element or until the stack is empty.

      • Finally, push the current element onto the stack.

  3. Time Complexity:

    • The time complexity is O(n).

    • We traverse the array once O(n), and each element is pushed and popped from the stack at most once (another O(n)).

    • So, the total time complexity is 2n, which simplifies to O(n).

Time - O(n)

Space - O(n)

public class Solution {
    public ArrayList<Integer> prevSmaller(ArrayList<Integer> A) {
        Stack<Integer> stack = new Stack<>();

        ArrayList<Integer> nse = new ArrayList<>();

        for(int i=0; i<A.size(); i++){
            while(!stack.empty() && A.get(i) <= stack.peek()){
                stack.pop();
            }
            int ans = !stack.empty() ? stack.peek() : -1;

            nse.add(ans);
            stack.push(A.get(i));
        }

        return nse;
    }
}