螺旋矩阵
螺旋矩阵Ⅱ
代码随想录的解答非常清晰,这里贴上:
相信很多同学刚开始做这种题目的时候,上来就是一波判断猛如虎。
结果运行的时候各种问题,然后开始各种修修补补,最后发现改了这里那里有问题,改了那里这里又跑不起来了。
求解本题一定要坚持循环不变量原则。
模拟顺时针画矩阵的过程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
由外向内一圈一圈这么画下去。
一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。
附上源地址:https://programmercarl.com/0059.%E8%9E%BA%E6%97%8B%E7%9F%A9%E9%98%B5II.html#%E6%80%9D%E8%B7%AF
代码:
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> Matrix(n,vector<int>(n,0)); //矩阵初始化0
int startx = 0,starty = 0; //每一圈的起始位置
int loop = n/2; //螺旋的圈数
int offset = 1; //每次螺旋的长度缩短量
int count = 1; //给矩阵赋值
int mid = n/2; //若n为奇数,需要单独给矩阵的中心点赋值
int i,j;
while(loop--) { //一个螺旋圈要进行四次循环,分别是从左到右,从上到下,从右到左,从下到上
i = startx;
j = starty;
//每一条边的赋值保持左闭右开
for(; j < n - offset; j++) { //从左到右
Matrix[i][j] = count++;
}
for(; i < n - offset ; i++) { //从上到下
Matrix[i][j] = count++;
}
for(; j > starty; j--) { //从右到左
Matrix[i][j] = count++;
}
for(; i > startx; i--) { //从下到上
Matrix[i][j] = count++;
}
startx++;
starty++;
offset += 1;
}
if(n % 2) {
Matrix[mid][mid] = count;
}
return Matrix;
}
};
相关题目:螺旋矩阵
这道题我想用同样的思路解答,而因为这道题行数不等于列数,我就修改从左到右和从上到下的循环截止条件,即:
int m = matrix.size(); //矩阵的行数
int n = matrix[0].size(); //矩阵的列数
for(; j < n - offset; j++) { //从左到右
seq.push_back(matrix[i][j]);
}
for(; i < m - offset ; i++) { //从上到下
seq.push_back(matrix[i][j]);
}
并且,当行数为奇数时,会剩下最中间一行没有读取,所以加上以下代码:
if(m % 2) { //当行数为奇数时,需要单独读取矩阵mid的行数
for(j = starty; j < n - offset ; j++) {
seq.push_back(matrix[mid][j]);
}
}
但是,遇到了如下问题:
1、当行数为奇数时,最后读取最中间一行的代码存在问题,不能读取完所有的矩阵元素。
在试图通过下列代码解决1问题后:
//当行数等于列数时,因为左闭右开的缘故,会漏下螺旋的最后一个元素
if(m == n) {
seq.push_back(matrix[mid][mid]);
}
2、又发现当行数为1或列数为1时,会发生重复读取的问题。
那单独对这两种情况进行处理:
if(m == 1 || n == 1) {
for(i = 0; i < m; i++)
for( j = 0; j < n; j++) {
seq.push_back(matrix[i][j]);
}
}
}
3、测试后,发现1“当行数为奇数时,最后读取最中间一行的代码存在问题,不能读取完所有的矩阵元素”的描述有误。
因为每一次螺旋,并不能保证从完整经历完从左到右,从上到下,从右到左,从下到上四个步骤,这是与螺旋矩阵Ⅱ不同的地方。
如果仍然用之前的代码,会出现很多错误。
修修补补几次后,我要放弃这种算法!
学习了leetcode-YouLookDeliciousC的题解思想:
定义四个边界,上下左右,每从左到右,从上到下,从右到左,从下到上过一次,就修改边界,当边界交错,即螺旋完成。
代码:
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int> seq; //返回的矩阵序列
int m = matrix.size(); //矩阵的行数
int n = matrix[0].size(); //矩阵的列数
int u = 0, d = m -1, l = 0, r =n - 1; //定义矩阵的上下左右
int i,j;
while(true) {
for(i = u, j = l; j <= r; j++) { //从左到右
seq.push_back(matrix[i][j]);
}
if(++u > d) break; //修改上边界,若上边界大于下边界,则遍历完成
for(i = u; i <= d ; i++) { //从上到下
seq.push_back(matrix[i][r]);
}
if(--r < l) break; //修改右边界
for(j = r; j >=l ; j--) { //从右到左
seq.push_back(matrix[d][j]);
}
if(--d < u) break; //修改下边界
for(i = d; i >= u; i--) { //从下到上
seq.push_back(matrix[i][l]);
}
if(++l > r) break; //修改左边界
}
return seq;
}
};
这个代码看着太清爽啦!
原题解:C++ 详细题解
后续
再思考了一下后突然发现,螺旋次数loop并不能用行数m除以2,而是min(m,n)/2(n是列数),这样,当min(m,n)为奇数时,会留下一行或一列没有读取,最后单独对这一行或列单独操作也可。
然后通过啦!
代码:
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector<int> seq; //返回的矩阵序列
int m = matrix.size(); //矩阵的行数
int n = matrix[0].size(); //矩阵的列数
int startx = 0,starty = 0; //每一圈的起始位置
int loop = min(m,n)/2; //螺旋的圈数
int offset = 1; //每次螺旋的长度缩短量
int i,j;
while(loop--) { //一个螺旋圈要进行四次循环,分别是从左到右,从上到下,从右到左,从下到上
i = startx;
j = starty;
//每一条边的赋值保持左闭右开
for(; j < n - offset; j++) { //从左到右
seq.push_back(matrix[i][j]);
// vector<int>::iterator it = seq.end();
// seq.insert(it,matrix[i][j]);
}
for(; i < m - offset ; i++) { //从上到下
seq.push_back(matrix[i][j]);
// vector<int>::iterator it = seq.end();
// seq.insert(it,matrix[i][j]);
}
for(; j > starty; j--) { //从右到左
seq.push_back(matrix[i][j]);
// vector<int>::iterator it = seq.end();
// seq.insert(it,matrix[i][j]);
}
for(; i > startx; i--) { //从下到上
seq.push_back(matrix[i][j]);
// vector<int>::iterator it = seq.end();
// seq.insert(it,matrix[i][j]);
}
startx++;
starty++;
offset += 1;
}
//当行数为奇数时,会剩下一行或以列没有读取完
if(min(m,n) % 2) {
for (i = startx, j = starty; i <= m - offset ; i++) {
for (j = starty; j <= n - offset ; j++) {
seq.push_back(matrix[i][j]);
}
}
}
return seq;
}
};