• 【LeetCode】矩阵操作


    1. 矩阵旋转

    将 n × n 矩阵顺时针旋转 90°。

    我的思路是 “ 从外到内一层一层旋转 ”。

    一个 n × n 矩阵有 (n + 1) / 2 层,每层有 4 部分,将这 4 部分旋转。

    顺时针旋转 90° 就是将 matrix[n - 1 - q][p] 赋值给 matrix[p][q] 即可。

    C++代码:

     1 void rotate(vector<vector<int>>& matrix) {
     2     int n = matrix.size();
     3     for (int i = 0; i < (n + 1) / 2; i++) {
     4         for (int j = i; j < n - 1 - i; j++) {
     5             int p = i, q = j;
     6             int temp = matrix[p][q];
     7             for (int k = 0; k < 3; k++) {
     8                 matrix[p][q] = matrix[n - 1 - q][p];
     9                 int tp = p;
    10                 p = n - 1 - q;
    11                 q = tp;
    12             }
    13             matrix[p][q] = temp;
    14         }
    15     }
    16 }

    答案的方法非常直观易懂!学到了!

    首先将矩阵从上到下逆置,然后按对角线对称交换元素。

    1 2 3    7 8 9    7 4 1
    4 5 6 => 4 5 6 => 8 5 2
    7 8 9    1 2 3    9 6 3
    1 void rotate(vector<vector<int> > &matrix) {
    2     reverse(matrix.begin(), matrix.end());
    3     for (int i = 0; i < matrix.size(); ++i) {
    4         for (int j = i + 1; j < matrix[i].size(); ++j)
    5             swap(matrix[i][j], matrix[j][i]);
    6     }
    7 }

    逆时针旋转的原理类似。

    首先将矩阵从左到右逆置,然后按对角线对称交换元素。注意从上到下和从左到右逆置矩阵的区别

    1 2 3     3 2 1     3 6 9
    4 5 6  => 6 5 4  => 2 5 8
    7 8 9     9 8 7     1 4 7
    1 void anti_rotate(vector<vector<int> > &matrix) {
    2     for (auto vi : matrix)
    3         reverse(vi.begin(), vi.end());
    4     for (int i = 0; i < matrix.size(); ++i) {
    5         for (int j = i + 1; j < matrix[i].size(); ++j)
    6             swap(matrix[i][j], matrix[j][i]);
    7     }
    8 }

    2. 矩阵螺旋遍历

    给定 m x n 矩阵,以螺旋顺序遍历矩阵所有元素。

    e.g. 给出如下矩阵

    [
        [ 1, 2, 3 ],
        [ 4, 5, 6 ],
        [ 7, 8, 9 ]
    ]

    返回 [1, 2, 3, 6, 9, 8, 7, 4, 5]。

    我尝试定义行标记 i 和列标记 j,第一层是 (0, 0),第二层是 (1, 1),不断循环。每次循环内改变 i 和 j,实现螺旋遍历。但实现起来代码要写的非常冗长,比如最内层只有一行或只有一列时,就会出错。于是看了别人的解法,深受启发!

    方法一:死办法

     1 vector<int> spiralOrder(vector<vector<int>>& matrix) {
     2     if (matrix.empty()) return {};
     3     int m = matrix.size(), n = matrix[0].size();
     4     vector<int> result(m * n);
     5     int u = 0, d = m - 1, l = 0, r = n - 1, k = 0;
     6     while (1) {
     7         for (int j = l; j <= r; j++) result[k++] = matrix[u][j];
     8         if (++u > d) break;
     9         for (int i = u; i <= d; i++) result[k++] = matrix[i][r];
    10         if (--r < l) break;
    11         for (int j = r; j >= l; j--) result[k++] = matrix[d][j];
    12         if (--d < u) break;
    13         for (int i = d; i >= u; i--) result[k++] = matrix[i][l];
    14         if (++l > r) break;
    15     }
    16     return result;
    17 }

    定义 u(上),d(下),l(左),r(右)表示各个方向的极限下标,转了一圈后,由于 ++u、--r、--d、++l ,进入里面一层继续螺旋遍历。

    此外,由于知道结果数组大小(m x n),预先分配空间并直接赋值要比 push_back 效率高。

    方法二:方向矩阵法

    原理:

        螺旋遍历就是不断地向 4 个方向(右、下、左、上)移动。假设要处理一个 5 x 3 的矩阵,初始标志位坐标设为 (0, -1),即 '0' 的位置。

    0 [ 1  2  3  4  5]
      [ 6  7  8  9 10]
      [11 12 13 14 15]

    接下来需要移动标志位:

    • 向右移动 5 位
      向下移动 2 位
    • 向左移动 4 位
      向上移动 1 位
    • 向右移动 3 位
      向下移动 0 位 --> 结束

    注意到方向一直是 “ 右 → 下 → 左 →上 ”,水平移动的步数是 { 5,4,3 } (5 是矩阵的行数),竖直移动的步数是 { 2,1,0 } (2 是矩阵的列数减 1)。

    因此,可以构造一个方向矩阵存储所有方向,以及一个含有两个元素的数组存储水平和竖直移动的步数。这样就用一次循环代替了四次循环。

    而且这样做的一个好处是,如果我们要改变遍历的起点(如从右上角元素开始),或者改变螺旋的方向(如逆时针),只需要改变方向矩阵,循环主体不需要改变。虽然更为复杂,但增加了代码的可重用性!

    C++实现:

     1 vector<int> spiralOrder(vector<vector<int>>& matrix) {
     2     if (matrix.empty()) return {};
     3     int m = matrix.size(), n = matrix[0].size();
     4     vector<int> result;
     5     vector<vector<int>> dir = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
     6     vector<int> step = {n, m - 1};
     7     int i = 0, j = -1, dirIndex = 0, stepIndex = 0;
     8     while (step[stepIndex]) {
     9         for (int k = 0; k < step[stepIndex]; k++) {
    10             i += dir[dirIndex][0];
    11             j += dir[dirIndex][1];
    12             result.push_back(matrix[i][j]);
    13         }
    14         step[stepIndex]--;
    15         stepIndex = (stepIndex + 1) % 2;
    16         dirIndex = (dirIndex + 1) % 4;
    17     }
    18     return result;
    19 }

    可以发现 stepIndex 刚好等于 dirIndex % 2,可以进行替换,少定义一个变量,但也降低了可读性。

    3. 螺旋矩阵

    给定一个整数 n,产生一个方阵,该方阵由元素 1 到 n2 以螺旋顺序填充。

    e.g. n = 3,返回矩阵

    [
        [ 1, 2, 3 ],
        [ 8, 9, 4 ],
        [ 7, 6, 5 ]
    ]

    死办法,定义 u、d、l、r。

     1 vector<vector<int>> generateMatrix(int n) {
     2     /* 注意 vector<vector<int>> 的初始化方式 */
     3     vector<vector<int>> result(n, vector<int>(n));
     4     int u = 0, r = n - 1, d = n - 1, l = 0, k = 1;
     5     while(1) {
     6         for (int j = l; j <= r; j++)    result[u][j] = k++;
     7         if (++u > d)  break;
     8         for (int i = u; i <= d; i++)    result[i][r] = k++;
     9         if (--r < l)  break;
    10         for (int j = r; j >= l; j--)    result[d][j] = k++;
    11         if (--d < u)  break;
    12         for (int i = d; i >= u; i--)    result[i][l] = k++;
    13         if (++l > r)  break;
    14     }
    15     return result;
    16 }

     也可以使用定义方向矩阵的方法。

  • 相关阅读:
    接口测试
    Appium应用
    adb常用指令与APPium环境搭建
    移动端专项测试
    tomcat修改端口号
    Linux之Redis安装
    FTL常用标签及语法
    .ftl文件介绍
    maven之clean、install命令
    tomcat环境变量详细配置步骤
  • 原文地址:https://www.cnblogs.com/wayne793377164/p/7229592.html
Copyright © 2020-2023  润新知