-
题目:
将一个矩阵的值顺时针打印到一个数组中;
第一个方向是👉; -
思路:
1.缩减边界法:O(mn):m和n分别是矩阵的长和宽,需要遍历完矩阵的每一个位置即m*n,O(1):只用到了几个常数变量;
上下左右四个边界决定一个矩阵大小;
按👉👇👈👆的顺序,每遍历完一个方向,缩减响应的边界;
每个方向都能作为最后一个结束遍历的方向,因此每个方向结束都要判断是否结束;
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if (!matrix.size() || !matrix[0].size()) return {};
vector<int> res;
int m = matrix.size();
int n = matrix[0].size();
int l = 0, r = n - 1;//左右边界
int up = 0, down = m - 1;//上下边界
while (1) { //可能从任意方向退出,因此每个方向结束后都应该判断
for (int i = l; i <= r; ++i) {// →
res.push_back(matrix[up][i]);
}
if (++up > down) break;
for (int i = up; i <= down; ++i) {//↓
res.push_back(matrix[i][r]);
}
if (l > --r) break;
for (int i = r; i >= l; --i) {//←
res.push_back(matrix[down][i]);
}
if (up > --down) break;
for (int i = down; i >= up; --i) {//↑
res.push_back(matrix[i][l]);
}
if (++l > r) break;
}
return res;
}
};
2.提前定义四个方向:O(mn):遍历矩阵的每个位置,O(mn):额外需要一个mn的矩阵visted标记走过的位置
虽然耗费空间大一些,但思路更清晰,且不容易错:① 结束条件由for循环条件 i < mn 控制 ②四个方向由数组控制,无需挨着手动描述四个方向,且更灵活,例如题目改成逆时针,则只需修改方向数组dx和dy即可,而方法1需要全部重新写;
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if (!matrix.size() || !matrix[0].size()) return {};
vector<int> res;
int m = matrix.size();
int n = matrix[0].size();
vector<vector<bool>> visted(m, vector<bool>(n, false));//用来标记走过的格子
int dx[4] = {0, 1, 0, -1}, dy[4] = {1, 0, -1, 0};//右下左上
int direction = 0; //初始方向:右
int x = 0, y = 0; //初始位置[0, 0]
for (int i = 0; i < m * n; ++i) { //共需遍历m*n个位置
res.push_back(matrix[x][y]);
visted[x][y] = true;//走过就标记
int newX = x + dx[direction], newY = y + dy[direction];//下一位置
if (newX < 0 || newX >= m || newY < 0 || newY >= n || visted[newX][newY]) {//若出界了或者走过了就换个方向
direction = (direction + 1) % 4;//四个方向循环使用
newX = x + dx[direction], newY = y + dy[direction];//换个方向一定能走,因为要把m*n个位置走完,既然能进入这层for循环,说明格子还没走完
}
x = newX, y = newY;//真正的下一位置;
}
return res;
}
};
- 总结:
两种方法各有优劣,要多多尝试使用方法2
resize和reserve的区别:resize调整last,而reserve调整end_of_storage;res[cnt] = 2这种赋值方式必须在[ first,last ]之内,push_back会默认从last后的下个位置插入一个对象并调整last;