Crushing Heavy Boxes — Problem Statement & Solution Guide
Quick Answer & Algorithm Key Takeaway
Master coding challenges related to Stack and solve the Crushing Heavy Boxes problem optimally.
O(n)O(n)Problem Description
You are managing a warehouse conveyor belt where boxes are stacked sequentially. Each box is represented by an array of two integers [weight, color]. You process the boxes from left to right, placing them onto a single vertical stack. When a new box is placed on the stack, it may crush the box directly below it if both of the following conditions are met: 1. They have the same color. 2. The incoming box has a greater weight than the box directly below it. The output should be a list of the weights of the boxes in the final stack.
DSA Pattern Breakdown
DSA Pattern Breakdown
"Crushing Heavy Boxes"
WHY DOES IT MATTER?
The stack pattern is essential because it transforms a potentially quadratic comparison problem into a linear one by exploiting the local nature of the crushing rule. It ensures each box is examined only once, which is critical for performance in high‑throughput systems.
OPTIMIZATION CHALLENGE
The key insight is that once a box is crushed, it can never be involved in future comparisons. This allows us to discard it immediately, reducing both time and space. The algorithm’s linearity hinges on this observation.
REAL-WORLD CONNECTION
Think of a warehouse where pallets are stacked and heavier pallets can crush lighter ones of the same type. The stack algorithm mirrors how a robotic arm would only need to look at the top pallet to decide whether to place a new one, without scanning the entire pile.
When explaining this in an interview, emphasize the *locality* of the crushing rule and how a stack naturally captures that locality. Also, be ready to discuss edge cases like identical colors and equal weights, and how the comparison operator affects the outcome.
COMPLEXITY AT A GLANCE
O(n)O(n)Core Theory — Why This Approach?
The problem is a classic example of a *monotonic stack* pattern, where we maintain a stack of boxes that are guaranteed to be in a specific order based on the crushing rule. A naive simulation would compare each new box with every box below it, leading to an O(n^2) time complexity for n boxes. By realizing that once a box is crushed it can never be compared again, we can process each box exactly once: push the incoming box onto the stack, then repeatedly pop the top of the stack while the top two boxes share the same color and the incoming box’s weight is strictly greater than the one below. This greedy, stack‑based approach guarantees that the stack always represents the current state of the conveyor, and each box is pushed and popped at most once, yielding an optimal O(n) time and O(n) space solution.
The key insight is that the crushing condition depends only on the immediate neighbor below the incoming box. Therefore, we never need to look further down the stack once the condition fails. This local property allows us to discard the need for nested loops and to use a simple stack to keep track of the surviving boxes.
In large inputs, the O(n^2) approach quickly becomes infeasible because the number of comparisons grows quadratically. The stack approach reduces the number of comparisons to linear, making it scalable to millions of boxes, which is essential for real‑world warehouse automation systems.
Interview Questions on This Problem
Q1How would you modify the algorithm if the crushing rule changed to "a box crushes the one below if it has the same color and a weight *greater than or equal to* the box below?"
You would change the comparison from strictly greater to greater than or equal. The stack logic remains the same: after pushing the new box, pop while the top two have the same color and the top’s weight is >= the one below. This subtle change can affect the final stack size, so careful testing is required.
Q2A fintech company asks: "If we had to process boxes in parallel across multiple conveyor belts, how would you ensure the final stack is consistent?"
You would partition the input into segments, process each segment independently using the stack algorithm, and then merge the partial stacks. The merge step must respect the crushing rule across segment boundaries, which can be done by treating the top of the left stack as the incoming box for the right stack and applying the same pop logic. This approach allows parallelism while preserving correctness.
Q3During a coding interview, the interviewer says: "What if we need to support queries that ask for the weight of the k-th box from the bottom after all crushing?"
Maintain an auxiliary array or balanced BST that tracks the stack elements. After processing, you can answer k-th-from-bottom queries in O(log n) by indexing into the array or using order statistics. Alternatively, you can store the stack as a vector and answer in O(1) by direct indexing, since the stack is static after processing.
Examples
Input
[[1, 1], [2, 1], [3, 1], [4, 2]]
Output
[3, 2, 4]
Explanation: Step-by-step: 1. We start with an empty stack. 2. We process the boxes from left to right. 3. The box [1, 1] is placed on the stack. 4. The box [2, 1] is placed on top of the box [1, 1] because they have the same color and weight. 5. The box [3, 1] is placed on top of the box [2, 1] because they have the same color and weight. 6. The box [4, 2] is placed on top of the box [3, 1] because they have different colors.
Input
[[6, 1], [3, 1], [2, 1]]
Output
[6, 3, 2]
Explanation: Step-by-step: 1. We start with an empty stack. 2. We process the boxes from left to right. 3. The box [6, 1] is placed on the stack. 4. The box [3, 1] is placed on top of the box [6, 1] because they have the same color and weight. 5. The box [2, 1] is placed on top of the box [3, 1] because they have the same color and weight.
Constraints
- 1 <= boxes.length <= 10^5
- boxes[i].length == 2
- 1 <= boxes[i][0], boxes[i][1] <= 10^9
Optimal Approach & Strategy
Use a stack: push each incoming box, then repeatedly pop the top while the top two boxes have the same color and the top’s weight is greater than the one below. Each box is processed in constant time, yielding O(n) overall.
Brute Force Approach
Simulate the process by iterating over each box and, for each new box, compare it with every box below it in the stack until the crushing rule fails. This leads to O(n^2) time complexity.
Code Solutions
/**
* @param {number[][]} boxes
* @return {number[]}
*/
var crushBoxes = function(boxes) {
const stack = [];
for (const [weight, color] of boxes) {
if (stack.length > 0 && stack[stack.length - 1] === color) {
stack.pop();
}
stack.push(weight);
}
return stack;
};class Solution {
public:
vector<int> crushBoxes(vector<vector<int>>& boxes) {
vector<int> stack;
for (const auto& box : boxes) {
int weight = box[0];
int color = box[1];
if (!stack.empty() && stack.back() == color) {
stack.pop_back();
}
stack.push_back(weight);
}
return stack;
}
};class Solution {
public List<Integer> crushBoxes(int[][] boxes) {
List<Integer> stack = new ArrayList<>();
for (int[] box : boxes) {
int weight = box[0];
int color = box[1];
if (!stack.isEmpty() && stack.get(stack.size() - 1).equals(color)) {
stack.remove(stack.size() - 1);
}
stack.add(weight);
}
return stack;
}
}class Solution:
def crushBoxes(self, boxes: List[List[int]]) -> List[int]:
stack = []
for weight, color in boxes:
if stack and stack[-1] == color:
stack.pop()
stack.append(weight)
return stack/**
* @param {number[][]} boxes
* @return {number[]}
*/
var crushBoxes = function(boxes) {
const stack = [];
for (const [weight, color] of boxes) {
if (stack.length > 0 && stack[stack.length - 1] === color) {
stack.pop();
}
stack.push(weight);
}
return stack;
};Asked in Top Tech Interviews
Solve in Interative Editor
Ready to test your code? Open our built-in compiler, run custom test suites, and see detailed complexity analysis reports instantly.