Facebook Pixel

1260. Shift 2D Grid

EasyArrayMatrixSimulation
Leetcode Link

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 to grid[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 at grid[i + 1][0]
  • The element at the bottom-right corner grid[m - 1][n - 1] wraps around to the top-left corner grid[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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Convert each 2D position (i,j) to a 1D index using the formula index = i * n + j (where n is the number of columns)
  2. Add k to this index to get the new position after k shifts
  3. Use modulo (m * n) to handle the circular wrapping
  4. Convert the new 1D index back to 2D coordinates using x = index // n and y = 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:

  1. Initialize the result grid: Create a new 2D array ans with the same dimensions m x n as the input grid, filled with zeros.

  2. Iterate through each element: Use nested loops to visit each element in the original grid at position (i, j) with value v.

  3. Calculate the flattened index: For each element at position (i, j), convert it to a 1D index using the formula i * n + j. This treats the 2D grid as if it were a single continuous array.

  4. Apply the shift: Add k to the flattened index to simulate k shifts: (i * n + j + k).

  5. 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.

  6. 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 index
    • y = new_index % n gives the column index
    • Python's divmod does both operations at once: x, y = divmod(new_index, n)
  7. Place the element: Assign the original value v to its new position ans[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 Evaluator

Example 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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Consider the classic dynamic programming of fibonacci numbers, what is the recurrence relation?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More