力扣螺旋矩阵系列总结(c++)

 
Category: DSA

写在前面

螺旋矩阵系列, 严格来说不算双指针, 但是其中蕴含的思想很像双指针. (应该叫四指针)

  1. 54. 螺旋矩阵 - 力扣(LeetCode);(需要四个指针分别在需要转弯的时候移动)
  2. 59. 螺旋矩阵 II - 力扣(LeetCode);(跟上面的题异曲同工)
  3. 885. 螺旋矩阵 III - 力扣(LeetCode);(不需要考虑边界直接模拟, 注意这个题是从内往外转, 需要定义方向数组)
  4. 2326. 螺旋矩阵 IV - 力扣(LeetCode);(同基本的螺旋矩阵, 加上链表向后遍历的基本操作即可)

螺旋矩阵I

54. 螺旋矩阵 - 力扣(LeetCode);

只能说, 用Python不讲武德:

  def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
      res = []
      while matrix:
          # 削头(第一层)
          res += matrix.pop(0)
          # 将剩下的逆时针转九十度,等待下次被削
          matrix = list(zip(*matrix))[::-1]
      return res

C++的写法很简练, 思路直接在代码中体现出来了. 四个变量逐次更新.

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size(), SIZE = m * n;
        int l{}, r{n - 1}, t{}, b{m - 1}, i{}, x{}, y{};
        vector<int> ans(SIZE);
        while (i < SIZE) {
            while (y <= r && i < SIZE) ans[i++] = matrix[t][y++];
            ++t, x = t;
            while (x <= b && i < SIZE) ans[i++] = matrix[x++][r];
            --r, y = r;
            while (y >= l && i < SIZE) ans[i++] = matrix[b][y--];
            --b, x = b;
            while (x >= t && i < SIZE) ans[i++] = matrix[x--][l];
            ++l, y = l;
        }
        return ans;
    }
};
// 用for循环也一样: 可能看起来简练一些
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int m=matrix.size(), n = matrix[0].size();
        int t{}, l{-1}, b{m-1}, r{n-1}, N=m*n, k{};
        vector<int> ans(N);
        while (k < N) {
            for (int i{++l}; k < N && i <= r; ++i) ans[k++] = matrix[t][i];
            for (int i{++t}; k < N && i <= b; ++i) ans[k++] = matrix[i][r];
            for (int i{--r}; k < N && i >= l; --i) ans[k++] = matrix[b][i];
            for (int i{--b}; k < N && i >= t; --i) ans[k++] = matrix[i][l];
        }
        return ans;
    }
};

或者用一种定义方向数组的写法, 算是一种模板了.

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size(), N = m * n;
        // 方向: 右下左上
        int dirs[4][2]{0, 1, 1, 0, 0, -1, -1, 0};
        int k{}, r{}, c{}, d{}, i{}, j{};
        vector<int> ans(N);
        while (k < N) {
            ans[k++] = matrix[i][j];
            matrix[i][j] = 101; // 标记遍历过
            r = i + dirs[d][0], c = j + dirs[d][1];
            // 换向
            if (r < 0 || r >= m || c < 0 || c >= n || matrix[r][c] == 101)
                d = (d + 1) % 4, r = i + dirs[d][0], c = j + dirs[d][1];
            i = r, j = c;
        }
        return ans;
    }
};

螺旋矩阵II

59. 螺旋矩阵 II - 力扣(LeetCode);

第一题代码改改还能用:

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        int SIZE = n * n;
        int l{}, r{n - 1}, t{}, b{n - 1}, i{1}, x{}, y{};
        vector<vector<int>> ans(n, vector<int>(n));
        while (i <= SIZE) {
            for (y = l; y <= r; ++y) ans[t][y] = i++;
            ++t;
            for (x = t; x <= b; ++x) ans[x][r] = i++;
            --r;
            for (y = r; y >= l; --y) ans[b][y] = i++;
            --b;
            for (x = b; x >= t; --x) ans[x][l] = i++;
            ++l;
        }
        return ans;
    }
};

定义方向数组的方法: (不容易想, 但是代码相对简洁)

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        int N = n * n, k{1};
        // 方向: 右下左上
        int dirs[4][2]{0, 1, 1, 0, 0, -1, -1, 0};
        int r{}, c{}, d{}, i{}, j{};
        vector<vector<int>> ans(n, vector<int>(n));
        while (k <= N) {
            ans[i][j] = k++;
            r = i + dirs[d][0], c = j + dirs[d][1];
            // 换向
            if (r < 0 || r >= n || c < 0 || c >= n || ans[r][c])
                d = (d + 1) % 4, r = i + dirs[d][0], c = j + dirs[d][1];
            i = r, j = c;
        }
        return ans;
    }
};

螺旋矩阵III

885. 螺旋矩阵 III - 力扣(LeetCode);

这个题和1,2,4不太一样, 原因在于旋转是由内而外的了, 这就要考虑一下边界情况了.

用定义四个方向数组的方法套模板就可以.

class Solution {
public:
    vector<vector<int>> spiralMatrixIII(int rows, int cols, int rStart,
                                        int cStart) {
        int dirs[4][2]{0, 1, 1, 0, 0, -1, -1, 0};
        int r{rStart}, c{cStart}, N{rows * cols}, steps{}, d{}, k{1};
        vector<vector<int>> ans(N);
        ans[0] = {r, c};
        while (k < N) {
            ++steps;
            for (int p{}; p < 2; ++p) {
                for (int i{}; i < steps; ++i) {
                    r += dirs[d][0], c += dirs[d][1];
                    if (k < N && r >= 0 && r < rows && c >= 0 && c < cols)
                        ans[k++] = {r, c};
                }
                d = (d + 1) % 4;
            }
        }
        return ans;
    }
};

螺旋矩阵IV

2326. 螺旋矩阵 IV - 力扣(LeetCode);

熟悉链表的遍历, 这道题就会了. 直接用第一题代码.

class Solution {
public:
    vector<vector<int>> spiralMatrix(int m, int n, ListNode* head) {
        vector<vector<int>> ans(m, vector<int>(n, -1));
        int t{}, l{}, r{n - 1}, b{m - 1}, i{}, j{};
        while (head) {
            for (j = l; j <= r && head; ++j, head = head->next)
                ans[t][j] = head->val;
            ++t;
            for (i = t; i <= b && head; ++i, head = head->next)
                ans[i][r] = head->val;
            --r;
            for (j = r; j >= l && head; --j, head = head->next)
                ans[b][j] = head->val;
            --b;
            for (i = b; i >= t && head; --i, head = head->next)
                ans[i][l] = head->val;
            ++l;
        }
        return ans;
    }
};

方向数组版:

class Solution {
public:
    vector<vector<int>> spiralMatrix(int m, int n, ListNode* head) {
        // 方向: 右下左上
        int dirs[4][2]{0, 1, 1, 0, 0, -1, -1, 0};
        int r{}, c{}, d{}, i{}, j{};
        vector<vector<int>> ans(m, vector<int>(n, -1));
        while (head) {
            ans[i][j] = head->val, head = head->next;
            r = i + dirs[d][0], c = j + dirs[d][1];
            // 换向
            if (r < 0 || r >= m || c < 0 || c >= n || ans[r][c] != -1)
                d = (d + 1) % 4, r = i + dirs[d][0], c = j + dirs[d][1];
            i = r, j = c;
        }
        return ans;
    }
};

通用方法

可以看出熟悉套路之后I,II,IV都可以迎刃而解, 但是III需要考虑的多一些, 比如换向和 steps 递增.

class Solution {
public:
    vector<vector<int>> spiralMatrix(int m, int n, ListNode* head) {
        // 方向数组: 右下左上
        int dirs[4][2]{0, 1, 1, 0, 0, -1, -1, 0};
        
        // 循环变量
        int r{}, c{}, d{}, i{}, j{};
        // 初始化结果数组
        vector<int> ans(m * n);
        while (/* 满足循环条件 */) {
            // 更新结果
            ans[i][j] = k
            r = i + dirs[d][0], c = j + dirs[d][1];

            if (r < 0 || r >= m || c < 0 || c >= n || /* 元素被遍历过? */)
                // 换方向, 更新步
                d = (d + 1) % 4, r = i + dirs[d][0], c = j + dirs[d][1];
            // 更新索引
            i = r, j = c;
        }
        return ans;
    }
};