leetcode:498.对角线遍历

题目来源

题目描述

在这里插入图片描述

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也越界,此时需要改变横纵坐标ij
      • 向右上移动的话[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;
    }
};

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
给定一个整数数组 nums 和一个目标值 target,要求在数组中找出两个数的和等于目标值,并返回这两个数的索引。 思路1:暴力法 最简单的思路是使用两层循环遍历数组的所有组合,判断两个数的和是否等于目标值。如果等于目标值,则返回这两个数的索引。 此方法的时间复杂度为O(n^2),空间复杂度为O(1)。 思路2:哈希表 为了优化时间复杂度,可以使用哈希表来存储数组中的元素和对应的索引。遍历数组,对于每个元素nums[i],我们可以通过计算target - nums[i]的值,查找哈希表中是否存在这个差值。 如果存在,则说明找到了两个数的和等于目标值,返回它们的索引。如果不存在,将当前元素nums[i]和它的索引存入哈希表中。 此方法的时间复杂度为O(n),空间复杂度为O(n)。 思路3:双指针 如果数组已经排序,可以使用双指针的方法来求解。假设数组从小到大排序,定义左指针left指向数组的第一个元素,右指针right指向数组的最后一个元素。 如果当前两个指针指向的数的和等于目标值,则返回它们的索引。如果和小于目标值,则将左指针右移一位,使得和增大;如果和大于目标值,则将右指针左移一位,使得和减小。 继续移动指针,直到找到两个数的和等于目标值或者左指针超过了右指针。 此方法的时间复杂度为O(nlogn),空间复杂度为O(1)。 以上三种方法都可以解决问题,选择合适的方法取决于具体的应用场景和要求。如果数组规模较小并且不需要考虑额外的空间使用,则暴力法是最简单的方法。如果数组较大或者需要优化时间复杂度,则哈希表或双指针方法更合适。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值