算法力扣刷题记录五【59.螺旋矩阵II】

前言

第五篇,继续。
力扣【59】:螺旋矩阵II
(Hint: 我认为blog记录得到最终代码的过程,所以有些代码片是逻辑错误(会标明“有错”),体现改进,所以阅读时请不要错位,或复制错误代码。)


一、题目阅读和理解

题目阅读

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

示例 1:
在这里插入图片描述

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

示例 2:

输入:n = 1
输出:[[1]]

提示:

1 <= n <= 20

二、第一次尝试

思路:阅读时可以画图更明白

(1)不成功;

我首先画n <= 5的顺时针过程,第一步发现:行列交错填充数组。所以:

  • 定义一个bool=0(初始值);

  • if(bool=0),操作行,结束时bool=1;

  • else,操作列。在一个for循环内。

      过程如下:
      for(int k = 0; k < xxx;k++){		//xxx代表还没确定范围,需要根据n找规律。
      	if(!bool){
      		//操作行
      	}else
      		//操作列
      }
    

行不通,原因,循环条件不统一

1. 在操作行/列,for循环变量i一会向右,i++;一会向左,i--;
2. 另外for循环变量i的初始值每次也不一样,所以循环条件一直在动。

难道还要if判断什么时候++,什么时候--?那我要加的if好多,所以换思路。

(2)成功

第二步发现:

  • 循环操作:向→;再↓;再←;再↑。4个为一组,看作一轮。所以有了大框架

      for(int k = 0;k < (2*n-1);k++){		//为什么k < (2*n-1),看下一点找规律
      	 switch(k % 4){
      	 	case 0:             //方向向右
      	 		//执行操作;
      	 		break;
      	 	case 1:		        //方向向下
      	 		//执行操作;
      	 		break;
      	 	case 2:             //方向向左
      	 		//执行操作;
      	 		break;
      	 	case 3:             //方向向下
      	 		//执行操作;
      	 		break;
      	 }
      	 //xxxx  后面分析还要加什么。
      }
    
  • n=2时,3步:向→;再↓;再←;
    n=3时,5步:向→;再↓;再←;再↑;向→;
    n=4时,7步:向→;再↓;再←;再↑;向→;再↓;再←;
    n=5时,9步:向→;再↓;再←;再↑;向→;再↓;再←;再↑;再→;

    所以for循环的范围是k < (2*n-1)

  • 接着填充每个方向内的代码,即case下的代码,完成矩阵元素填充

    以向右操作case0,作完整分析:(其余类似)

    • 首先想到for循环:

        	int i=0;//行坐标
        	int j = 0;//列坐标
        	case 0:
        		for(;j <= line; j++) {//初始值——没有固定值,先不写;范围——line每次减1,初始值(n-1);向右,所以j++
        			matrix[i][j] = num++;	//先赋值后++,所以num初始为1.
        		}
        		j -=1; //传给下一个case的j,for循环结束,j = n,所以要减1.
              i +=1; //因为转角元素已经填充,所以↓,不能重复操作转角元素,所以i+1.
              break;
      
    • 但我想着写成while更好看,所以改变了下:

       case 0:             //方向向右
           while(j <= line){
                 matrix[i][j] = num++;   //先赋值后++
                  j++;
           }
           j -=1;
           i +=1;
           break;
      
    • 其余同理完成case填充,每个while结束后调整i,j,都是为了递交下一棒时索引正确。最后大框架得到中间步骤1

         //每个case内的代码操作填充完毕
         for(int k = 0;k < (2*n-1);k++){
         switch(k % 4){
             case 0:             //方向向右
                 while(j <= line){
                     matrix[i][j] = num++;   //先赋值后++
                     j++;
                 }
                 j -=1;
                 i +=1;
                 break;
             case 1:           //方向向下
                 while(i <= line){
                     matrix[i][j] = num++;
                     i++;
                 }
                 i -=1;	//传给case2,因为while结束,i = n,超出范围,所以减1
                 j -=1;    // 转角元素已经操作过。
                 break;
             case 2:         //方向向左
                 while(j >= row){
                     matrix[i][j] = num++;
                     j--;
                 }
                 j +=1;	//while结束后,j=-1,超出下标范围
                 i -=1;	//转角元素已经操作过
                 break;
             case 3:           //方向向下
                 while(i >= row+1){
                     matrix[i][j] = num++;
                     i--;
                 }
                 i +=1;
                 j +=1;
                 break;
             default: break;
         }
      
    • while控制范围的条件:line和row变量。

      • 向→或↓步骤:每一轮到这两步时,逐步收缩下标,进入内圈,所以line一轮结束后要减1;
      • 向←或↑步骤,每一轮到这两步时,逐步增加下标,进入内圈,所以row一轮结束后要加1;

      所以大框架得到中间步骤2

      //完善for循环中:line和row变量操作—— if(k%4 == 3)
      
      int line = n - 1;		//初始值
      int row = 0;			//初始值
      int num = 1;  			//填入数字,初始1,不是0
      for(int k = 0;k < (2*n-1);k++){
          switch(k % 4){
              case 0:             //方向向右
                  while(j <= line){
                      matrix[i][j] = num++;   //先赋值后++
                      j++;
                  }
                  j -=1;
                  i +=1;
                  break;
              case 1:           //方向向下
                  while(i <= line){
                      matrix[i][j] = num++;
                      i++;
                  }
                  i -=1;
                  j -=1;
                  break;
              case 2:         //方向向左
                  while(j >= row){
                      matrix[i][j] = num++;
                      j--;
                  }
                  j +=1;
                  i -=1;
                  break;
              case 3:           //方向向下
                  while(i >= row+1){
                      matrix[i][j] = num++;
                      i--;
                  }
                  i +=1;
                  j +=1;
                  break;
              default: break;
          }     一步
          
          if(k%4 == 3){			//此时要开启新一轮, 下一步是→。收缩到内圈顺时针旋转。
              line--;
              row++;
          }
      }
      
  • 总结:再加上初始化vector二维数组的定义,得到完整代码

//通过测试,没有问题。
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> matrix(n,vector<int> (n,0)); //返回值:得到的矩阵
        //for(int i = 0;i < n;i++){          //怎么初始化容器的二维数组?
        //    vector<int> subrow = new int[n]];    //初始化指针,分配内存。
        //    matrix.push_back(subrow);
        //}这个是错误方式初始化。

        int i = 0;//行指针
        int  j = 0;//列指针,操作matrix坐标。

        //控制每轮的范围
        int line = n - 1;
        int row = 0;
        int num = 1;//填入数字,初始1,不是0
        for(int k = 0;k < (2*n-1);k++){
            switch(k % 4){
                case 0:             //方向向右
                    while(j <= line){
                        matrix[i][j] = num++;   //先赋值后++
                        j++;
                    }
                    j -=1;
                    i +=1;
                    break;
                case 1:           //方向向下
                    while(i <= line){
                        matrix[i][j] = num++;
                        i++;
                    }
                    i -=1;
                    j -=1;
                    break;
                case 2:         //方向向左
                    while(j >= row){
                        matrix[i][j] = num++;
                        j--;
                    }
                    j +=1;
                    i -=1;
                    break;
                case 3:           //方向向下
                    while(i >= row+1){
                        matrix[i][j] = num++;
                        i--;
                    }
                    i +=1;
                    j +=1;
                    break;
                default: break;
            }     
            if(k%4 == 3){
                line--;
                row++;
            }
        }
        return matrix;
    }
};
额外补充vector初始化:
1 初始化一维数组:
	vector<int> nums();或vector<int> nums;//创建空容器,没有元素。可以看成一维数组。
	vector<int> nums(n,0);//创建有n个元素的容器,每个元素的值,初始化为0.
2 初始化二维数组:
	vector<int> matrix(n,vector<int> (m,0));//n行m列,元素初始化为0.

三、代码随想录学习

学习内容:

循环不变量

(1)理解:遇到循环,始终坚持一种区间处理原则,[ )或[ ]或( ],无论哪种类型都可以,但一定要从头坚持到尾。不可以混合多个区间处理原则,一会一变,就会陷入逻辑混乱。
(2)在【记录一:二分查找】中,同理。

思路对比

我的思路:虽然发现了循环规律,但是我把每一步单独拆开操作,for循环一次代表下一步:遇到转角元素,要改变方向。所以相对复杂;
卡哥思路:把4步看作一个整体,while循环一次代表完成一圈。更简单。

代码实现

//靠自己按新思路写一遍,坚持左闭右开区间,即转角元素交给下一条边填充。通过测试。
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> matrix(n,vector<int> (n,0));//返回值
        int i = 0;  //行坐标
        int j = 0;  //列坐标
        int offset = 0;//每次收缩内圈
        int num = 1;
        int k = 0;
        while(k < n/2){
            for(;j < n - 1-offset;j++){   //没有操作转角元素,留给下一个for
                matrix[i][j] = num++;
            }
            for(;i < n-1-offset;i++){
                matrix[i][j] = num++;
            }
            for(;j > offset;j--){
                matrix[i][j] = num++;
            }
            for(;i > offset;i--){
                matrix[i][j] = num++;
            }
            //收缩内圈
            i++;
            j++;
            offset++;
            k++;
        }
        //n为奇数
        if(n%2){
            matrix[i][j] = num++;
        }
        return matrix;
    }
};

对比参考代码:
1 直接用i ,j 操作元素,相当于startx和starty,所以可以不要startx和starty。
2 我用k控制循环次数,多了两行代码:int k = 0;k++;
	但参考直接loop--即可。(采纳)

另外我想再写一个区间是左开右闭,即转角元素交给当前边填充:(也就是第一次尝试的思路)

注意:第四个for没有“=”,而且第一个for开始需要特别处理起始元素。所以比左闭右开麻烦一些,需要注意边界。

//通过测试。左开右闭,即转角元素交给当前边填充。特别处理起始元素。
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> matrix(n,vector<int> (n,0));
        int i = 0;//横坐标
        int j =0;//纵坐标
        int offset = 0;//收缩内圈
        int loop = n/2;//循环圈数
        int mid = n/2; //正中间的元素
        int num = 1;//填充元素
        while(loop--){
            matrix[i][j] = num++;   //起始元素处理
            for(j = j+1;j <= n-1-offset;j++){
                matrix[i][j] = num++;
            }
            for(i = i+1,j=j-1;i <= n-1-offset;i++){
                matrix[i][j] = num++;
            }
            for(j=j-1,i=i-1; j >= offset;j--){
                matrix[i][j] = num++;
            }
            for(i=i-1,j=j+1;i > offset;i--){    //注意没有“=”
                matrix[i][j] = num++;
            }
            i++;
            j++;
            offset++;
        }
        if(n%2){
            matrix[mid][mid] = num++;
        }
        return matrix;


    }
};

总结

反思

(1)我发现了规律,想到了循环,但是没有看作一个整体,而是一步一步的划分开。所以操作麻烦,每次都要修正i,j坐标。但是按照参考思路,每个while完成一圈就很方便。

收获

通过控制i,j,始终坚持循环不变量,遵循从一而终的区间原则,通过本文可以掌握较好。

(欢迎指正,转载标明出处)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python FastAPI操作数据库实现注册登录

代码如下 from fastapi import FastAPI, APIRouter, HTTPException, status from pydantic import BaseModel from fastapi.responses import JSONResponse from typing import Optional from fastapi.middleware.cors import CORSMiddleware from utils.time import DateTime…

搭建抖音微短剧系统:源码部署与巨量广告回传全解析

在数字化浪潮中&#xff0c;抖音微短剧已成为内容创作的新宠。想要搭建一个高效的抖音微短剧系统&#xff0c;并实现与巨量广告的有效回传吗&#xff1f;本文将为您详细解析源码部署与广告回传的关键步骤。 一、源码部署&#xff1a;构建短剧系统的基石 源码是软件开发的起点…

ubuntu 挂载新硬盘 记录

Ref 安全自动挂载硬盘&#xff0c; https://berylbot.com/archives/mount-disks-ubuntu 挂载新硬盘, https://berylbot.com/archives/mount-disks-ubuntu 1. 检查新硬盘是否被系统识别 lsblk -f 查看所有硬盘的UUID, 其中 mount point 为空则表示尚未挂载的硬盘。 列出所有可用…

鸿蒙开发设备管理:【@ohos.batteryInfo (电量信息)】

电量信息 该模块主要提供电池状态和充放电状态的查询接口。 说明&#xff1a; 本模块首批接口从API version 6开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import batteryInfo from ohos.batteryInfo;属性 描述电池信息。 系统能…

三元和磷酸铁锂电池有什么区别?

现在的电动车大多都会使用到锂电池&#xff0c;在常见的锂电池分为两种&#xff0c;一种是三元锂电池另外一种是磷酸铁锂电池&#xff0c;面对这两种锂电池时&#xff0c;它们到底有什么不同&#xff1f; 1、材料不同 这两种锂电池的不同之处便是材料不同&#xff0c;磷酸铁锂…

C#和python端通信之使用共享内存

一、前言 本篇主要实验通过使用共享内存实现C#端代码和python端代码之间的通信&#xff0c;主要目的是相较于直接传输较大的数据&#xff08;例如图像数据&#xff09;&#xff0c;该方式更节省时间。 二、代码 C#端&#xff1a; 创建了一个大小为1的共享内存&#xff0c;名为…

搜维尔科技:SenseGlove Nova2国内首款支持手掌心力回馈手套开售

《SenseGlove Nova 2》现正全球发行中! 搜维尔科技独家代理最新上市的 SenseGlove Nova 2 是世上首款&#xff0c;也是目前市面上唯一一款提供手掌力回馈的无缐VR力回馈手套&#xff0c;它结合了三种最先进的反馈技术&#xff0c;包括主动反馈、强力反馈及震动反馈&#xff0c…

【Flink metric(1)】Flink指标系统的系统性知识:获取metric以及注册自己的metric

文章目录 一. Registering metrics&#xff1a;向flink注册新自己的metrics1. 注册metrics2. Metric types:指标类型2.1. Counter2.2. Gauge2.3. Histogram(ing)2.4. Meter 二. Scope:指标作用域1. User Scope2. System Scope ing3. User Variables 三. Reporter ing四. System…

Linux线程互斥锁

目录 &#x1f6a9;看现象&#xff0c;说原因 &#x1f6a9;解决方案 &#x1f6a9;互斥锁 &#x1f680;关于互斥锁的理解 &#x1f680;关于原子性的理解 &#x1f680;如何理解加锁和解锁是原子的 &#x1f6a9;对互斥锁的简单封装 引言 大家有任何疑问&#xff0c;可…

昇思25天学习打卡营第4天|onereal

今天学习的内容是&#xff1a;ResNet50迁移学习 以下内容拷贝至教程&#xff0c;实话实话看不懂&#xff0c;迷迷糊糊都运行jupyter里的代码。走完程序&#xff0c;训练生成了一些图片。 ResNet50迁移学习 在实际应用场景中&#xff0c;由于训练数据集不足&#xff0c;所以很少…

python OpenCV 库中的 cv2.Canny() 函数来对图像进行边缘检测,并显示检测到的边缘特征

import cv2# 加载图像 image cv2.imread(4.png)# 使用 Canny 边缘检测算法提取边缘特征 edges cv2.Canny(image, 100, 200)# 显示边缘特征 cv2.imshow(Edges, edges) cv2.waitKey(0) cv2.destroyAllWindows() 代码解析&#xff1a; 导入 OpenCV 库&#xff1a; import cv2加…

【十】【QT开发应用】QT中文乱码解决方案

QT中文乱码解决方案 粘贴代码导致的乱码 粘贴别人的代码时,在记事本里面"过一遍",然后再粘贴到QTCreator 使用u8 配置QT 不使用QT使用VS QT自选编码格式 结尾 最后&#xff0c;感谢您阅读我的文章&#xff0c;希望这些内容能够对您有所启发和帮助。如果您有任何问…

新能源汽车CAN总线故障定位与干扰排除的几个方法

CAN总线是目前最受欢迎的现场总线之一,在新能源车中有广泛应用。新能源车的CAN总线故障和隐患将影响驾驶体验甚至行车安全,如何进行CAN总线故障定位及干扰排除呢? 目前,国内机动车保有量已经突破三亿大关。由于大量的燃油车带来严峻的环境问题,因此全面禁售燃油车的日程在…

C语言笔记26 •顺序表应用•

基于动态顺序表实现通讯录项目 1.通讯录其实也就是顺序表&#xff0c;就是把里面存的数据类型变了一下 &#xff0c;所以有一些方法对于顺序表适用&#xff0c;对于通讯录也是适用的&#xff08;初始化&#xff0c;销毁&#xff0c;内存空间扩容&#xff09;。 2.要用到顺序表…

Ngnix内存池——高并发实现高效内存管理

目录 一、高并发下传统方式的弊端 1、常用的内存操作函数 2、弊端一 3、弊端二 4、弊端三 5、弊端四 二、弊端解决之道 1、内存管理维度分析 2、内存管理组件选型 三、高并发内存管理最佳实践 1、内存池技术 2、内存池如何解决弊端 3、高并发内存池如何实现 四、…

springboot+vue+mysql+mybatis 二手交易平台

springbootvuemysqlmybatis 二手交易平台 相关技术 javaspringbootmybatismysqlvueelementui

十常侍乱政 | 第2集 | 愿领精兵五千,斩关入内,册立新君,诛杀宦党,扫清朝廷,以安天下 | 三国演义 | 逐鹿群雄

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f4cc;这篇博客是毛毛张分享三国演义文学剧本中的经典台词和语句&#xff0c;本篇分享的是《三国演义》第Ⅰ部分《群雄逐鹿》的第2️⃣集《十常侍乱政治》&am…

DigitalOcean Droplet 云主机新增内置第五代 Xeon CPU 机型

DigitalOcean 近期宣布&#xff0c;在其高级 CPU 服务器&#xff08;Premium CPU-Optimized Droplet&#xff09;队列中引入英特尔第五代Xeon可扩展处理器&#xff08;代号为 Emerald Rapids&#xff09;。作为英特尔产品线中的最新一代用于数据中心工作负载的处理器&#xff0…

干涉阵型成图参数记录【robust】

robust 这个玩意经常忘记&#xff0c;就是取2的时候是更加显示大尺度的结构&#xff0c;取-2更加显示小尺度结果&#xff0c;一般取0就是正常就好了

数学建模--lingo解决线性规划问题~~灵敏度分析的认识

目录 1.线性规划问题举隅 &#xff08;1&#xff09;问题介绍 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;灵敏度分析 &#xff08;4&#xff09;方法缺陷 &#xff08;5&#xff09;可行域&凸集 2.lingo的简单认识 &#xff08;1&#xff09;默认…