1260. Shift 2D Grid
Problem Description
You are given a 2D grid with dimensions m x n
(m rows and n columns) and an integer k
. Your task is to shift all elements in the grid k
times and return the resulting grid.
A single shift operation works as follows:
- Each element at position
grid[i][j]
moves one position to the right togrid[i][j + 1]
- When an element reaches the end of a row (at position
grid[i][n - 1]
), it wraps around to the beginning of the next row atgrid[i + 1][0]
- The element at the bottom-right corner
grid[m - 1][n - 1]
wraps around to the top-left cornergrid[0][0]
Essentially, if you imagine the 2D grid as a continuous sequence reading left-to-right, top-to-bottom, each shift moves every element one position forward in this sequence, with the last element cycling back to the first position.
For example, with a 3x3 grid and k=1:
[[1,2,3], [[9,1,2], [4,5,6], → [3,4,5], [7,8,9]] [6,7,8]]
The solution leverages the fact that shifting a 2D grid is equivalent to rotating elements in a flattened 1D array. By converting 2D coordinates (i,j)
to a 1D index i*n + j
, adding the shift amount k
, and then converting back to 2D coordinates using division and modulo operations, we can directly calculate where each element should end up after k
shifts.
Intuition
The key insight is recognizing that the shift operation described in the problem is essentially a circular rotation of elements when we view the 2D grid as a flattened 1D array.
Think about how elements move during a shift: they go right along their row, and when they reach the end of a row, they jump to the beginning of the next row. The last element wraps around to the first position. This is exactly how we read elements in a 2D array sequentially - left to right, top to bottom.
If we imagine "unrolling" the 2D grid into a 1D array, the shift operation becomes much simpler - it's just moving each element one position to the right in a circular manner. For example, a 2x3 grid [[1,2,3],[4,5,6]]
can be viewed as [1,2,3,4,5,6]
. A single shift would make it [6,1,2,3,4,5]
, which maps back to [[6,1,2],[3,4,5]]
.
To find where each element ends up after k
shifts, we can:
- Convert each 2D position
(i,j)
to a 1D index using the formulaindex = i * n + j
(where n is the number of columns) - Add
k
to this index to get the new position after k shifts - Use modulo
(m * n)
to handle the circular wrapping - Convert the new 1D index back to 2D coordinates using
x = index // n
andy = index % n
This approach allows us to directly calculate the final position of each element without actually performing k individual shift operations, making it much more efficient, especially for large values of k.
Solution Approach
The implementation follows the flattening strategy described in the reference approach. Here's how the solution works step by step:
-
Initialize the result grid: Create a new 2D array
ans
with the same dimensionsm x n
as the input grid, filled with zeros. -
Iterate through each element: Use nested loops to visit each element in the original grid at position
(i, j)
with valuev
. -
Calculate the flattened index: For each element at position
(i, j)
, convert it to a 1D index using the formulai * n + j
. This treats the 2D grid as if it were a single continuous array. -
Apply the shift: Add
k
to the flattened index to simulate k shifts:(i * n + j + k)
. -
Handle circular wrapping: Use modulo operation
% (m * n)
to ensure the index wraps around when it exceeds the total number of elements. This handles the circular nature of the shift operation. -
Convert back to 2D coordinates: Use the
divmod
function to convert the new 1D index back to 2D coordinates:x = new_index // n
gives the row indexy = new_index % n
gives the column index- Python's
divmod
does both operations at once:x, y = divmod(new_index, n)
-
Place the element: Assign the original value
v
to its new positionans[x][y]
.
The complete formula for the new position is:
x, y = divmod((i * n + j + k) % (m * n), n)
This approach has a time complexity of O(m * n)
since we visit each element exactly once, and a space complexity of O(m * n)
for the result array. The elegance lies in avoiding k actual shift operations by directly computing the final position mathematically.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with a 2×3 grid and k=4 shifts:
Initial Grid:
[[1, 2, 3], [4, 5, 6]]
Step 1: Set up
- Grid dimensions: m=2 rows, n=3 columns
- Total elements: m×n = 6
- Number of shifts: k=4
- Create result grid:
[[0, 0, 0], [0, 0, 0]]
Step 2: Process each element
Let's trace where each element goes:
Element at (0,0) with value 1:
- Convert to 1D index: 0×3 + 0 = 0
- Add shifts: 0 + 4 = 4
- Apply modulo: 4 % 6 = 4
- Convert back to 2D: divmod(4, 3) = (1, 1)
- Place 1 at position (1,1) in result
Element at (0,1) with value 2:
- Convert to 1D index: 0×3 + 1 = 1
- Add shifts: 1 + 4 = 5
- Apply modulo: 5 % 6 = 5
- Convert back to 2D: divmod(5, 3) = (1, 2)
- Place 2 at position (1,2) in result
Element at (0,2) with value 3:
- Convert to 1D index: 0×3 + 2 = 2
- Add shifts: 2 + 4 = 6
- Apply modulo: 6 % 6 = 0
- Convert back to 2D: divmod(0, 3) = (0, 0)
- Place 3 at position (0,0) in result
Element at (1,0) with value 4:
- Convert to 1D index: 1×3 + 0 = 3
- Add shifts: 3 + 4 = 7
- Apply modulo: 7 % 6 = 1
- Convert back to 2D: divmod(1, 3) = (0, 1)
- Place 4 at position (0,1) in result
Element at (1,1) with value 5:
- Convert to 1D index: 1×3 + 1 = 4
- Add shifts: 4 + 4 = 8
- Apply modulo: 8 % 6 = 2
- Convert back to 2D: divmod(2, 3) = (0, 2)
- Place 5 at position (0,2) in result
Element at (1,2) with value 6:
- Convert to 1D index: 1×3 + 2 = 5
- Add shifts: 5 + 4 = 9
- Apply modulo: 9 % 6 = 3
- Convert back to 2D: divmod(3, 3) = (1, 0)
- Place 6 at position (1,0) in result
Final Result:
[[3, 4, 5], [6, 1, 2]]
To verify: if we think of the grid as a flattened array [1,2,3,4,5,6] and shift it 4 times circularly, we get [3,4,5,6,1,2], which matches our result when reshaped back to 2×3.
Solution Implementation
1class Solution:
2 def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]:
3 # Get dimensions of the grid
4 num_rows, num_cols = len(grid), len(grid[0])
5
6 # Initialize result grid with zeros
7 result = [[0] * num_cols for _ in range(num_rows)]
8
9 # Iterate through each element in the original grid
10 for row_idx, row in enumerate(grid):
11 for col_idx, value in enumerate(row):
12 # Convert 2D position to 1D position
13 flat_position = row_idx * num_cols + col_idx
14
15 # Calculate new position after k shifts
16 new_flat_position = (flat_position + k) % (num_rows * num_cols)
17
18 # Convert 1D position back to 2D coordinates
19 new_row, new_col = divmod(new_flat_position, num_cols)
20
21 # Place the value at the new position
22 result[new_row][new_col] = value
23
24 return result
25
1class Solution {
2 public List<List<Integer>> shiftGrid(int[][] grid, int k) {
3 // Get dimensions of the grid
4 int numRows = grid.length;
5 int numCols = grid[0].length;
6
7 // Initialize result list with zeros
8 List<List<Integer>> result = new ArrayList<>();
9 for (int row = 0; row < numRows; row++) {
10 List<Integer> currentRow = new ArrayList<>();
11 for (int col = 0; col < numCols; col++) {
12 currentRow.add(0);
13 }
14 result.add(currentRow);
15 }
16
17 // Shift each element by k positions
18 for (int row = 0; row < numRows; row++) {
19 for (int col = 0; col < numCols; col++) {
20 // Convert 2D position to 1D index
21 int currentIndex = row * numCols + col;
22
23 // Calculate new position after k shifts (with wraparound)
24 int newIndex = (currentIndex + k) % (numRows * numCols);
25
26 // Convert new 1D index back to 2D coordinates
27 int newRow = newIndex / numCols;
28 int newCol = newIndex % numCols;
29
30 // Place the element at its new position
31 result.get(newRow).set(newCol, grid[row][col]);
32 }
33 }
34
35 return result;
36 }
37}
38
1class Solution {
2public:
3 vector<vector<int>> shiftGrid(vector<vector<int>>& grid, int k) {
4 // Get dimensions of the grid
5 int numRows = grid.size();
6 int numCols = grid[0].size();
7
8 // Initialize result grid with same dimensions
9 vector<vector<int>> result(numRows, vector<int>(numCols));
10
11 // Iterate through each element in the original grid
12 for (int row = 0; row < numRows; ++row) {
13 for (int col = 0; col < numCols; ++col) {
14 // Convert 2D position to 1D index
15 int currentIndex = row * numCols + col;
16
17 // Calculate new position after k shifts (with wraparound)
18 int newIndex = (currentIndex + k) % (numRows * numCols);
19
20 // Convert new 1D index back to 2D coordinates
21 int newRow = newIndex / numCols;
22 int newCol = newIndex % numCols;
23
24 // Place the element at its new position
25 result[newRow][newCol] = grid[row][col];
26 }
27 }
28
29 return result;
30 }
31};
32
1/**
2 * Shifts all elements in a 2D grid k positions to the right.
3 * When an element reaches the end of a row, it wraps to the beginning of the next row.
4 * When an element reaches the bottom-right corner, it wraps to the top-left corner.
5 *
6 * @param grid - The input 2D array to be shifted
7 * @param k - The number of positions to shift right
8 * @returns A new 2D array with all elements shifted k positions
9 */
10function shiftGrid(grid: number[][], k: number): number[][] {
11 // Get dimensions of the grid
12 const rowCount: number = grid.length;
13 const columnCount: number = grid[0].length;
14
15 // Initialize result grid with zeros
16 const resultGrid: number[][] = Array.from(
17 { length: rowCount },
18 () => Array.from({ length: columnCount }, () => 0)
19 );
20
21 // Iterate through each element in the original grid
22 for (let row = 0; row < rowCount; row++) {
23 for (let column = 0; column < columnCount; column++) {
24 // Calculate the linear index of current position
25 const currentIndex: number = row * columnCount + column;
26
27 // Calculate new position after shifting k positions
28 const shiftedIndex: number = (currentIndex + k) % (rowCount * columnCount);
29
30 // Convert linear index back to 2D coordinates
31 const newRow: number = Math.floor(shiftedIndex / columnCount);
32 const newColumn: number = shiftedIndex % columnCount;
33
34 // Place the element at its new position
35 resultGrid[newRow][newColumn] = grid[row][column];
36 }
37 }
38
39 return resultGrid;
40}
41
Time and Space Complexity
Time Complexity: O(m × n)
, where m
is the number of rows and n
is the number of columns in the grid. The algorithm uses nested loops to iterate through every element in the grid exactly once. For each element, it performs constant-time operations (calculating the new position using division and modulo operations, and assigning the value to the result array). Therefore, the total time complexity is proportional to the total number of elements in the grid.
Space Complexity: O(m × n)
for the answer array that stores the shifted grid. The algorithm creates a new 2D array ans
with the same dimensions as the input grid to store the result. If we exclude the space required for the output (as mentioned in the reference answer), the auxiliary space complexity is O(1)
since only a constant amount of extra variables (m
, n
, i
, j
, v
, x
, y
) are used regardless of the input size.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Modulo Operation Placement
A common mistake is applying the modulo operation incorrectly, either forgetting it entirely or applying it at the wrong step:
Incorrect:
# Forgetting modulo - causes index out of bounds
new_flat_position = flat_position + k
new_row, new_col = divmod(new_flat_position, num_cols) # Can exceed grid bounds!
# Or applying modulo only to k
new_flat_position = flat_position + (k % (num_rows * num_cols))
# This still causes overflow when flat_position + k exceeds total elements
Correct:
# Apply modulo to the entire sum
new_flat_position = (flat_position + k) % (num_rows * num_cols)
new_row, new_col = divmod(new_flat_position, num_cols)
2. Confusing Row/Column Order in Flattening Formula
Mixing up the conversion between 2D and 1D coordinates is extremely common:
Incorrect:
# Wrong: Using num_rows instead of num_cols flat_position = row_idx * num_rows + col_idx # WRONG! # Wrong: Using column index for multiplication flat_position = col_idx * num_cols + row_idx # WRONG!
Correct:
# Row index × number of columns + column index flat_position = row_idx * num_cols + col_idx
3. Modifying the Original Grid In-Place
Attempting to modify the grid in-place leads to incorrect results because elements get overwritten before they can be moved:
Incorrect:
def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]:
num_rows, num_cols = len(grid), len(grid[0])
for row_idx in range(num_rows):
for col_idx in range(num_cols):
value = grid[row_idx][col_idx]
new_flat_position = (row_idx * num_cols + col_idx + k) % (num_rows * num_cols)
new_row, new_col = divmod(new_flat_position, num_cols)
grid[new_row][new_col] = value # WRONG! Overwrites values we haven't processed yet
return grid
Correct: Always create a new result grid to avoid overwriting values that haven't been processed yet.
4. Inefficient Handling of Large k Values
While the modulo operation handles large k values correctly, not optimizing k beforehand can lead to unnecessary large number operations:
Suboptimal:
# Works but inefficient for very large k new_flat_position = (flat_position + k) % (num_rows * num_cols)
Optimized:
# Pre-reduce k to avoid large number arithmetic k = k % (num_rows * num_cols) # Optimize k at the beginning new_flat_position = (flat_position + k) % (num_rows * num_cols)
This optimization is particularly important when k is much larger than the grid size, as it reduces the computational overhead in subsequent calculations.
Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!