【leetcode常见面试题】螺旋矩阵解题思路

文章目录

  • 螺旋矩阵
    • 解题思路
      • 先找行进路线
      • 找每条路线的结束位置
      • 再找每条路线的结束位置
      • 模拟行走
  • 螺旋矩阵 II
  • 总结

螺旋矩阵

【leetcode常见面试题】螺旋矩阵解题思路_第1张图片

解题思路

本题可以采用模拟的方式,设4种行走方向,如下图:

先找行进路线

4个方向的行走路线分别是:从左到右,从上到下,从右到左,从下到上。

并且
从左到右是从第一行开始,
从上到下是从最后一列开始,
从右到左是从最后一行开始,
从下到上是从第一列开始。

【leetcode常见面试题】螺旋矩阵解题思路_第2张图片

代码定义如下:

public List<Integer> spiralOrder(int[][] matrix) {
    // 结果集
    List<Integer> ans = new ArrayList<>();
    // 定义4个方向
    int leftToRight = 0;
    int upToDown = matrix[0].length - 1;
    int rightToLeft = matrix.length - 1;
    int downToUp = 0;
    return ans;
}

当第一圈走完以后,4个方向一定都会向内收缩一圈。

public List<Integer> spiralOrder(int[][] matrix) {
    // 结果集
    List<Integer> ans = new ArrayList<>();
    // 定义4个方向
    int leftToRight = 0;
    int upToDown = matrix[0].length - 1;
    int rightToLeft = matrix.length - 1;
    int downToUp = 0;
    // 第一行走完
    leftToRight++;
    // 最后一列走完
    upToDown--;
    // 最后一行走完
    rightToLeft--;
    // 第一列走完
    downToUp++;
    return ans;
}

找每条路线的结束位置

现在我们只需要搞清楚如何定义每个方向算走完这个逻辑即可,从图上可以看出,每个方向的结束位置,就是下一个方向的开始位置,比如从左向右走时,行保持不变,列应该一直走到下一个方向的开始位置,即upToDown定义的范围。
从上到下走时,列保持不变,行应该一直走到下一个方向的开始位置,即rightToLeft定义的范围。

由此可得,如下代码:

public List<Integer> spiralOrder(int[][] matrix) {
    // 结果集
    List<Integer> ans = new ArrayList<>();
    
    // 定义4个方向
    int leftToRight = 0;
    int upToDown = matrix[0].length - 1;
    int rightToLeft = matrix.length - 1;
    int downToUp = 0;
    
    // 1. 从左到右
    for(int i = ?; i <= upToDown; i++){
    }
    // 从左到右走完
    leftToRight++;
    
    // 2. 从上到下
    for(int i = ?; i <= rightToLeft; i++){
    }
    // 从上到下走完
    upToDown--;
    
    // 3. 从右到左
    for(int i = ?; i >= downToUp; i--){
    }
    // 从右到左走完
    rightToLeft--;
    
    // 4. 从下到上
    for(int i = ?; i >= leftToRight; i--){
    }
    // 从下到上走完
    downToUp++;
    return ans;
}

再找每条路线的结束位置

弄清楚4个方向的结束位置之后,剩下来的只要在定义好开始位置就好了,很明显,上一个方向的结束位置,即是当前方向的开始位置。

所以,我们填入4个方向的开始值后,代码如下:

public List<Integer> spiralOrder(int[][] matrix) {
    // 结果集
    List<Integer> ans = new ArrayList<>();
    // 定义4个方向
    int leftToRight = 0;
    int upToDown = matrix[0].length - 1;
    int rightToLeft = matrix.length - 1;
    int downToUp = 0;
    // 1. 从左到右
    for (int i = downToUp; i <= upToDown; i++) {
    }
    // 从左到右走完
    leftToRight++;
    // 2. 从上到下
    for (int i = leftToRight; i <= rightToLeft; i++) {
    }
    // 从上到下走完
    upToDown--;
    // 3. 从右到左
    for (int i = upToDown; i >= downToUp; i--) {
    }
    // 从右到左走完
    rightToLeft--;
    // 4. 从下到上
    for (int i = rightToLeft; i >= leftToRight; i--) {
    }
    // 从下到上走完
    downToUp++;
    return ans;
}

模拟行走

接下来,让每个方向走起来,并挨个添加到集合中,代码如下:

public List<Integer> spiralOrder(int[][] matrix) {
    // 结果集
    List<Integer> ans = new ArrayList<>();
    // 定义4个方向
    int leftToRight = 0;
    int upToDown = matrix[0].length - 1;
    int rightToLeft = matrix.length - 1;
    int downToUp = 0;
    // 1. 从左到右
    for (int i = downToUp; i <= upToDown; i++) {
        ans.add(matrix[leftToRight][i]);
    }
    // 从左到右走完
    leftToRight++;
    // 2. 从上到下
    for (int i = leftToRight; i <= rightToLeft; i++) {
        ans.add(matrix[i][upToDown]);
    }
    // 从上到下走完
    upToDown--;
    // 3. 从右到左
    for (int i = upToDown; i >= downToUp; i--) {
        ans.add(matrix[rightToLeft][i]);
    }
    // 从右到左走完
    rightToLeft--;
    // 4. 从下到上
    for (int i = rightToLeft; i >= leftToRight; i--) {
        ans.add(matrix[i][downToUp]);
    }
    // 从下到上走完
    downToUp++;
    return ans;
}

矩阵大小即是一共要走的步数,那么每走一步记录一下,直到走满矩阵大小即表示走完。

最后代码实现如下:

public List<Integer> spiralOrder(int[][] matrix) {
    // 结果集
    List<Integer> ans = new ArrayList<>();
    int step = 0;
    int totalStep = matrix.length * matrix[0].length;
    // 定义4个方向
    int leftToRight = 0;
    int upToDown = matrix[0].length - 1;
    int rightToLeft = matrix.length - 1;
    int downToUp = 0;
    // 外层while控制每一圈走完后,要不要继续走
    // 里面的每一个for控制每一步走完后,要不要继续走
    while (step < totalStep) {
        // 1. 从左到右
        for (int i = downToUp; i <= upToDown && step < totalStep; i++) {
            ans.add(matrix[leftToRight][i]);
            step++;
        }
        // 从左到右走完
        leftToRight++;
        // 2. 从上到下
        for (int i = leftToRight; i <= rightToLeft && step < totalStep; i++) {
            ans.add(matrix[i][upToDown]);
            step++;
        }
        // 从上到下走完
        upToDown--;
        // 3. 从右到左
        for (int i = upToDown; i >= downToUp && step < totalStep; i--) {
            ans.add(matrix[rightToLeft][i]);
            step++;
        }
        // 从右到左走完
        rightToLeft--;
        // 4. 从下到上
        for (int i = rightToLeft; i >= leftToRight && step < totalStep; i--) {
            ans.add(matrix[i][downToUp]);
            step++;
        }
        // 从下到上走完
        downToUp++;
    }
    return ans;
}

螺旋矩阵 II

使用同样的套路即可快速完成
【leetcode常见面试题】螺旋矩阵解题思路_第3张图片

public int[][] generateMatrix(int n) {
    int[][] ans = new int[n][n];
    int step = 0;
    int leftToRight = 0;
    int upToDown = n - 1;
    int rightToLeft = n - 1;
    int downToUp = 0;
    while (step < n * n) {
        for (int i = downToUp; i <= upToDown && step < n * n; i++) {
            ans[leftToRight][i] = ++step;
        }
        leftToRight++;
        for (int i = leftToRight; i <= rightToLeft && step < n * n; i++) {
            ans[i][upToDown] = ++step;
        }
        upToDown--;
        for (int i = upToDown; i >= downToUp && step < n * n; i--) {
            ans[rightToLeft][i] = ++step;
        }
        rightToLeft--;
        for (int i = rightToLeft; i >= leftToRight && step < n * n; i--) {
            ans[i][downToUp] = ++step;
        }
        downToUp++;
    }
    return ans;
}

总结

记住三个关键点即可:

  1. 总共上下左右4个方向。
  2. 每个方向的开始都由上一个方向的结束来决定。
  3. 每个方向的结束都由下一个方向的开始来决定。

你可能感兴趣的:(leetcode,矩阵,算法)