Validate Stack Sequences
🌀 Stack Permutations: A Unified Editorial
Stack permutations involve determining if a specific output sequence can be generated from an input sequence using a single stack. This converts a simulation problem into a structural validation problem.
We will explore two main approaches:
-
Simulation (Greedy): The standard \(O(N)\) approach using a stack data structure to mimic the push/pop operations.
-
Forbidden Pattern (Property Check): The \(O(1)\) space approach (conceptually \(O(N^2)\) time) that validates the mathematical properties of the permutation without using auxiliary memory.
📝 Problem Statement
Problem: Validate Stack Sequences
You are given a target permutation target of length \(N\) containing distinct integers from \(0\) to \(N-1\).
You have an initially empty stack. You also have a stream of numbers from \(0\) to \(N-1\) arriving in increasing order.
At any point, you can perform two operations: 1. Push: Push the next available number from the input stream onto the stack. 2. Pop: Pop the top element from the stack and print it to the output sequence.
Return true if the target permutation can be generated using a valid sequence of push and pop operations, otherwise return false.
Input Example: target = [0, 3, 4, 2, 1]
Output Example: true
Simulation Approach
These solutions typically rely on the LIFO (Last In, First Out) property. We greedily push elements until we match the target, then pop immediately.
1. 🏗️ Standard Simulation (Difficulty: Easy)
Logic:
We maintain a pointer to the source number (current_source) we are ready to push.
For every number x in the target sequence:
-
Push Phase: While
current_source\(\le\)x, we pushcurrent_sourceonto the stack and increment the source pointer. -
Pop Phase: Check if the top of the stack matches
x.-
If Match: Pop from the stack.
-
If No Match: The sequence is invalid (we need
x, but it is buried beneath other elements or not yet available).
-
Dry Run (Example: 0 3 4 2 1):
-
Target
0: Push0. Top is0. Pop0. Stack:[]. -
Target
3: Push1, 2, 3. Top is3. Pop3. Stack:[1, 2]. -
Target
4: Push4. Top is4. Pop4. Stack:[1, 2]. -
Target
2: Top is2. Pop2. Stack:[1]. -
Target
1: Top is1. Pop1. Stack:[]. -
Result: True.
Implementation (C++):
Click to expand solution code
bool validateStackSequence(vector<int>& target) {
stack<int> s;
int current_source = 0;
for (int x : target) {
// Keep pushing onto stack until the top is x
while (current_source <= x) {
s.push(current_source);
current_source++;
}
// Check if the top is what we need
if (!s.empty() && s.top() == x) {
s.pop();
} else {
return false;
}
}
return true;
}
Pattern Validation Approach
To solve this without a stack data structure (optimizing Space to \(O(1)\)), we must verify the mathematical property of stack-sortable permutations.
2. 📉 The "3-1-2" Pattern (Difficulty: Medium)
Problem: Validate the sequence using O(1) auxiliary space (modifying the input array is not allowed).
The Mathematical Rule:
A sequence is a valid stack permutation if and only if it does not contain a "3-1-2" pattern.
Mathematically, for any indices \(i < j < k\):
If target[i] (the "3") is the largest of the three, then we cannot have target[k] > target[j].
Simplified Rule: For any number X in the sequence, all numbers to its right that are smaller than X must appear in descending order.
Visualizing the Violation:
Imagine the target is 0 3 4 1 2.
Look at 4. The numbers to its right that are smaller are 1 and 2.
Since 1 < 2, they are in ascending order. This is a violation.
-
When
4was popped,1and2were in the stack. -
Since
1is pushed before2,2is above1in the stack. -
Therefore,
2must be popped before1.
Algorithm (In-Place Check):
Iterate through the array with pointer i. For each target[i]:
-
Scan all
target[j]where \(j > i\). -
Filter for numbers where
target[j] < target[i]. -
Ensure these smaller numbers are strictly decreasing. If we find a smaller number that is larger than the previous smaller number, return False.
Implementation (C++):
Click to expand solution code
bool validateStackSequenceO1(vector<int>& target) {
int n = target.size();
for (int i = 0; i < n; ++i) {
int current = target[i];
int last_smaller_seen = -1;
// Scan all numbers to the right of 'current'
for (int j = i + 1; j < n; ++j) {
// We only care about numbers smaller than 'current'
if (target[j] < current) {
// If we find an ascent (Low -> High) among smaller numbers:
if (last_smaller_seen != -1 && target[j] > last_smaller_seen) {
return false; // 3-1-2 Pattern Detected
}
last_smaller_seen = target[j];
}
}
}
return true;
}
Answer: This approach reduces Space Complexity to \(O(1)\) at the cost of increasing Time Complexity to \(O(N^2)\).
📚 Task Links (Similar Tasks)
Here are similar problems to practice stack validations:
-
LeetCode - Validate Stack Sequences: https://leetcode.com/problems/validate-stack-sequences/
-
GeeksForGeeks - Stack Permutations: https://www.geeksforgeeks.org/stack-permutations-check-if-an-array-is-stack-permutation-of-other/
-
POJ - Rails (Classic Problem): http://poj.org/problem?id=1363