LeetCode 1260 - Shift 2D Grid


https://leetcode.com/problems/shift-2d-grid/
Given a 2D grid of size m x n and an integer k. You need to shift the grid k times.
In one shift operation:
  • Element at grid[i][j] becomes at grid[i][j + 1].
  • Element at grid[i][n - 1] becomes at grid[i + 1][0].
  • Element at grid[n - 1][n - 1] becomes at grid[0][0].
Return the 2D grid after applying shift operation k times.

Example 1:
Input: grid = [[1,2,3],[4,5,6],[7,8,9]], k = 1
Output: [[9,1,2],[3,4,5],[6,7,8]]
Example 2:
Input: grid = [[3,8,1,9],[19,7,2,5],[4,6,11,10],[12,0,21,13]], k = 4
Output: [[12,0,21,13],[3,8,1,9],[19,7,2,5],[4,6,11,10]]
Example 3:
Input: grid = [[1,2,3],[4,5,6],[7,8,9]], k = 9
Output: [[1,2,3],[4,5,6],[7,8,9]]

Constraints:
  • m == grid.length
  • n == grid[i].length
  • 1 <= m <= 50
  • 1 <= n <= 50
  • -1000 <= grid[i][j] <= 1000
  • 0 <= k <= 100
https://leetcode.com/problems/shift-2d-grid/discuss/431102/JavaPython-3-2-simple-codes-using-mod-space-O(m-*-n)-and-O(1).
  1. Number the cells from 0 to m * n - 1;
  2. In case k >= m * n, use k % (m * n) to avoid those whole cycles of m * n operations;
Method 1: space O(m * n)
Since shifting right will put the last k cells in grid on the first k cells, we start from the kth cells from last, the index of which is m * n - k % (m * n).
    public List<List<Integer>> shiftGrid(int[][] grid, int k) {
        int m = grid.length, n = grid[0].length; 
        int start = m * n - k % (m * n);
        LinkedList<List<Integer>> ans = new LinkedList<>();
        for (int i = start; i < m * n + start; ++i) {
            int j = i % (m * n), r = j / n, c = j % n;
            if ((i - start) % n == 0)
                ans.add(new ArrayList<>());
            ans.peekLast().add(grid[r][c]);
        }
        return ans;
    }
    def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]:
        m, n = len(grid), len(grid[0])
        start = m * n - k % (m * n)
        ans = []
        for i in range(start, m * n + start):
            j = i % (m * n)
            r, c =  j // n, j % n
            if not (j - start) % n:
                ans.append([])
            ans[-1].append(grid[r][c]) 
        return ans

Method 2: space O(1) - excluding return list
  1. Imagine the grid to be a 1-D array of size m * n;
  2. reverse the whole array;
  3. reverse the first k elements
  4. reverse the remaing m * n - k element.
    public List<List<Integer>> shiftGrid(int[][] grid, int k) {
        int m = grid.length, n = grid[0].length;
        k %= m * n;
        reverse(grid, 0, m * n - 1);
        reverse(grid, 0, k - 1);
        reverse(grid, k, m * n - 1);
        List<List<Integer>> ans = new ArrayList<>();
        for (int[] row : grid)
            ans.add(Arrays.stream(row).boxed().collect(Collectors.toList()));
        return ans;
    }
    private void reverse(int[][] g, int lo, int hi) {
        int m = g.length, n = g[0].length;
        while (lo < hi) {
            int r = lo / n, c = lo++ % n, i = hi / n, j = hi-- % n, 
            tmp = g[r][c];
            g[r][c] = g[i][j];
            g[i][j] = tmp;
        }
    }
    def shiftGrid(self, grid: List[List[int]], k: int) -> List[List[int]]:

        def reverse(lo: int, hi: int) -> None:
            while lo < hi:
                r, c, i, j = lo // cols, lo % cols, hi // cols, hi % cols
                grid[r][c], grid[i][j] = grid[i][j], grid[r][c]
                lo += 1
                hi -= 1

        rows, cols = len(grid), len(grid[0])
        k %= rows * cols
        reverse(0, rows * cols - 1)
        reverse(0, k - 1)
        reverse(k, rows * cols - 1)
        return grid

https://leetcode.com/problems/shift-2d-grid/discuss/431111/Simple-to-understand-java
  public static List<List<Integer>> shiftGrid(int[][] grid, int k) {
        int[][] temp = new int[grid.length][grid[0].length]; // took temp grid
        int n = grid.length;
        int m = grid[0].length;
        int mod = n * m;
        k = k % mod; // minimize the k value
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                int p = j + k; // defines which col
                int r = p / (m); // defines which row
                if (p < m) {
                    temp[i][p] = grid[i][j];
                } else {
                    temp[(i + r) % n][p % m] = grid[i][j];
                }
            }
        }
  // making temp grid into list
        List<List<Integer>> result = new LinkedList<>();
        for (int[] t : temp) {
            LinkedList<Integer> c = new LinkedList<>();
            for (int i : t) {
                c.add(i);
            }
            result.add(c);
        }
        return result;
    }

https://www.cnblogs.com/wentiliangkaihua/p/11886518.html
首先是最小化k,从原理(画个2*2)可以得到k可以优化到k % (n*m).
接下来要确定每个元素要转移到的位置,首先是column因为比较简单,算当前col(j)+ k = p,然后p < m时可以直接转换temp[i][p] = grid[i][j];
如果p>m, column就要再模m,p % m = j。
然后是比较难的row,如果p < m,就可以直接用temp[i][p] = grid[i][j]
如果p>m, 根据题意我们需要把每个第一列的元素下移,最后一个元素登顶,因为最终决定位置的是列,所以最重要加的只有(j+k) / m,最后再+row(i)模n
temp[(i + r) % n][p % m] = grid[i][j];
     public static List<List<Integer>> shiftGrid(int[][] grid, int k) {
            int[][] temp = new int[grid.length][grid[0].length]; // took temp grid
            int n = grid.length;
            int m = grid[0].length;
            int mod = n * m;
            k = k % mod; // minimize the k value
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    int p = j + k; // defines which col
                    int r = p / (m); // defines which row,因为整个process是column在移动所以移动整数倍m是一样的
                    if (p < m) {
                        temp[i][p] = grid[i][j];
                    } else {
                        temp[(i + r) % n][p % m] = grid[i][j];
                    }
                }
            }
            // making temp grid into list
            List<List<Integer>> result = new LinkedList<>();
            for (int[] t : temp) {
                LinkedList<Integer> c = new LinkedList<>();
                for (int i : t) {
                    c.add(i);
                }
                result.add(c);
            }
            return result;
        }

     public static List<List<Integer>> shiftGrid(int[][] grid, int k) {
            int[][] temp = new int[grid.length][grid[0].length]; // took temp grid
            int n = grid.length;
            int m = grid[0].length;
            int mod = n * m;
            k = k % mod; // minimize the k value
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    int p = j + k; // defines which col
                    int r = p / m; // defines which row
                        temp[(i + r) % n][p % m] = grid[i][j]; 
                }
            }
            // making temp grid into list
            List<List<Integer>> result = new LinkedList<>();
            for (int[] t : temp) {
                LinkedList<Integer> c = new LinkedList<>();
                for (int i : t) {
                    c.add(i);
                }
                result.add(c);
            }
            return result;
        }
(数学,原地算法) O(nm)
  1. 使用额外数组的算法很基础,这里考虑不使用额外数组,且只能遍历一次整个数组。
  2. 观察从 (0, 0) 位置开始的一次 k 迁移,(0, 0) 被换到 (0, k),依次类推。如果 k 和 n*m 互质,则所有位置就都换了一次,这样的迁移仅需要常数的空间。
  3. 如果 k 和 n*m 不互质,则除了从 (0, 0) 开始后,还需要从 (0, 1)(0, 2) 等位置开始,一直到 (0, g - 1) 的位置开始迁移,其中 g 为最大公约数。
  4. 虽然我们从多个位置开始了迁移,但每个位置的迁移序列都不相同,即每个位置仅会被换一次。

时间复杂度

  • 求最大公约数的时间复杂度为 O(log(nm))
  • 每个位置仅会被换一次,故总时间复杂度为 O(nm)

空间复杂度

  • 原地算法,只需要常数的额外空间。
    int gcd(int x, int y) {
        while (y) {
            int t = x % y;
            x = y;
            y = t;
        }
        return x;
    }

    vector<vector<int>> shiftGrid(vector<vector<int>>& grid, int k) {
        int n = grid.size(), m = grid[0].size();
        int g = gcd(k, n * m);

        for (int s = 0; s < g; s++) {
            int x = s / m, y = s % m;
            int tmp = grid[x][y];

            while (1) {
                int nx = x, ny = y - k;
                while (ny < 0) {
                    nx = (nx - 1 + n) % n;
                    ny += m;
                }
                grid[x][y] = grid[nx][ny];
                if (nx == s / m && ny == s % m) {
                    grid[x][y] = tmp;
                    break;
                }
                x = nx; y = ny;
            }
        }

        return grid;
    }

Solution 2: Rotate
Shift k times is equivalent to flatten the matrix and rotate by -k or (m*n – k % (m * n)).

Time complexity: O(m*n)
Space complexity: O(m*n)
  vector<vector<int>> shiftGrid(vector<vector<int>>& grid, int k) {  
    const int n = grid.size();
    const int m = grid[0].size();
    k = m * n - k % (m*n);
    vector<int> g;
    for (int i = 0; i < n; ++i)
      g.insert(end(g), begin(grid[i]), end(grid[i]));
    rotate(begin(g), begin(g) + k, end(g));
    auto it = begin(g);
    for (int i = 0; i < n; ++i)
      for (int j = 0; j < m; ++j)
        grid[i][j] = *it++;
    return grid;
  }

O(1) space in-place rotation
  MatrixIterator(vector<vector<int>>& data, int index = 0) :
    data_(data),
    index_(index) {}
  
  MatrixIterator& operator++() {
    ++index_;
    return *this;
  }  
  
  MatrixIterator& operator--() {
    --index_;
    return *this;
  }
  
  MatrixIterator operator +(int dis) const {
    return MatrixIterator(data_, index_ + dis);
  }
  
  MatrixIterator operator -(int dis) const {
    return MatrixIterator(data_, index_ - dis);
  }
  
  bool operator==(const MatrixIterator& o) const {
    return data_ == o.data_ && index_ == o.index_;
  }    
  
  bool operator!=(const MatrixIterator& o) const {
    return data_ != o.data_ || index_ != o.index_;
  }
  
  ptrdiff_t operator-(const MatrixIterator& o) const {
    return index_ - o.index_;
  }
  
  MatrixIterator& operator=(const MatrixIterator& o) {
    data_ = o.data_;
    index_ = o.index_;
    return *this;
  }
  
  int& operator*() {
    if (index_ <= 0) {
      return data_[0][0];
    } else if (index_ >= m() * n()) {
      return data_.back().back();
    } else {
      return data_[index_ / m()][index_ % m()];
    }
  }
  
private:
  int n() const { return data_.size(); }
  int m() const { return n() == 0 ? 0 : data_[0].size(); }  
  vector<vector<int>>& data_;
  int index_;
};
 
namespace std {
  template<>
  struct iterator_traits<MatrixIterator> {
    typedef ptrdiff_t                  difference_type;
    typedef int                        value_type;
    typedef int*                       pointer;
    typedef int&                       reference;
    typedef random_access_iterator_tag iterator_category;
  };
}
 
class Solution {
public:
  vector<vector<int>> shiftGrid(vector<vector<int>>& grid, int k) {  
    const int n = grid.size();
    const int m = grid[0].size();
    k = m * n - k % (m*n);
    MatrixIterator it(grid);
    rotate(it, it + k, it + m * n);
    return grid;
  }


Solution 1: Simulation
Simulate the shift process for k times.

Time complexity: O(k*n*m)
Space complexity: O(1) in-place
  vector<vector<int>> shiftGrid(vector<vector<int>>& grid, int k) {
    const int n = grid.size();
    const int m = grid[0].size();
    while (k--) {
      int last = grid[n - 1][m - 1];
      for (int i = n - 1; i >= 0; --i)
        for (int j = m - 1; j >= 0; --j) {          
          if (i == 0 && j == 0)
            grid[i][j] = last;
          else if (j == 0)
            grid[i][j] = grid[i - 1][m - 1];
          else
            grid[i][j] = grid[i][j - 1];
        }
      }
    return grid;
    }    


Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts