二维数组2:螺旋矩阵两道题

二维数组的特性决定了它可以有一种非常有意思的题:可以顺时针或者逆时针转圈访问数组,可以从外围到中心,也可以从中心到外围(该类型还没见过)。这种题在leetcode中还不止一道,其难点是如何准确判定打印时上下左右四个角的位置,只要位置定了,每条边的访问就是一个简单的线性遍历了。我们就来看一下具体怎么处理吧。

一.leetcode54 顺时针打印二维数组

给定一个m x n大小的矩阵,按螺旋的顺序返回矩阵中的所有元素。

这个题思路很简单,就是转圈打印,但是写正确并且能执行却很难。如果你能一把写对,面试官一定会喜欢你。这里的主要问题是一堆边界条件的判断。为了方便处理,对于上下左右四个位置,我们添加四个标志点,这样正式循环的时候只考虑四个标志位之间的问题就行了。否则写几行循环自己都无法确定循环到哪里结束,想死的冲动都有:

int top=0,bottom=matrix.length-1;int left=0,right=matrix[0].length-1;

那么接下来,就是调整这四个参数,调整一次,打印一圈,直到最终结束。于是我们就很容易构造出了代码框架:

public ArrayList<Integer> spiralOrder(int[][] matrix) {
       ArrayList<Integer> res=new ArrayList<>();
       if(matrix.length==0){
           return res;
      }
       //定义四个坐标,表示每次循环的四个位置
    int top=0,bottom=matrix.length-1;
    int left=0,right=matrix[0].length-1;

    while(??){//①
        for(int i=??;i??;i++){//②
            res.add(matrix[top][i]);
        }
       
        for(int i=??;i??;i++){//③
            res.add(matrix[i][right]);
        }
       
        for(int i= ??;i??;i--){//④
            res.add(matrix[bottom][i]);
        }
        for(int i=??;i??;i--){//⑤
            res.add(matrix[i][left]);
        }
        top++;
        bottom--;
        left++;
        right--;
       
    }  
       return res;
  }

好了,代码的框架就是这样子,接下来将上面五个标记序号的位置填上对应的条件,这里一共有9处,有的还需要使用&&,只要你一个写错,执行就会挂掉,请问你该怎么办?

我们逐步分析:

1.什么时候循环该结束

如果想不通,我们可以画个简单的图:

在这里,很明显看到,每循环一圈,两侧都会减少一行,所以每个变量走到中间位置就行了。那中间应该是什么呢?先看列的,这里应该是要到第二行4的位置,因为(matrix.length+1)/2=2,所以应该有left<2 。那列还需要考虑吗?肯定的,因为要保证行能到第二列,因为(matrix[0].length+1)/2=1,所以此时应该有right<(matrix[0].length+1)/2.不放心,再画两种情况看一下:

如果还不放心,可以再画一个4x4的。最后:应该将两个并还是交呢?这里如果是并的话,如果left要停止了,但是right还能继续循环,那么此时就不应该循环了,所以肯定是与。如果不放心,再画一个差异大点的矩阵,例如5X8,会发现一端结束的时候,另外一端要么只剩一行,此时循环里的一路遍历就打印出来了,要么就没有了,所以此时应该将其与起来。

所以我们在①处的条件为top<(matrix.length+1)/2 &&left<(matrix[0].length+1)/2.

1.2 ②的条件是什么

此时肯定从i=left开始循环的,问题不大。那结束条件呢?应该是遇到right的时候,那该小于还是小于相等呢?还是画图来看

这里很明显left=0,right=2,循环的时候应该是要能访问3的,所以条件就是i<=right。

如果不放心,就再画个其他图看看。

1.3 ③的条件是什么

从上面图里可以看到,是要同3之后的6开始循环到9.所以我们可以放心的写起止条件为:i=top+1,结束条件似乎i<=bottom,其他推论一样。

1.4 ④的条件是什么

很明显,这里是从8到7的,所以开始条件好判断:i=right-1,那结束条件呢?7的时候i=left,所以有i>=left,那这就结束了吗?这里还要top<bottom,top一直在++,bottom一直在–,不过不保证top<bottom,就可能继续循环打印了。灵魂之问又来了,这里top能不能等于bottm?答案是不能,为什么呢?还是以3X3为例,经过上面的分析,可以简化为:while(top<2&&right<2){for(){}。。。。}可以看到while是会循环0 和1 两次的,如果上面是等于的话,top==bottom的时候就会多循环一次,因此不行。⑤的位置和上面类似,所以我们最终的代码是:

public ArrayList<Integer> spiralOrder(int[][] matrix) {
      ArrayList<Integer> res=new ArrayList<>();
      if(matrix.length==0){
          return res;
      }
      //定义四个坐标,表示每次循环的四个位置
    int top=0,bottom=matrix.length-1;
    int left=0,right=matrix[0].length-1;
    while(top<(matrix.length+1)/2 &&left<(matrix[0].length+1)/2){
        for(int i=left;i<=right;i++){
            res.add(matrix[top][i]);
        }
       
        for(int i=top+1;i<=bottom;i++){
            res.add(matrix[i][right]);
        }
       
        for(int i= right-1;i>=left&& top!=bottom;i--){
            res.add(matrix[bottom][i]);
        }
        for(int i=bottom-1;i>=(top+1)&&left!=right;i--){
            res.add(matrix[i][left]);
        }
        top++;
        bottom--;
        left++;
        right--;
       
    }
       
      return res;
       
       
  }

二.59. 螺旋矩阵 II

 给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

示例1:输入:n = 3输出:[[1,2,3],[8,9,4],[7,6,5]]示例2:输入:n = 1输出:[[1]]

这个题有多种思路,我自己实现的也有些复杂,我找到的最简洁的实现是这样的:

生成一个 n×n 空矩阵 mat,随后模拟整个向内环绕的填入过程:

  (1)定义当前左右上下边界 l,r,t,b,初始值 num = 1,迭代终止值 tar = n * n;

  (2)当 num <= tar 时,始终按照 从左到右 从上到下 从右到左 从下到上 填入顺序循环,每次填入后: 

      执行 num += 1:得到下一个需要填入的数字; 

      更新边界:例如从左到右填完后,上边界 t += 1,相当于上边界向内缩 1。

(3)使用num <= tar而不是l < r || t < b作为迭代条件,是为了解决当n为奇数时,矩阵中心数字无法在迭代过程中被填充的问题。

最终返回 mat 即可。

图示是这样的:

代码:

class Solution {    public int[][] generateMatrix(int n) {        int l = 0, r = n - 1, t = 0, b = n - 1;        int[][] mat = new int[n][n];        int num = 1, tar = n * n;        while(num <= tar){            for(int i = l; i <= r; i++) mat[t][i] = num++; // left to right.            t++;            for(int i = t; i <= b; i++) mat[i][r] = num++; // top to bottom.            r--;            for(int i = r; i >= l; i--) mat[b][i] = num++; // right to left.            b--;            for(int i = b; i >= t; i--) mat[i][l] = num++; // bottom to top.            l++;        }        return mat;    }}

 


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

纵横千里,捭阖四方

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值