leetcode-master/problems/0054.螺旋矩阵.md

177 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="https://code-thinking-1253855093.file.myqcloud.com/pics/20210924105952.png" width="1000"/>
</a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
## 54.螺旋矩阵
[力扣题目链接](https://leetcode-cn.com/problems/spiral-matrix/)
给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
示例1:
输入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
输出:[1,2,3,6,9,8,7,4,5]
## 思路
本题解决思路继承自[59.螺旋矩阵II](https://www.programmercarl.com/0059.%E8%9E%BA%E6%97%8B%E7%9F%A9%E9%98%B5II.html)建议看完59.螺旋矩阵II之后再看本题
与59.螺旋矩阵II相同的是两者都是模拟矩形的顺时针旋转所以核心依然是依然是坚持循环不变量按照左闭右开的原则
模拟顺时针画矩阵的过程:
* 填充上行从左到右
* 填充右列从上到下
* 填充下行从右到左
* 填充左列从下到上
由外向内一圈一圈这么画下去,如下所示:
![螺旋矩阵](https://img-blog.csdnimg.cn/2020121623550681.png)
这里每一种颜色,代表一条边,我们遍历的长度,可以看出每一个拐角处的处理规则,拐角处让给新的一条边来继续画。
与59.螺旋矩阵II不同的是前题中的螺旋矩阵是正方形只有正方形的边长n一个边界条件而本题中需要考虑长方形的长和宽(m行和n列)两个边界条件。自然m可以等于n即前题可视为本题在m==n的特殊情况。
我们从最一般的情况开始考虑与59.螺旋矩阵II题解对比起来m和n的带入主要引来两方面的差异
* loop的计算
本题的loop计算与59.螺旋矩阵II算法略微差异因为存在rows和columns两个维度可自行分析loop只能取min(rows, columns)例如rows = 5, columns = 7那loop = 5 / 7 = 2
* mid的计算及填充
1、同样的原理本题的mid计算也存在上述差异
2、
如果min(rows, columns)为偶数,则不需要在最后单独考虑矩阵最中间位置的赋值
如果min(rows, columns)为奇数,则矩阵最中间位置不只是[mid][mid],而是会留下来一个特殊的中间行或者中间列具体是中间行还是中间列要看rows和columns的大小如果rows > columns,则是中间列,相反,则是中间行
代码如下已经详细注释了每一步的目的可以看出while循环里判断的情况是很多的代码里处理的原则也是统一的左闭右开。
整体C++代码如下:
```CPP
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if (matrix.size() == 0 || matrix[0].size() == 0)
return {};
int rows = matrix.size(), columns = matrix[0].size();
int total = rows * columns;
vector<int> res(total); // 使用vector定义一个一维数组存放结果
int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
int loop = min(rows, columns) / 2;
// 本题的loop计算与59.螺旋矩阵II算法略微差异因为存在rows和columns两个维度可自行分析loop只能取min(rows, columns)例如rows = 5, columns = 7那loop = 5 / 7 = 2
int mid = min(rows, columns) / 2;
// 1、同样的原理本题的mid计算也存在上述差异
// 2、
//如果min(rows, columns)为偶数,则不需要在最后单独考虑矩阵最中间位置的赋值
//如果min(rows, columns)为奇数,则矩阵最中间位置不只是[mid][mid],而是会留下来一个特殊的中间行或者中间列具体是中间行还是中间列要看rows和columns的大小如果rows > columns,则是中间列,相反,则是中间行
//相信这一点不好理解,建议自行画图理解
int count = 0;// 用来给矩阵中每一个空格赋值
int offset = 1;// 每一圈循环,需要控制每一条边遍历的长度
int i,j;
while (loop --) {
i = startx;
j = starty;
// 下面开始的四个for就是模拟转了一圈
// 模拟填充上行从左到右(左闭右开)
for (j = starty; j < starty + columns - offset; j++) {
res[count++] = matrix[startx][j];
}
// 模拟填充右列从上到下(左闭右开)
for (i = startx; i < startx + rows - offset; i++) {
res[count++] = matrix[i][j];
}
// 模拟填充下行从右到左(左闭右开)
for (; j > starty; j--) {
res[count++] = matrix[i][j];
}
// 模拟填充左列从下到上(左闭右开)
for (; i > startx; i--) {
res[count++] = matrix[i][starty];
}
// 第二圈开始的时候起始位置要各自加1 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
startx++;
starty++;
// offset 控制每一圈里每一条边遍历的长度
offset += 2;
}
// 如果min(rows, columns)为奇数的话,需要单独给矩阵最中间的位置赋值
if (min(rows, columns) % 2) {
if(rows > columns){
for (int i = mid; i < mid + rows - columns + 1; ++i) {
res[count++] = matrix[i][mid];
}
} else {
for (int i = mid; i < mid + columns - rows + 1; ++i) {
res[count++] = matrix[mid][i];
}
}
}
return res;
}
};
```
## 类似题目
* [59.螺旋矩阵II](https://leetcode-cn.com/problems/spiral-matrix-ii/)
* [剑指Offer 29.顺时针打印矩阵](https://leetcode-cn.com/problems/shun-shi-zhen-da-yin-ju-zhen-lcof/)
## 其他语言版本
Python:
```python
class Solution:
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
m, n = len(matrix), len(matrix[0])
left, right, up, down = 0, n - 1, 0, m - 1 # 定位四个方向的边界,闭区间
res = []
while True:
for i in range(left, right + 1): # 上边,从左到右
res.append(matrix[up][i])
up += 1 # 上边界下移
if len(res) >= m * n: # 判断是否已经遍历完
break
for i in range(up, down + 1): # 右边,从上到下
res.append(matrix[i][right])
right -= 1 # 右边界左移
if len(res) >= m * n:
break
for i in range(right, left - 1, -1): # 下边,从右到左
res.append(matrix[down][i])
down -= 1 # 下边界上移
if len(res) >= m * n:
break
for i in range(down, up - 1, -1): # 左边,从下到上
res.append(matrix[i][left])
left += 1 # 左边界右移
if len(res) >= m * n:
break
return res
```
-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>