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

news2024/11/25 18:39:54

版本说明

当前版本号[20230810]。

版本修改说明
20230810初版

目录

文章目录

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

59.螺旋矩阵II

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

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

示例:

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

思路

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

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

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

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

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

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

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

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

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

image-20230810172412707

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

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

左闭右开方法

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

image-20230810172502146

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

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

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

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

代码如下,已经详细注释了每一步的目的,可以看出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

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

image-20230810175904882

测试代码如下:

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();
    }
}
       

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/859292.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BDA初级分析——数据收集、清洗和整理

一、认识数据 什么是数据&#xff1f; 数据是对客观世界的记录&#xff0c;用来记载事物的性质、状态、相互关系等。 有哪些常见的数据类型&#xff1f; 什么是数据集&#xff1f; 数据集&#xff1a;数据的集合&#xff0c;通常以表格形式出现。 二、收集数据 我们都会从哪里…

优思学院|6sigma合格率计算公式

概述 在现代竞争激烈的商业环境中&#xff0c;质量控制对于制造业和服务业都至关重要。其中&#xff0c;一个强大的方法是采用6sigma&#xff08;也称为6σ&#xff09;方法来提高质量和降低缺陷率。本文将深入探讨6sigma合格率计算公式&#xff0c;解释其在质量管理中的应用&…

20款奔驰C260 L(W206)更换内饰最全发光套件,提升车内氛围

原厂1:1设计&#xff0c;免编程匹配&#xff0c;无损安装&#xff0c;可升级项目&#xff1a; 1、碳纤维中控氛围灯&#xff08;阿凡达水滴款&#xff09; 2、发光前风口&#xff1b; 3、发光后风口&#xff1b; 4、发光座椅背气氛灯&#xff1b; 5、中音发光盖板 6、主动…

企业有VR全景拍摄的需求吗?能带来哪些好处?

在传统图文和平面视频逐渐疲软的当下&#xff0c;企业商家如何做才能让远在千里之外的客户更深入、更直接的详细了解企业品牌和实力呢&#xff1f;千篇一律的纸质材料已经过时了&#xff0c;即使制作的再精美&#xff0c;大家也会审美疲劳&#xff1b;但是你让客户远隔千里&…

网络编程基础:Linux网络I/O模型、JavaI/O模型、Netty

文章目录 一、Linux网络I/O模型简介0.文件描述符和系统调用1. 阻塞I/O模型2. 非阻塞I/O模型&#xff08;轮询&#xff09;3. I/O复用模型&#xff08;轮询、事件驱动&#xff09; 二、Java的I/O演进1.BIO&#xff08;阻塞&#xff09;&#xff08;1&#xff09;套接字&#xff…

安达发|疫情影响下的APS排程可以给制造业解决哪些问题

随着市场需求的不断变化&#xff0c;特别是对柔性、小单量多批次的需求日益增长&#xff0c;再加上疫情的影响&#xff0c;企业随时可能面临延期交货的风险。这使得行业供应链管理的复杂性不断加剧&#xff0c;企业对于生产管理高效性的需求也日益显著。 同时&#xff0c;我们…

vm workstation pro安装centos7

官网下载vm workstation pro&#xff0c;参考上一篇文章&#xff1a;https://editor.csdn.net/md/?articleId132208423安装centos7 官网下载的vm&#xff0c;是有一个镜像iso文件的 完成后稍等&#xff1a; 这一步很重要&#xff0c;别急着点开始安装&#xff0c;看到有感叹…

.NET 8 Preview 6 中推出 .NET MAUI: 欢迎使用 VS Code 和 VS for Mac

作者&#xff1a;David Ortinau 排版&#xff1a;Alan Wang .NET 8 Preview 6 推出了.NET MAUI&#xff0c;同时修复了 23 个关键问题&#xff0c;并引入了适用于 iOS 的 Native AOT。此外&#xff0c;您现在可以在 .NET 8 中使用 .NET MAUI&#xff0c;只要安装 Visual Studio…

excel将主信息和明细信息整理为多对多(每隔几行空白如何填充)

excel导出的数据是主信息和明细信息形式。 原始数据图&#xff1a; 最终效果:

UML之四种事物

目录 结构事物 行为事物 分组事物&#xff1a; 注释事物 结构事物 1.类(Class) -类是对一组具有相同属性、方法、关系和语义的对象的描述。一个类实现一个或多个接口 2.接口(interface) -接口描述 了一个类或构件的一个服务的操作集。接口仅仅是定义了一组操作的规范&…

23、springboot日志使用入门-- SLF4J+Logback 实现(springboot默认的日志实现),日志打印到控制台及日志输出到指定文件

springboot日志使用入门 ★ 典型的Spring Boot日志依赖&#xff1a; spring-boot-start.jar -- spring-boot-starter-logging.jar (Spring Boot的日志包&#xff09;-- logback&#xff08;core、classic&#xff09;-- log4j-to-slf4j.jar-- jul-to-slf4j.jar就是springboo…

【el-upload】批量上传图片时在before-upload中添加弹窗判断时的踩坑记录

一、初始代码 1. 初始使用组件代码片段 <!-- 上传 --> <DialogUploadFile ref"uploadFile" success"refresh" />// 上传 const uploadHandle () > {if (selections.value.length ! 1) {onceMessage.warning(请选择一条数据操作)return}u…

并行FIR滤波器

FIR 滤波器原理 FIR 滤波器是有限长单位冲击响应滤波器&#xff0c;又称为非递归型滤波器。FIR 滤波器具有严格的线性相频特性&#xff0c;同时其单位响应是有限长的&#xff0c;因而是稳定的系统。 FIR 滤波器本质上就是输入信号与单位冲击响应函数的卷积&#xff0c;表达式…

MapBox加载不同风格

初始化MapBox地图&#xff1a; var map new mapboxgl.Map({container: map,zoom: 3,center: [105, 34],//此处更改地图风格style: mapbox://styles/mapbox/satellite-v9,hash: false,});1.户外地图&#xff08;mapbox://styles/mapbox/basic-v9&#xff09;新版&#xff1a;&a…

python——案例15:判断奇数还是偶数

案例15&#xff1a;判断奇数还是偶数numint(input(输入数值&#xff1a;))if(num%2)0: #通过if语句判断print("{0}是偶数".format(num))else: #通过else语句判断print("{0}是奇数".format(num))

Linux Linux基础命令

1.pwd——显示当前位置的绝对路径 2.cd——切换目录&#xff0c;cd 后的参数表示要切换到的位置 &#xff08;1&#xff09;cd后面的参数为绝对路径&#xff1a; &#xff08;2&#xff09;cd后面的参数为相对路径&#xff1a; &#xff08;3&#xff09;cd ~回到家目录&#…

Ansys Lumerical | 针对多模干涉耦合器的仿真设计与优化

说明 本示例演示通过12端口多模干涉(MMI)耦合器计算宽带传输和光损耗&#xff0c;并使用S参数在 INTERCONNECT 中创建 MMI 的紧凑模型。(联系我们获取文章附件) 综述 低损耗光耦合器和光分路器是基于 Mach-Zehnder 的光调制器的基本组件&#xff0c;是集成电路的关键组成部分。…

数据结构——双向链表

双向链表实质上是在单向链表的基础上加上了一个指针指向后面地址 单向链表请参考http://t.csdn.cn/3Gxk9 物理结构 首先我们看一下两种链表的物理结构 我们可以看到&#xff1a;双向在单向基础上加入了一个指向上一个地址的指针&#xff0c;如此操作我们便可以向数组一样操作…

【Android NDK开发】Android Studio 编写 JNI (C++)代码无提示

随笔记 Android Studio在编写C代码时候&#xff0c;引入对应的头文件&#xff0c;Android Studio里却不提示对应的方法&#xff0c;需要在Studio中设置一下。 Mac中&#xff0c;选择 Android Studio > Preferences&#xff0c;选择Clangd >>Disable Clangd completio…

【Vue3】自动引入插件-`unplugin-auto-import`

Vue3自动引入插件-unplugin-auto-import&#xff0c;不必再手动 import 。 自动导入 api 按需为 Vite, Webpack, Rspack, Rollup 和 esbuild 。支持TypeScript。由unplugin驱动。 插件安装&#xff1a;unplugin-auto-import 配置vite.config.ts&#xff08;配置完后需要重启…