一、求解方法
(1)按点模拟路径
在原有坐标的基准上,叠加 横纵坐标 的变化值,求出下一位置,并按题完成要求。但需注意转角的时机判断,特别是最后即将返回上一出发点的位置。
(2)按层模拟路径
在原有的形状基础上,画出 上下左右 四根边界线,按照顺序完成画圈。但需要关注单行或单列可能存在重复的情况,因此需在操作 下 和 左 边界线时加上判定条件。
(3)心路历程
最开始掌握的是 Carl 教的循环画框类的思路,但是它的应用是在方形样式,其中的 offset 和 中间元素处理对于方形图案比较好处理,但是针对矩形样式,我真的不知道要怎么改了。后改学其他思路,也就是上述两种。
二、推荐例题
(1)方形样式的螺旋矩阵
方法1:点模拟
class Solution {
public int[][] generateMatrix(int n) {
int curNum = 1;
int maxNum = n * n;
int curRow = 0;
int curCol = 0;
int dirIndex = 0;
int[][] dirs = {{0,1}, {1,0}, {0,-1}, {-1,0}};
int[][] res = new int[n][n];
while(curNum <= maxNum){
res[curRow][curCol] = curNum++;
//判定后续位置是否越界
int nextRow = curRow + dirs[dirIndex][0];
int nextCol = curCol + dirs[dirIndex][1];
//需要加一个res[nextRow][nextCol] != 0 因为走了一圈之后就回到了原点,需要避免回原点,而是调整方向
//比如三阶矩阵中的最后9,若不调整方向,则会放到原点处
if(nextRow < 0 || nextRow > n-1 || nextCol < 0 || nextCol > n-1 || res[nextRow][nextCol] != 0){
dirIndex = (dirIndex+1) % 4;
}
curRow = curRow + dirs[dirIndex][0];
curCol = curCol + dirs[dirIndex][1];
}
return res;
}
}
方法2:层模拟
class Solution {
public int[][] generateMatrix(int n) {
int left = 0;
int right = n-1;
int top = 0;
int bottom = n-1;
int num = 1;
int[][] res = new int[n][n];
while(left <= right && top <= bottom){
//上:从左往右-----j 从 left ---> right
for(int j = left; j <= right; j++){
res[top][j] = num++;
}
top++;
//右:从上到下-----i 从 top ---> bottom
for(int i = top; i <= bottom; i++){
res[i][right] = num++;
}
right--;
if(top <= bottom){
//下:从右到左-----j 从 right ---> left
for(int j = right; j >= left; j--){
res[bottom][j] = num++;
}
bottom--;
}
if(left <= right){
//左:从下到上-----i 从 bottom ---> top
for(int i = bottom; i >= top; i--){
res[i][left] = num++;
}
left++;
}
}
return res;
}
}
方法3:“循环画框”
class Solution {
public int[][] generateMatrix(int n) {
int loopCur = 1; //当前所处的圈数
int loopCnt = n / 2; //所需绘制的完整圈数
int xStart = 0; //横坐标的开始坐标
int yStart = 0; //纵坐标的开始坐标
int offset = 1; //当前绘制圈的偏置值
int num = 1; //当前绘制的数字
int i,j; //绘制地图用的横纵坐标
int[][] res = new int[n][n];
while(loopCur <= loopCnt){
//上:从左往右-----j 从 yStart ---> n-offset
for(j = yStart; j < n-offset; j++){
res[xStart][j] = num++;
}
//右:从上到下-----i 从 xStart ---> n-offset
for(i = xStart; i < n-offset; i++){
res[i][j] = num++;
}
//下:从右到左-----j 递减到 yStart+1
for(; j > yStart; j--){
res[i][j] = num++;
}
//左:从下到上-----i 递减到 xStart+1
for(; i > xStart; i--){
res[i][j] = num++;
}
offset++; //调整偏置值
loopCur++;
xStart++;
yStart++;
}
if(1 == n%2){
res[xStart][yStart] = num;
}
return res;
}
}
(2)矩形样式的螺旋矩阵
方法1:点模拟
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> res = new ArrayList<Integer>();
if(matrix == null){
return res;
}
int rows = matrix.length;
int cols = matrix[0].length;
int cnt = rows * cols;
if (rows == 0 || cols == 0) {
return res;
}
int curRow = 0;
int curCol = 0;
int dirIndex = 0;
int[][] dirs = {{0,1}, {1,0}, {0,-1}, {-1,0}};
boolean[][] visited = new boolean[rows][cols];
while(cnt > 0){
res.add(matrix[curRow][curCol]);
visited[curRow][curCol] = true;
//判定后续位置是否越界
int nextRow = curRow + dirs[dirIndex][0];
int nextCol = curCol + dirs[dirIndex][1];
//需要加一个res[nextRow][nextCol] != 0 因为走了一圈之后就回到了原点,需要避免回原点,而是调整方向
//比如三阶矩阵中的最后9,若不调整方向,则会放到原点处
if(nextRow < 0 || nextRow > rows-1 || nextCol < 0 || nextCol > cols-1 || visited[nextRow][nextCol] != false){
dirIndex = (dirIndex+1) % 4;
}
curRow = curRow + dirs[dirIndex][0];
curCol = curCol + dirs[dirIndex][1];
cnt--;
}
return res;
}
}
方法2:层模拟
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> res = new ArrayList<Integer>();
if(matrix == null){
return res;
}
int rows = matrix.length;
int cols = matrix[0].length;
if (rows == 0 || cols == 0) {
return res;
}
int top = 0;
int bottom = rows -1;
int left = 0;
int right = cols -1;
while(top <= bottom && left <= right){
//上:从左到右
for(int j = left; j <= right; j++){
res.add(matrix[top][j]);
}
top++;
//右:从上到下
for(int i = top; i <= bottom; i++){
res.add(matrix[i][right]);
}
right--;
//下:从右到左
if(top <= bottom){ //若不判断,遇见单行,则会重复处理
for(int j = right; j >= left;j--){
res.add(matrix[bottom][j]);
}
}
bottom--;
//左:从下到上
if(left <= right){ //若不判断,遇见单列,则会重复处理
for(int i = bottom; i >= top; i--){
res.add(matrix[i][left]);
}
}
left++;
}
return res;
}
}
方法3: “循环画框”--错误代码演示,没改出来
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> res = new ArrayList<Integer>();
int rows = matrix.length;
int cols = matrix[0].length;
int loopCur = 1;
int loopCnt = rows/2 + 1; //为便捷处理中心区域的多个数据
int xStart = 0;
int yStart = 0;
int offset = 1;
//因为增加了圈数,导致单行的矩阵会重复打印
if(rows == 1){
for(int temp = 0; temp < cols; temp++){
res.add(Integer.valueOf(matrix[0][temp]));
}
return res;
}
//因为增加了圈数,导致单列的矩阵会重复打印
if(cols == 1){
for(int temp = 0; temp < rows; temp++){
res.add(Integer.valueOf(matrix[temp][0]));
}
return res;
}
int i,j;
while(loopCur <= loopCnt){
//上:从左到右
for(j = yStart; j < cols-offset; j++){
res.add(matrix[xStart][j]);
}
//右:从上到下
for(i = xStart; i < rows-offset; i++){
res.add(matrix[i][j]);
}
//下:从右到左
for(; j > yStart; j--){
res.add(matrix[i][j]);
}
//左:从下到上
for(; i > xStart; i--){
res.add(matrix[i][j]);
}
offset++;
loopCur++;
xStart++;
yStart++;
}
if(rows == cols && rows%2 ==1){
res.add(matrix[xStart-1][yStart-1]);
}
return res;
}
}