题目来源
题目描述
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& mat) {
}
};
题目解析
- 一般来讲,我们都是横着遍历的
- 每次遍历的起始坐标:
[i, 0]
,每次遍历的结束坐标[i, vec[i].size() - 1]
- 每次遍历时,只往一个方向移动,越界的时候才重置起点,并开始下一次遍历
- 每次遍历时,坐标怎么变化?
- 每移动一次,都只需要改变纵坐标。也就是假设当前坐标为
[i, j]
,那么下一个坐标为[i, j + 1]
- 当越界时,需要改变横纵坐标
- 什么时候改变?当
j == vec[0].size()
时,此时j越界,如果j
再次加1,那么就会异常,所以需要重置j为0。为了避免重复,i需要加1 - 即: 假设当前坐标为
[i, j]
,那么下一个坐标为[i + 1, 0]
- 什么时候改变?当
- 每移动一次,都只需要改变纵坐标。也就是假设当前坐标为
- 每次遍历的起始坐标:
- 但是这里要求对角线遍历,问题是:
- 每次遍历时,可能有两种移动方向:左上移动,右下移动
- 每次移动时,坐标是怎么变化的?
- 每移动一次,横纵坐标都需要变换
- 向右上移动的话坐标要加上
[-1, 1]
,向左下移动的话坐标要加上[1, -1]
- 怎么确定移动的方向,怎么知道什么时候需要改变方向?(即什么时候往左上移动,什么时候要往右上移动。)
- 分析例子1,可以看出移动方向为左上、 右下、左上、右下…,每次越界时,就改变方向,因此
- 可以用一个dir标记变换方向,默认为true,
- 当为true时,左上;当为false时,右下
- 每次越界时,就改变dir的值
- 什么时候越界呢?
- 当横纵坐标超过
i
或者j
的边界时,就越界。 - 横坐标
i
的边界有0, vec.size() - 1
- 纵坐标
j
的边界有0, vec[0].size() - 1
- 当横纵坐标超过
- 当越界时,坐标需要怎么改变?因为往左上和右下两个方向都有可能越界
- (下一次)越界有这么几种情况:
- 横坐标
i
越界,而纵坐标j
不越界。那么i不变,但是改变j,同时改变方向 - 横坐标
i
不越界,而纵坐标j
越界。那么j不变,但是改变i,同时改变方向 - 横坐标
i
越界,纵坐标j
也越界,此时需要改变横纵坐标i
和j
- 横坐标
- 向右上移动的话
[i,j]
坐标要加上[-1, 1]
,而初始时j
一定在起点,即[i, 0]
,那么越界情况- 横坐标
i
越界,而纵坐标j
不越界:即i == 0
,但是j
不为vec[0].size() - 1
,那么i不变,但是j加1,同时改变方向,即使下一个位置为[0, j + 1]
- 横坐标
i
越界,纵坐标j
也越界,即到了右上角[0, vec[0].size() - 1]
,即i == 0,j == vec[0].size() - 1
时,下一个位置为[1, vec[0].size() - 1]
- 横坐标
- 往左下方向移动的话,
[i,j]
坐标要加上[1, -1]
,而初始时i
一定在起点,即[0, j]
,那么越界情况- 横坐标
i
不越界,而纵坐标j
越界: - 横坐标
i
越界,纵坐标j
也越界,即移动到了左下角,也就是[vec.size() - 1, 0]
时,即i == vec.size() - 1,j == 0
时,下一个位置为[vec.size() - 1, 1]
- 横坐标
- (下一次)越界有这么几种情况:
- 遍历时的起始坐标? 遍历的结束坐标?
- 初始坐标为
[0, 0]
,dir为true,表示左上方向,每次移动前,先判断自己是不是到了边界- 如果到了边界,就根据上面的分析改变起始坐标并重置dir;
- 如果不是边界,那么根据dir加上
[-1, 1]
或者[1, -1]
- 初始坐标为
总结:这道题的难点在于怎么处理越界的情况
- 怎么遍历呢?用循环,而且每次都要计算出当前循环需要访问的位置
- 怎么确定遍历应该结束呢?一共有m*n个元素,如果全部访问过了,就退出
- 怎么计算需要访问的位置呢?
- 初始坐标为[0,0]。
- 由于移动的方向不再是水平或竖直方向,而是对角线方向,那么每移动一次,横纵坐标都要变化,向右上移动的话要坐标加上[-1, 1],向左下移动的话要坐标加上[1, -1]
- 怎么处理越界情况呢?向右上和左下两个对角线方向遍历的时候都会有越界的可能,但是除了左下角和右上角的位置越界需要改变两个坐标之外,其余的越界只需要改变一个
- 因此,我们需要先判断要同时改变两个坐标的情况,即在左上角和右下角的位置
- 如果在右上角的位置还要往右上走时,那么要移动到它下面的位置,也就是说如果col超过了n-1的范围,那么col就要重置为n-1,并且row自增2,然后改变遍历的方向
- 同理,如果row超过了m-1的范围,那么row重置为m-1,并且col自增2,然后改变遍历的方向。
- 我们再来处理一般越界的情况
- 如果row小于0,那么row重置0,然后改变遍历的方向。
- 同理如果col小于0,那么col重置0,然后改变遍历的方向。
- 因此,我们需要先判断要同时改变两个坐标的情况,即在左上角和右下角的位置
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& mat) {
if(mat.empty() || mat[0].empty()){
return {};
}
int m = mat.size(), n = mat[0].size(), r = 0, c = 0, k = 0;
vector<int> res(m * n);
vector<vector<int>> dirs{{-1,1}, {1,-1}};
for (int i = 0; i < m * n; ++i) {
res[i] = mat[r][c];
r += dirs[k][0];
c += dirs[k][1];
if (r >= m) {
r = m - 1; c += 2; k = 1 - k;
}
if (c >= n) {
c = n - 1; r += 2; k = 1 - k;
}
if (r < 0) {
r = 0; k = 1 - k;
}
if (c < 0) {
c = 0; k = 1 - k;
}
}
return res;
}
};
下面这种方法跟上面的方法思路相同,不过写法有些不同,这里根据横纵左边之和的奇偶性来判断遍历的方向,然后对于越界情况再单独处理即可
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& mat) {
if(mat.empty() || mat[0].empty()){
return {};
}
int m = mat.size(), n = mat[0].size(), r = 0, c = 0;
vector<int> res(m * n);
for (int i = 0; i < m * n; ++i) {
res[i] = mat[r][c];
if((r + c) % 2 == 0){
if(c == n - 1){
++r;
}else if(r == 0){
++c;
}else{
--r;
++c;
}
}else{
if(r == m - 1){
++c;
}else if(c == 0){
++r;
}else{
++r;
--c;
}
}
}
return res;
}
};