1861. Rotating the Box - Explanation
Description
You are given an m x n matrix of characters boxGrid representing a side-view of a box. Each cell of the box is one of the following:
- A stone
'#' - A stationary obstacle
'*' - Empty
'.'
The box is rotated 90 degrees clockwise, causing some of the stones to fall due to gravity. Each stone falls down until it lands on an obstacle, another stone, or the bottom of the box. Gravity does not affect the obstacles' positions, and the inertia from the box's rotation does not affect the stones' horizontal positions.
It is guaranteed that each stone in boxGrid rests on an obstacle, another stone, or the bottom of the box.
Return an n x m matrix representing the box after the rotation described above.
Example 1:
Input: boxGrid = [
["#",".","*","."],
["#","#","*","."]
]
Output: [
["#","."],
["#","#"],
["*","*"],
[".","."]
]Example 2:
Input: boxGrid = [
["#","#","*",".","*","."],
["#","#","#","*",".","."],
["#","#","#",".","#","."]
]
Output: [
[".","#","#"],
[".","#","#"],
["#","#","*"],
["#","*","."],
["#",".","*"],
["#",".","."]
]Constraints:
m == boxGrid.lengthn == boxGrid[i].length1 <= m, n <= 500boxGrid[i][j]is either'#','*', or'.'.
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- 2D Array Manipulation - Working with grids, understanding row and column indexing
- Two Pointers Technique - Using a pointer to track the next available position while iterating through elements
- Gravity Simulation - Moving elements (stones) toward a direction until they hit an obstacle or boundary
- Matrix Rotation - Transforming coordinates from original position (r,c) to rotated position for 90-degree clockwise rotation
1. Brute Force
Intuition
When the box is rotated 90 degrees clockwise, gravity pulls stones downward in the new orientation. Before rotation, rows become columns, so stones in each row should fall to the right (toward the end of the row). We simulate gravity by moving each stone as far right as possible until it hits another stone, an obstacle, or the boundary. After simulating gravity, we perform the rotation by transposing and reversing the row order.
Algorithm
- For each row, iterate from right to left.
- When a stone
#is found, scan rightward to find the farthest empty cell.before hitting an obstacle*or boundary. - Move the stone to that position.
- After processing all rows, create the rotated
grid:- The new
gridhasCOLSrows andROWScolumns. - For each column
cin the originalgrid, the new row at indexccontains elements from bottom to top of that column.
- The new
- Return the rotated
grid.
class Solution:
def rotateTheBox(self, boxGrid: List[List[str]]) -> List[List[str]]:
ROWS, COLS = len(boxGrid), len(boxGrid[0])
for r in range(ROWS - 1, -1, -1):
for c1 in range(COLS - 1, -1, -1):
if boxGrid[r][c1] == '#':
c2 = c1 + 1
while c2 < COLS and boxGrid[r][c2] == '.':
c2 += 1
boxGrid[r][c1] = '.'
boxGrid[r][c2 - 1] = '#'
res = []
for c in range(COLS):
col = []
for r in range(ROWS - 1, -1, -1):
col.append(boxGrid[r][c])
res.append(col)
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of rows and is the number of columns.
2. Two Pointers - I
Intuition
Instead of scanning rightward for each stone, we can use a two-pointer technique. Maintain a pointer i that tracks the rightmost available position for a stone. Scan from right to left: when we see a stone, swap it with position i and decrement i. When we see an obstacle, reset i to just before the obstacle. This avoids redundant scanning and processes each cell at most twice.
Algorithm
- For each row, initialize pointer
itoCOLS - 1(rightmost position). - Iterate from right to left through the row:
- If the cell is a stone
#, swap it with positioni, then decrementi. - If the cell is an obstacle
*, resetitoc - 1(just before the obstacle).
- If the cell is a stone
- After processing all rows, construct the rotated
gridby mapping each column of the original to a row in the result (reading bottom to top). - Return the rotated
grid.
class Solution:
def rotateTheBox(self, boxGrid: List[List[str]]) -> List[List[str]]:
ROWS, COLS = len(boxGrid), len(boxGrid[0])
for r in range(ROWS):
i = COLS - 1
for c in reversed(range(COLS)):
if boxGrid[r][c] == "#":
boxGrid[r][c], boxGrid[r][i] = boxGrid[r][i], boxGrid[r][c]
i -= 1
elif boxGrid[r][c] == "*":
i = c - 1
res = []
for c in range(COLS):
col = [] # this is a row after rotation
for r in reversed(range(ROWS)):
col.append(boxGrid[r][c])
res.append(col)
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of rows and is the number of columns.
3. Two Pointers - II
Intuition
We can combine the gravity simulation and rotation into a single pass. Instead of modifying the original grid and then rotating, we directly write stones and obstacles to their final positions in the rotated result grid. This eliminates the need to modify the input and performs both operations simultaneously.
Algorithm
- Create a result
gridof sizeCOLS x ROWS, filled with empty cells.. - For each row in the original
grid, initialize pointeritoCOLS - 1. - Iterate from right to left:
- If the cell is a stone
#, place it at the rotated position corresponding toi, then decrementi. - If the cell is an obstacle
*, place it at its rotated position and resetitoc - 1.
- If the cell is a stone
- The rotated position for original
(r, c)is(c, ROWS - r - 1)for obstacles, and(i, ROWS - r - 1)for stones. - Return the result
grid.
class Solution:
def rotateTheBox(self, boxGrid: List[List[str]]) -> List[List[str]]:
ROWS, COLS = len(boxGrid), len(boxGrid[0])
res = [["."] * ROWS for _ in range(COLS)]
for r in range(ROWS):
i = COLS - 1
for c in reversed(range(COLS)):
if boxGrid[r][c] == "#":
res[i][ROWS - r - 1] = "#"
i -= 1
elif boxGrid[r][c] == "*":
res[c][ROWS - r - 1] = "*"
i = c - 1
return resTime & Space Complexity
- Time complexity:
- Space complexity:
Where is the number of rows and is the number of columns.
Common Pitfalls
Applying Gravity in the Wrong Direction
After rotation, gravity pulls stones downward, but before rotation stones should fall to the right (toward higher column indices). Processing stones left-to-right instead of right-to-left causes stones to fall incorrectly, as earlier stones block the path for later ones.
Forgetting to Reset the Drop Position After Obstacles
When an obstacle * is encountered, the drop position pointer must reset to just before the obstacle (c - 1). Failing to reset this pointer causes stones to incorrectly pass through or stack on top of obstacles.
Incorrect Rotation Index Mapping
The rotation transforms position (r, c) to (c, ROWS - 1 - r) for a 90-degree clockwise rotation. Using incorrect formulas like (c, r) or (ROWS - 1 - r, c) results in a transposed or incorrectly flipped matrix instead of a proper rotation.
Sign in to join the discussion