螺旋矩阵II

力扣题目链接(opens new window)

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

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

初见思路

说实话没什么思路,唯一的思路就是找规律

但是只能找到一个:数组长度=n,没了

常规思路

本体为常见面试题,不涉及算法

从题目要做的事情来看,就是要模拟一个转圈的过程

也就是行为模拟题

易错点就是:如何处理转圈过程中的边界问题

一种错误思路是,在处理关键点位(如正方形的四个顶点)时,使用了不一样的标准。

例如遍历最上面的第一条边时,处理了该边上的两个顶点,到了遍历正方形左边的时候,应该跳过一个顶点再处理,此时有可能没跳或者跳多了,导致后面处理逻辑乱掉。

引入循环不变量原则

即在遍历时坚持一种规则,比如左闭右开【详见二分查找思路】,逻辑如下:

解题模板

核心方法就是4个for循环,注释都在代码里了,自己看

有个注意点是:在前两条边转完之后,从底边转到左侧边的过程中,结束条件就记住左闭右开,最后一个点不处理就可以推出来

c++版

  1. class Solution {
  2. public:
  3. vector<vector<int>> generateMatrix(int n) {
  4. vector<vector<int>> matrix(n, vector<int>(n, 0));//定义需要填充的正方形矩阵
  5. int starX = 0;//初始化起始位置
  6. int starY = 0;
  7. int stopIndex = 1;//初始化终止位置(遵循左闭右开)
  8. int count = 1;//要填入的数
  9. int looptimes = n/2;//循环次数(因为是正方形所以循环次数是可以计算的)
  10. int midIndex = n/2;//中心点位置(只要是用于能处理n为奇数时的情况,该情况下中心点填充不到,需要单独处理)
  11. //循环填充
  12. //j控制纵向,i控制横向,通过四个循环不断遍历填充数组
  13. int i,j;
  14. while(looptimes--){//4个for循环
  15. for(j = starY; j < n - stopIndex; ++j) matrix[starX][j] = count++;//模拟在上侧时,从左到右(固定纵坐标)
  16. for(i = starX; i < n - stopIndex; ++i) matrix[i][j] = count++;//模拟在右侧时,从上到下(当前j已经最大,相当于固定横坐标)
  17. //注意这里不再需要对x、y进行初始化
  18. //因为经过一轮遍历,x和y都已经取到最大值
  19. for( ; j > starX; --j) matrix[i][j] = count++;//模拟底侧从右到左,停止条件是大于起始点位,因为最后一个点不处理//i不变,j在减
  20. for( ; i > starY; --i) matrix[i][j] = count++;//模拟左侧从下到上//i在减,j不变
  21. starX++;//起始位置往中心移动
  22. starY++;
  23. stopIndex++;//终止位置前移
  24. }
  25. //单独处理mid位置
  26. if(n % 2 == 1){
  27. matrix[midIndex][midIndex] = count;//此时的count已经累加到最后
  28. }
  29. return matrix;
  30. }
  31. };

二刷问题:

​ 遍历过程中的横纵坐标要考虑清楚(i横j纵),且默认我们遵循左闭右开的原则,所以需要设置一个stop停止变量

Java版

ps:

1、初始化变量的时候不要像C++那样连着写

2、Java取模判断奇偶n % 2 == 1 ? "是奇数" : "是偶数"

  1. class Solution {
  2. public int[][] generateMatrix(int n) {
  3. /*
  4. int startIndex = 0;//也可以这样写,考虑到正方形矩阵,这俩值是相同的,所以可以只用一个变量表示
  5. */
  6. int[][] nums = new int[n][n];//定义正方形矩阵
  7. int startX = 0;//起始位置变量
  8. int startY = 0;
  9. int stop = 1;//控制遍历终止位置的变量
  10. int conut = 1;//数组应该填充的值的计数变量
  11. int looptimes = n/2;//循环的圈数,例如n为奇数3,那么looptimes = 1 只是循环一圈,矩阵中间的值需要单独处理
  12. int midIndex = n/2;//当n为奇数时,矩阵中间的值的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
  13. //遵循左闭右开的规则进行遍历
  14. //i控制纵向,j控制横向,通过四个循环不断遍历填充数组
  15. int i, j;
  16. while(looptimes-- > 0){
  17. for(j = startX; j < n - stop; j++){//模拟上侧从左到右
  18. nums[startX][j] = conut++;
  19. }
  20. //
  21. for(i = startY; i < n - stop; i++){//模拟右侧从上到下
  22. nums[i][j] = conut++;
  23. }
  24. //注意这里不再需要对i、j进行初始化
  25. //因为经过一轮遍历,i和j都已经取到最大值
  26. for(;j > startX; j--){//模拟底侧从右到左,停止条件是大于起始点位,因为最后一个点不处理
  27. nums[i][j] = conut++;//i不变,j在减
  28. }
  29. for(;i > startY; i--){//模拟左侧从下到上
  30. nums[i][j] = conut++;//i在减,j不变
  31. }
  32. //转完一圈,起始位置加1,[0,0]->[1,1]
  33. startX++;
  34. startY++;
  35. //终止位置肯定要提前,体现在变量上就是变量增大
  36. stop++;
  37. }
  38. //为了防止n为奇数,遍历到最后中间会留下一个空的情况,需要单独进行处理
  39. if(n % 2 == 1){
  40. nums[midIndex][midIndex] = conut;
  41. }
  42. return nums;
  43. }
  44. }

拓展练习

LeetCode54螺旋矩阵
思路

最开始还螺旋矩阵II的思路去做,发现不行

因为区别于正方形矩阵,在一个给定形状的矩阵中(该矩阵有可能是矩形),我们没有办法像之前那样去计算出我们需要转的圈数,也没有办法通过计算去得到每次转圈的起始点位

因此按老思路写,只能正常遍历一圈

Java版(错误)

  1. class Solution {
  2. public List<Integer> spiralOrder(int[][] matrix) {
  3. /*
  4. int startIndex = 0;//也可以这样写,考虑到正方形矩阵,这俩值是相同的,所以可以只用一个变量表示
  5. */
  6. List<Integer> list = new ArrayList<Integer>();//定义动态数组,存放遍历值
  7. //获取数组的行和列的数值
  8. int m = matrix.length;//行
  9. int n = matrix[0].length;//列
  10. //定义上下左右边界
  11. int left = 0;
  12. int rigth = n - 1;
  13. int top = 0;
  14. int bottom = m -1;
  15. //循环遍历
  16. while(true){
  17. //遍历上边界所在的行,从左往右(left->right),起始点为[left,top]
  18. for(int i = left; i < rigth; i++){
  19. list.add(matrix[top][i]);
  20. if(top > bottom){
  21. break;//越界直接结束遍历
  22. }else{
  23. top++;//上边界向下移
  24. }
  25. //精简写法:
  26. // if(++top){
  27. // break;
  28. // }
  29. }
  30. //遍历右边界所在的列,从上往下(top->bottom),起始点为[right,top]
  31. for(int i = top; i < bottom; i++){
  32. list.add(matrix[i][rigth]);
  33. if(rigth < left){
  34. break;
  35. }else{
  36. rigth--;//右边界向左移
  37. }
  38. }
  39. //遍历下边界所在的行,从右往左(right->left),起始点为[right,bottom]
  40. for(int i = rigth; i > left; i--){
  41. list.add(matrix[bottom][i]);
  42. if(bottom < top){
  43. break;
  44. }else{
  45. bottom--;//下边界向上移
  46. }
  47. }
  48. //遍历左边界所在的列,从下往上(bottom->top),起始点为[left,bottom]
  49. for(int i = bottom; i > top; i--){
  50. list.add(matrix[left][i]);
  51. if(left > rigth){
  52. break;
  53. }else{
  54. left++;//左边界向右移
  55. }
  56. }
  57. }
  58. return list;
  59. }
  60. }

因此需要换一种方式:参考

还是转圈,只不过这次我们以“边界”为单位去遍历

通过维护矩阵的“上下左右”四个边界来达到逐步遍历矩阵的目的,过程如下(该过程遵循左闭右闭原则):

  1. 上边界遍历得到(下移至第二行):
  2. 1234
  3. 右边界遍历得到(左移至第三列):
  4. 812
  5. 下边界遍历得到(上移至第二行):
  6. 11109
  7. 左边界遍历得到(右移至第二列):
  8. 5
  9. 上边界遍历得到(下移至第三行,于下边界相交,遍历结束):
  10. 67

这个过程也解释了为什么判断break时不能使用含等于的情况

题解

c++版

  1. class Solution {
  2. public:
  3. vector<int> spiralOrder(vector<vector<int>>& matrix) {
  4. //获取矩阵的行、列大小
  5. int m = matrix[0].size();//行
  6. int n = matrix.size();//列
  7. //设定上下左右四个边界
  8. int left = 0;
  9. int right = m - 1;//注意是行还是列
  10. int top = 0;
  11. int down = n - 1;
  12. vector<int> res;//保存遍历值
  13. //循环取值
  14. while(true){
  15. for(int i = left; i <= right; ++i) res.push_back(matrix[top][i]);//遍历上边界,从左到右(从左边界出发到右边界结束)
  16. top++;//遍历完一条边界后,边界移动
  17. if(top > down) break;
  18. for(int i = top; i <= down; ++i) res.push_back(matrix[i][right]);//遍历右边界,从上到下(从上边界出发到下边界结束)
  19. right--;//遍历完一条边界后,边界移动
  20. if(right < left) break;
  21. for(int i = right; i >= left; --i) res.push_back(matrix[down][i]);//遍历下边界,从右往左(从右边界出发到左边界结束)
  22. down--;//遍历完一条边界后,边界移动
  23. if(down < top) break;
  24. for(int i = down; i >= top; --i) res.push_back(matrix[i][left]);////遍历左边界,从下往上(从下边界出发到上边界结束)
  25. left++;//遍历完一条边界后,边界移动
  26. if(left > right) break;
  27. }
  28. return res;
  29. }
  30. };

二刷问题:

计算右边界和下边界时,行列搞混,导致无法ac

Java版代码如下

  1. class Solution {
  2. public List<Integer> spiralOrder(int[][] matrix) {
  3. int m = matrix.length;//行
  4. int n = matrix[0].length;//列
  5. //设定四个边界
  6. int left = 0;//左边界
  7. int right = n - 1;//右边界
  8. int top = 0;//上边界
  9. int down = m - 1;//下边界
  10. List<Integer> list = new ArrayList<Integer>();
  11. while(true){
  12. //遍历上边界,从左(边界)到右(边界)
  13. for(int i = left; i <= right; i++){
  14. list.add(matrix[top][i]);
  15. }
  16. // ++top;//上边界下移(先自增)
  17. // if(top > down){//如果上边界超过下边界,结束遍历
  18. // break;
  19. // }
  20. if(++top > down){//如果上边界超过下边界,结束遍历
  21. break;
  22. }
  23. //遍历右边界,从上(边界)到下(边界)
  24. for(int i = top; i <= down; i++){
  25. list.add(matrix[i][right]);
  26. }
  27. // --right;//右边界左移
  28. if(--right < left){//同理
  29. break;
  30. }
  31. //遍历下边界,从右(边界)往左(边界)
  32. for(int i = right; i >= left; i--){
  33. list.add(matrix[down][i]);
  34. }
  35. // --down;
  36. if(--down < top){
  37. break;
  38. }
  39. //遍历左边界,从下(边界)往上(边界)
  40. for(int i = down; i >= top; i--){
  41. list.add(matrix[i][left]);
  42. }
  43. // ++left;
  44. if(++left > right){
  45. break;
  46. }
  47. }
  48. return list;
  49. }
  50. }
易混淆点

1、我们要区分清楚边界遍历方向

例如,在遍历上边界时,实际上我们是从左向右遍历矩阵的第一行,遍历完成之后,我们需要让上边界下移

移动的是边界,遍历方向则是按顺时针方向来

2、边界的移动决定了遍历是否停止

如果某啷个边界相交,那么此时遍历就已经结束,必须立刻终止循环

二刷错误点

1、定义边界时,行列要分清楚

矩阵的长度是行,矩阵元素个数是列(想象一下)

  1. [[1,2,3],
  2. [4,5,6],
  3. [7,8,9]]

那么右边界right应该就是列减1,也就是matrix[0].length-1

而下边界down应该是行减1,也就是matrix.length-1

2、遍历是以"边界"为单位推进,不要老想着以某个具体的点为遍历的起始点,即不要寻找遍历初始点(重点问题)

这个错误是由于混淆了螺旋矩阵||的思路而产生的,没有明确本题所使用的方法是如何模拟“缩圈”的

注意:

  • 实际控制遍历指针的是循环变量i,它的值与当前左边界的定义值相同
  • 每当遍历完一个边界后,更新当前边界的值,那么下次遍历时所初始化的循环变量i也会相应的更新,进而改变了每次遍历的初始位置,也就模拟了不断收缩遍历圈大小的行为

遍历上边界时,我们是从左边界left开始,从左到右,到达右边界right结束;

  1. ·
  2. [[1,2,3],
  3. [4,5,6],
  4. [7,8,9]]

遍历右边界时,我们是从上边界top开始,从上到下,到达下边界down结束;

  1. [[1,2,3],↓
  2. [4,5,6],
  3. [7,8,9]]·

遍历下边界时,我们是从右边界right开始,从右到左,到达左边界left结束;

  1. [[1,2,3],
  2. [4,5,6],
  3. [7,8,9]]
  4. ·

遍历左边界时,我们是从下边界down开始,从下到上,到达上边界top结束;

  1. ·[[1,2,3],
  2. [4,5,6],
  3. [7,8,9]]

【LeetCode数组#5行为模拟】螺旋矩阵II+I的更多相关文章

  1. LeetCode(59):螺旋矩阵 II

    Medium! 题目描述: 给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵. 示例: 输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, ...

  2. 代码随想录训练营day 3|59.螺旋矩阵II 加 数组总结篇

    59.螺旋矩阵II 题目链接:59.螺旋矩阵II 题目描述:给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵. 示例: 输入: 3 输出: [ [ 1 ...

  3. 【LeetCode】59.螺旋矩阵II

    59.螺旋矩阵II 知识点:数组: 题目描述 给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix . 示例 输入:n = 3 ...

  4. Java实现 LeetCode 59 螺旋矩阵 II

    59. 螺旋矩阵 II 给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵. 示例: 输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ...

  5. leetcode 54. 螺旋矩阵 及 59. 螺旋矩阵 II

    54. 螺旋矩阵 问题描述 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素. 示例 1: 输入: [ [ 1, 2, 3 ], [ 4, 5, ...

  6. LintCode-381.螺旋矩阵 II

    螺旋矩阵 II 给你一个数n生成一个包含1-n^2的螺旋形矩阵 样例 n = 3 矩阵为 [     [ 1, 2, 3 ],     [ 8, 9, 4 ],     [ 7, 6, 5 ] ] 标 ...

  7. 【LeetCode-面试算法经典-Java实现】【059-Spiral Matrix II(螺旋矩阵II)】

    [059-Spiral Matrix II(螺旋矩阵II)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given an integer n, generate a ...

  8. 代码随想录第二天| 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II

    2022/09/22 第二天 第一题 这题我就直接平方后排序了,很无脑但很快乐啊(官方题解是双指针 第二题 滑动窗口的问题,本来我也是直接暴力求解发现在leetCode上超时,看了官方题解,也是第一次 ...

  9. [LeetCode] 59. Spiral Matrix II 螺旋矩阵 II

    Given an integer n, generate a square matrix filled with elements from 1 to n^2 in spiral order. For ...

  10. LeetCode 59. 螺旋矩阵 II(Spiral Matrix II)

    题目描述 给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵. 示例: 输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7 ...

随机推荐

  1. 慢SQL的致胜法宝

    大促备战,最大的隐患项之一就是慢SQL,对于服务平稳运行带来的破坏性最大,也是日常工作中经常带来整个应用抖动的最大隐患,在日常开发中如何避免出现慢SQL,出现了慢SQL应该按照什么思路去解决是我们必须 ...

  2. 使用JSON.stringify()去实现深拷贝,要小心哦,可能有巨坑

    对象中有时间类型的时候(时间类型会被变成字符串类型数据) const obj = { date: new Date() } console.log(typeof obj.date === 'objec ...

  3. 小程序之使用阿里字体图标 定义主题的颜色 控制首页标题的样式 如何使用组件 水平居中和垂直居中的方式 H5 关于上线后,

    项目搭建 1==> 需要创建的文件夹 styles 存放公共的样式 components 存放组件 lib第三方库的 utils 自己的帮助库 reques 自己的接口 2==>如何快速创 ...

  4. 替换 &开头。;结尾之间的内容。用空格代替他们

    替换 &开头.;结尾之间的内容.用空格代替他们 var regExp = /\&.*?\;/g; var str = '123&asdsa;dqwe'; str = str.r ...

  5. 如何处理开发环境没有问题,线上环境有问题这个bug

    解决思路 首先确认开发环境有没有这个问题: 如果没有这个问题: 将你的地址切换为线上的环境,看看线上环境有没有这个问题: 如果切换为线上环境有这个问题,就可以调试了: 如果切换为线上环境没有这个问题, ...

  6. 2021美亚杯团队赛write up

    个人赛与团队赛下载文件解压密码:MeiyaCup2021 加密容器解密密码: uR%{)Y'Qz-n3oGU`ZJo@(1ntxp8U1+bW;JlZH^I4%0rxf;[N+eQ)Lolrw& ...

  7. linux虚拟机固定ip

    1.查看宿主机IP信息 在windows宿主机上,键盘输入win+r,输出cmd,打开终端命令行: 输入ipconfig /all,查看宿主机IP信息: 2.修改Linux虚拟机的配置文件 Linux ...

  8. 从零开始匹配vim(2)——快捷键绑定

    如果说 vim有什么最吸引人,我想vim允许你自由的定义各种快捷键算是一个原因吧.你可以通过绑定各种快捷键来使经常使用的功能更加便利.通俗的讲,快捷键映射就是我按下某个键,我想让vim将它当成另一个键 ...

  9. yum 安装失败解决思路$releasever(curl#6 - "Could not resolve host: mirrorlist.centos.org; Unknown error")

    问题 公司使用刀片机的系统版本是CentOS 7.9.2009(Core),本人在重新安装虚拟机时,也使用对应的系统版本,在安装软件时,yum无法正常使用,一开始觉得,centos的release版本 ...

  10. 4.基于Label studio的训练数据标注指南:情感分析任务观点词抽取、属性抽取

    情感分析任务Label Studio使用指南 1.基于Label studio的训练数据标注指南:信息抽取(实体关系抽取).文本分类等 2.基于Label studio的训练数据标注指南:(智能文档) ...