目录
关于这一类矩阵题都可以通过模拟的方法进行解答,只是代码复杂的高低不同
首先是与Leetcode官方的题解一样的方法
层次遍历法
层次遍历法最重要的点有三个地方
1. 缩圈法进行层次遍历
整体的解题思路,从外围向内围进行顺时针遍历,外围遍历完就进行缩圈,对内层进行遍历,从而达成螺旋顺时针的遍历顺序
2. 循环的边界
第二个重要的是循环边界,关于遍历行列时的取值,如下中的while循环语句
首先要说的是这些while循环语句可以改成for循环,这样就可以在循环声明语句中进行创建变量和赋初值
其次是为什么是p <=right而不是p<right,很容易想到的一个点,就是如果left == right,那么这个while就不会被执行,因此需要结结实实的循环到right上,才能保证在双方左右或者上下边界重合时,正确的进行遍历对应的行列。
也正因如此,才需要在下一次初值时使用 p = upper + 1来避免重复遍历已遍历过的点
//以上层和右层遍历为例子
int p = left;
while(p <= right)
res.push_back(matrix[upper][p++]); //遍历上层
p = upper + 1;
while(p <= bottom)
res.push_back(matrix[p++][right]); //遍历右层
3. 上右遍历后是否要进行下左遍历
第三个重要的是在上右两次遍历后,在开启下左遍历前,要先经过判断在遍历
为什么?
通过if的条件很容易就可以看出来,这是为了保证只有在四个边界没有左右或者上下重合时才进行遍历。
原因也很简单,当出现重合时,如果继续遍历,就意味着将同一个序列,正着遍历一次(上右遍历)后,又反着遍历(下左)了一次,这是不正确的
vector<int> spiralOrder(vector<vector<int>>& matrix) {
//按层模拟,先模拟外层,再层层递推到下一层
vector<int> res;
int m = matrix.size(), n = matrix[0].size();
int upper = 0, bottom = m - 1, left = 0, right = n - 1;
while(upper <= bottom && left <= right)
{
int p = left;
while(p <= right)
res.push_back(matrix[upper][p++]); //遍历上层
p = upper + 1;
while(p <= bottom)
res.push_back(matrix[p++][right]); //遍历右层
if(left < right && upper < bottom)
{
p = right - 1;
while(p >= left)
res.push_back(matrix[bottom][p--]); //遍历下层
p = bottom - 1;
while(p > upper)
res.push_back(matrix[p--][left]); //遍历左层
}
left++;
right--;
upper++;
bottom--;
}
return res;
}
上面是官方的解法和我对中间一些细节的思考
之后还有一个是使用模拟法来进行解决,这个方法需要画图来进行充分的理解,也是我一开始想的解决办法,全网唯一,童叟无欺()
模拟法
模拟法就比较复杂了
模拟法主要源于我对遍历过程的规律发现
遍历的步数
设m为矩阵长,n为矩阵宽,则模拟法走的步长分别是
n-1 -> m-1 -> n-1 -> m-2 -> n-2 -> m-3......步,依次类推
可发现,排除第一次后,每走一轮(一行加一列),步数就会减一
因此使用一个 变量 z来记录这个减掉的值,使用step来记录步数
同时将初值赋值为 n - 1来特殊处理
遍历的方向
和步数不同,方向不存在首次特殊情况,方向在遍历完一个行列后(上层和右层),
会向反方向再遍历一个行列(下层和左层),整体方向呈现的规律是
正 -> 正 -> 负 -> 负 -> 正 -> 正 ->...
因此,直接使用一个dir来记录这个变量即可
基于以上规律事实,可以直接使用模拟法来进行模拟
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int> res;
int m = matrix.size(); //矩阵长
int n = matrix[0].size(); //矩阵宽
int dir = 1; //遍历方向(1为正向,-1为反向)
bool isM = false; //表示是否在列上进行遍历
int x = 0, y= 0, z = 0; //x, y表示在矩阵中的位置,z记录了当前遍历了几轮(一轮代表一次行列)
int step = n - 1; //本次遍历的剩余步数
res.push_back(matrix[0][0]); //先将第一个数放入(遍历时处理不到该元素)
while(res.size() < m * n ) //如果结果数组长度等于矩阵大小,说明遍历完成
{
if(step <= 0) //剩余步数小于等于0,说明该重置变量了
{
if(isM) //当前在列上进行遍历
{
step = n - z; //步数重置
dir *= -1; //方向取反
}
else //当前在行上进行遍历
{
z++; //轮次加1
step = m - z; //步数重置(比上次少一步)
}
isM = !isM; //更新当前在列上还是在行上
}
if(isM) //根据在列上还是在行上以及方向对坐标进行加减
x += dir;
else
y += dir;
res.push_back(matrix[x][y]); //推入结果数组
step--; //计步器减一
}
return res;
}