代码随想录—力扣算法题:59螺旋矩阵II.Java版(示例代码与导图详解)

版本说明

当前版本号[20230810]。

版本 修改说明
20230810 初版

目录

文章目录

  • 版本说明
  • 目录
  • 59.螺旋矩阵II
    • 思路
    • 左闭右开方法
    • 左闭右闭方法
    • 两种方法的区别
    • 总结

59.螺旋矩阵II

力扣题目链接
更多内容可点击此处跳转到代码随想录,看原版文件

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

示例:

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

思路

这道题目可以说在面试中出现频率较高的题目,本题并不涉及到什么算法,就是模拟过程,但却十分考察对代码的掌控能力。

要如何画出这个螺旋排列的正方形矩阵呢?

相信很多同学刚开始做这种题目的时候,上来就是一波判断猛如虎。

结果运行的时候各种问题,然后开始各种修修补补,最后发现改了这里那里有问题,改了那里这里又跑不起来了。

大家还记得我们讲解的二分法,提到如果要写出正确的二分法一定要坚持循环不变量原则

而求解本题依然是要坚持循环不变量原则

模拟顺时针画矩阵的过程:

  • 填充上行从左到右
  • 填充右列从上到下
  • 填充下行从右到左
  • 填充左列从下到上

由外向内一圈一圈这么画下去。就像下图一样:

代码随想录—力扣算法题:59螺旋矩阵II.Java版(示例代码与导图详解)_第1张图片

可以发现这里的边界条件非常多,在一个循环中,如此多的边界条件,如果不按照固定规则来遍历,那就是一进循环深似海,从此offer是路人

这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。

左闭右开方法

那么我按照左闭右开的原则,来画一圈,大家看一下:

代码随想录—力扣算法题:59螺旋矩阵II.Java版(示例代码与导图详解)_第2张图片

这里每一种颜色,代表一条边,我们遍历的长度,可以看出每一个拐角处的处理规则,拐角处让给新的一条边来继续画。

这也是坚持了每条边左闭右开的原则

一些同学做这道题目之所以一直写不好,代码越写越乱。

就是因为在画每一条边的时候,一会左开右闭,一会左闭右闭,一会又来左闭右开,岂能不乱。

代码如下,已经详细注释了每一步的目的,可以看出while循环里判断的情况是很多的,代码里处理的原则也是统一的左闭右开。

通过模拟四个方向的填充过程,将数字从1开始按顺时针螺旋的方式填入矩阵中

整体Java代码如下:

class Solution {
    public int[][] generateMatrix(int n) {
        int loop = 0;  // 控制循环次数
        int[][] res = new int[n][n];
        int start = 0;  // 每次循环的开始点 (start, start)
        int count = 1;  // 定义填充数字
        int i, j;

        while (loop++ < n / 2) { // 判断边界后,loop从1开始
            // 模拟上侧从左到右
            for (j = start; j < n - loop; j++) {
                res[start][j] = count++; // 在上侧从左到右的行中填充数字
            }

            // 模拟右侧从上到下
            for (i = start; i < n - loop; i++) {
                res[i][j] = count++; // 在右侧从上到下的列中填充数字
            }

            // 模拟下侧从右到左
            for (; j >= loop; j--) {
                res[i][j] = count++; // 在下侧从右到左的行中填充数字
            }

            // 模拟左侧从下到上
            for (; i >= loop; i--) {
                res[i][j] = count++; // 在左侧从下到上的列中填充数字
            }
            start++;
        }

        if (n % 2 == 1) {
            res[start][start] = count; // 处理矩阵边长为奇数的情况,中心点单独填充数字
        }

        return res;
    }
}
  • 时间复杂度 O(n^2): 模拟遍历二维矩阵的时间
  • 空间复杂度 O(1)

左闭右闭方法

这段代码使用左闭右闭的方式来定义边界。我们使用四个变量 leftrighttopbottom 来表示矩阵的边界,初始时 left=0right=n-1top=0bottom=n-1

  • 在循环中,我们首先从左到右填充上侧的行,行号为 top,列号从 leftright(包括 leftright),逐个填充数字 count,并将 count 加 1。然后,我们将 top 的值加 1。
  • 接着,从上到下填充右侧的列,列号为 right,行号从 topbottom(包括 topbottom),逐个填充数字 count,并将 count 加 1。然后,我们将 right 的值减 1。
  • 然后,从右到左填充下侧的行,行号为 bottom,列号从 rightleft(包括 rightleft),逐个填充数字 count,并将 count 加 1。然后,我们将 bottom 的值减 1。
  • 最后,从下到上填充左侧的列,列号为 left,行号从 bottomtop(包括 bottomtop),逐个填充数字 count,并将 count 加 1。然后,我们将 left 的值加 1。

重复上述步骤,直到 count 的值达到 n*n。循环结束后,我们生成了按顺时针顺序螺旋排列的正方形矩阵。

最后,我们返回生成的结果矩阵 res

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] res = new int[n][n];
        int count = 1;  // 定义填充数字
        int left = 0, right = n - 1, top = 0, bottom = n - 1;  // 定义边界
        
        while (count <= n * n) {
            // 从左到右填充上侧的行
            for (int i = left; i <= right; i++) {
                res[top][i] = count++;
            }
            top++;
            
            // 从上到下填充右侧的列
            for (int i = top; i <= bottom; i++) {
                res[i][right] = count++;
            }
            right--;
            
            // 从右到左填充下侧的行
            for (int i = right; i >= left; i--) {
                res[bottom][i] = count++;
            }
            bottom--;
            
            // 从下到上填充左侧的列
            for (int i = bottom; i >= top; i--) {
                res[i][left] = count++;
            }
            left++;
        }
        
        return res;
    }
}

两种方法的区别

左闭右开方法和左闭右闭方法的区别在于边界的定义方式。

  1. 左闭右开方法:边界的定义是左边界是闭区间,右边界是开区间。即在填充每一行或每一列时,右边界的索引不包含在当前填充的范围内。在填充完一行或一列后,边界会向内缩小
  2. 左闭右闭方法:边界的定义是左边界和右边界都是闭区间,即在填充每一行或每一列时,左右边界的索引都包含在当前填充的范围内。填充完一行或一列后,边界保持不变

总结

首先,定义变量 loop 表示当前的螺旋圈数,初始值为 0。螺旋圈数表示当前正在填充的圈数,总共会填充 n/2 圈。

然后,创建一个大小为 n×n 的空矩阵 res,用来存储结果。

开始循环,每次循环填充一圈。循环的条件是 loop++ < n / 2,保证循环次数不超过 n/2。

  • 在循环中,首先是从左到右填充上侧的。使用变量 j 作为列的索引,从 start 开始,一直到 n - loop - 1(因为最后一列由右侧从上到下填充),逐个填充数字 count,并将 count 加 1。
  • 接着,从上到下填充右侧的列。使用变量 i 作为行的索引,从 start 开始,一直到 n - loop - 1(因为最后一行由下侧从右到左填充),逐个填充数字 count,并将 count 加 1。
  • 然后,从右到左填充下侧的行。这次填充的行号保持为 i,而列号从 j 开始递减,直到 loop。同样,逐个填充数字 count,并将 count 加 1。
  • 最后,从下到上填充左侧的列。这次填充的列号保持为 j,而行号从 i 开始递减,直到 loop + 1。逐个填充数字 count,并将 count 加 1。

每填充完一圈,我们将变量 start 加 1,表示下一圈的起始位置。重复上述步骤,直到循环结束。

如果 n 是奇数,最后还需要填充矩阵的中心点,即 res[start][start],此时的 count 不需要加 1。

最后,返回生成的结果矩阵 res

整个过程按照数学上定义的顺时针顺序,从外圈向内圈逐步填充数字,最终生成了按顺时针顺序螺旋排列的正方形矩阵。具体也可以通过下图进行一个理解。

代码随想录—力扣算法题:59螺旋矩阵II.Java版(示例代码与导图详解)_第3张图片

测试代码如下:

package shuzhu;

public class Day05 {
    public int[][] generateMatrix(int n) {
        int loop = 0;  // 控制循环次数
        int[][] res = new int[n][n];  // 结果矩阵
        int start = 0;  // 每次循环的开始点 (start, start)
        int count = 1;  // 定义填充数字
        int i, j;
        
        while (loop++ < n / 2) {  // 循环次数不超过 n/2
            // 模拟上侧从左到右
            for (j = start; j < n - loop; j++) {
                res[start][j] = count++;  // 在上侧从左到右的行中填充数字
            }
            
            // 模拟右侧从上到下
            for (i = start; i < n - loop; i++) {
                res[i][j] = count++;  // 在右侧从上到下的列中填充数字
            }
            
            // 模拟下侧从右到左
            for (; j >= loop; j--) {
                res[i][j] = count++;  // 在下侧从右到左的行中填充数字
            }
            
            // 模拟左侧从下到上
            for (; i >= loop; i--) {
                res[i][j] = count++;  // 在左侧从下到上的列中填充数字
            }
            
            start++;  // 每次循环结束后,更新开始点的位置
        }
        
        if (n % 2 == 1) {
            res[start][start] = count;  // 处理矩阵边长为奇数的情况,中心点单独填充数字
        }
        
        return res;
    }
    
    public static void main(String[] args) {
        Day05 solution = new Day05();
        
        // 测试生成 3x3 的螺旋矩阵
        int[][] matrix1 = solution.generateMatrix(3);
        System.out.println("生成的 3x3 螺旋矩阵:");
        for (int i = 0; i < matrix1.length; i++) {
            for (int j = 0; j < matrix1[0].length; j++) {
                System.out.print(matrix1[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println();
    }
}
       

你可能感兴趣的:(力扣算法题学习笔记(自用),算法,leetcode,矩阵,java,开源)