59. 螺旋矩阵 II

59. 螺旋矩阵 II

题目链接
代码随想录

分析

我们可以按照顺序模拟这个过程,而且因为是正方形矩阵,可以像洋葱一样,一层一层往里遍历,遍历完外面的一层,再遍历里面的一层,而遍历下一圈的时候,实际上就换个起点就好了,那么问题就变成了如何遍历矩阵的一圈。
遍历一圈最大的问题是如何转向,其实,看起来很难,实际上只需要把条件设置好即可。这里我们参考卡哥的思路:
将一圈分成四条边,我们设置好四个方向的边界值,注意,每条边的最后一个位置都不在当前这个方向走完,而是在此转向,作为下一个方向的起点,于是,每次走到这个方向的边界值就转向下一个方向,直到回到起点,然后更新边界值,和起点,直到边界值相遇。
500

解题

下面这个写法,可以用于解决螺旋矩阵的通用问题代码。比如解决 54. 螺旋矩阵

public int[][] generateMatrix(int n) {
    int[][] result = new int[n][n];
    // 当前位置
    int rowNow=0,colNow=0;
	// 边界条件
    int rowMin=0,rowMax=n-1,colMin=0,colMax=n-1;
    for(int i=1;i<=n*n;i++){
	    // 如果只有一个点,则没有必要模拟转圈了,直接填充返回即可
        if(colMin==colMax){
            result[rowNow][colNow]=i;
        }else if(rowMin == rowNow && colMin<=colNow && colNow<colMax){
			// 从左到右,此时 rowNow 必须为 rowMin,colNow 的活动范围是 [colMin,colMax) 
            result[rowNow][colNow]=i;
            colNow++;
        }else if(colMax == colNow && rowMin<=rowNow && rowNow<rowMax){
			// 从上到下,此时 colNow 必须为 colMax,rowNow 的活动范围是 [rowMin,rowMax)
            result[rowNow][colNow]=i;
            rowNow++;
        }else if(rowMax == rowNow && colMin<colNow && colNow<=colMax){
			// 从右到左,此时 rowNow 必须为 rowMax,colNow 的活动范围是 (colMin,colMax],而且是从colMax到colMin
            result[rowNow][colNow]=i;
            colNow--;
        }else if(colMin == colNow && rowMin<rowNow && rowNow<=rowMax){
			// 从下到上,此时 colNow 必须为 colMin,rowNow 的活动范围是 (rowMin,rowMax],而且是从rowMax到rowMin
            result[rowNow][colNow]=i;
            rowNow--;
            // 这条路有点不一样的是,需要检查是否回到原点
            if(rowNow==rowMin && colNow==colMin ){
                // 修改边界条件
                rowMin++;
                rowMax--;
                colMin++;
                colMax--;
                // 修改起点
                rowNow++;
                colNow++;
            }
        }
    }
    return result;
}

还可以对上面的代码进行大量的简化,直接省略掉 colNow 和 rowNow,直接用 rowMin rowMax colMin colMax 这四个边界条件即可,是这个题代码最精简的答案。

/**  
 * 通过四个变量存放转弯的时候的边界,让模拟变得简单,思路清晰,严丝合缝  
 */  
public int[][] generateMatrix1(int n) {  
    int l = 0;  
    int r = n - 1;  
    int t = 0;  
    int b = n - 1;  
    int[][] mat = new int[n][n];  
    int num = 1;  
    int tar = n * n;  
    while (num <= tar) {  
        // left to right  
        for (int i = l; i <= r; i++) {  
            //固定一个维度,另一个维度用 i 确定,然后设置值  
            mat[t][i] = num++;  
        }  
        //这个方法秒就妙在,通过 t++的操作,把下一个方向的起点也设置好了,这就是为什么给人以一种严丝合缝的感觉  
        // 其实,这个方法本质上,还是模拟法,而不是分层法  
        t++;  
        // top to bottom.  
        for (int i = t; i <= b; i++) {  
            mat[i][r] = num++;  
        }  
        r--;  
        // right to left.  
        for (int i = r; i >= l; i--) {  
            mat[b][i] = num++;  
        }  
        b--;  
        // bottom to top.  
        for (int i = b; i >= t; i--) {  
            mat[i][l] = num++;  
        }  
        l++;  
    }  
    return mat;  
}

其他的答案感觉都很难想得到,比如
用一个变量来存填充的方向:设置 dir 为方向数组、dirIndex 为 dir 的下标,通过 dirIndex 来控制填充方向的转换(抽象做的好,值得学习

/**  
 * 关键是,要想到,我们需要一个变量来存填充的方向  
 * 设置 dir 为方向数组 dirIndex 为dir的下标,通过dirIndex来控制填充方向的转换,  
 * 抽象做的好,值得学习。  
 *  
 * 这也是最容易想到的解法  
 *  
 * @param n  
 * @return  
 */  
  
public int[][] generateMatrix0(int n) {  
    int[][] result = new int[n][n];  
    int x = 0;  
    int y = -1;  
    int ele = 1;  
    int[][] dir = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // 右下左上  
    int dirIndex = 0;  
    while (ele <= n * n) {  
        // 确定下一个方向  
        int nextX = x + dir[dirIndex][0];  
        int nextY = y + dir[dirIndex][1];  
        if (nextX < 0 || nextX >= n || nextY < 0 || nextY >= n || result[nextX][nextY] != 0) {  
            dirIndex = (dirIndex + 1) % 4; // 顺时针旋转至下一个方向  
        }  
        //确定了方向,求下一个坐标  
        x = x + dir[dirIndex][0];  
        y = y + dir[dirIndex][1];  
        result[x][y] = ele;  
        ele++;  
    }  
    return result;  
}

相关题

54. 螺旋矩阵