螺旋矩阵II

力扣题目链接(opens new window)

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

示例:

输入: 3 

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

初见思路

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

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

常规思路

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

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

也就是行为模拟题

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

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

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

引入循环不变量原则

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

解题模板

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

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

c++版

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

二刷问题:

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

Java版

ps:

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

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

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

拓展练习

LeetCode54螺旋矩阵
思路

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

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

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

Java版(错误)

class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
/*
int startIndex = 0;//也可以这样写,考虑到正方形矩阵,这俩值是相同的,所以可以只用一个变量表示
*/
List<Integer> list = new ArrayList<Integer>();//定义动态数组,存放遍历值 //获取数组的行和列的数值
int m = matrix.length;//行
int n = matrix[0].length;//列 //定义上下左右边界
int left = 0;
int rigth = n - 1;
int top = 0;
int bottom = m -1; //循环遍历
while(true){
//遍历上边界所在的行,从左往右(left->right),起始点为[left,top]
for(int i = left; i < rigth; i++){
list.add(matrix[top][i]);
if(top > bottom){
break;//越界直接结束遍历
}else{
top++;//上边界向下移
}
//精简写法:
// if(++top){
// break;
// }
}
//遍历右边界所在的列,从上往下(top->bottom),起始点为[right,top]
for(int i = top; i < bottom; i++){
list.add(matrix[i][rigth]);
if(rigth < left){
break;
}else{
rigth--;//右边界向左移
}
}
//遍历下边界所在的行,从右往左(right->left),起始点为[right,bottom]
for(int i = rigth; i > left; i--){
list.add(matrix[bottom][i]);
if(bottom < top){
break;
}else{
bottom--;//下边界向上移
}
}
//遍历左边界所在的列,从下往上(bottom->top),起始点为[left,bottom]
for(int i = bottom; i > top; i--){
list.add(matrix[left][i]);
if(left > rigth){
break;
}else{
left++;//左边界向右移
}
}
}
return list;
}
}

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

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

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

上边界遍历得到(下移至第二行):

1、2、3、4

右边界遍历得到(左移至第三列):

8、12

下边界遍历得到(上移至第二行):

11、10、9

左边界遍历得到(右移至第二列):

5

上边界遍历得到(下移至第三行,于下边界相交,遍历结束):

6、7

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

题解

c++版

class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
//获取矩阵的行、列大小
int m = matrix[0].size();//行
int n = matrix.size();//列 //设定上下左右四个边界
int left = 0;
int right = m - 1;//注意是行还是列
int top = 0;
int down = n - 1; vector<int> res;//保存遍历值
//循环取值
while(true){
for(int i = left; i <= right; ++i) res.push_back(matrix[top][i]);//遍历上边界,从左到右(从左边界出发到右边界结束)
top++;//遍历完一条边界后,边界移动
if(top > down) break; for(int i = top; i <= down; ++i) res.push_back(matrix[i][right]);//遍历右边界,从上到下(从上边界出发到下边界结束)
right--;//遍历完一条边界后,边界移动
if(right < left) break; for(int i = right; i >= left; --i) res.push_back(matrix[down][i]);//遍历下边界,从右往左(从右边界出发到左边界结束)
down--;//遍历完一条边界后,边界移动
if(down < top) break; for(int i = down; i >= top; --i) res.push_back(matrix[i][left]);////遍历左边界,从下往上(从下边界出发到上边界结束)
left++;//遍历完一条边界后,边界移动
if(left > right) break;
}
return res;
}
};

二刷问题:

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

Java版代码如下

class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
int m = matrix.length;//行
int n = matrix[0].length;//列 //设定四个边界
int left = 0;//左边界
int right = n - 1;//右边界
int top = 0;//上边界
int down = m - 1;//下边界
List<Integer> list = new ArrayList<Integer>(); while(true){
//遍历上边界,从左(边界)到右(边界)
for(int i = left; i <= right; i++){
list.add(matrix[top][i]);
}
// ++top;//上边界下移(先自增)
// if(top > down){//如果上边界超过下边界,结束遍历
// break;
// }
if(++top > down){//如果上边界超过下边界,结束遍历
break;
} //遍历右边界,从上(边界)到下(边界)
for(int i = top; i <= down; i++){
list.add(matrix[i][right]);
}
// --right;//右边界左移
if(--right < left){//同理
break;
} //遍历下边界,从右(边界)往左(边界)
for(int i = right; i >= left; i--){
list.add(matrix[down][i]);
}
// --down;
if(--down < top){
break;
} //遍历左边界,从下(边界)往上(边界)
for(int i = down; i >= top; i--){
list.add(matrix[i][left]);
}
// ++left;
if(++left > right){
break;
}
}
return list; }
}
易混淆点

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

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

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

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

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

二刷错误点

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

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

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

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

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

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

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

注意:

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

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

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

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

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

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

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

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

·[[1,2,3],
[4,5,6],
↑ [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. [转帖]Redis Scan 原理解析与踩坑

    https://www.cnblogs.com/jelly12345/p/16424080.html 1. 概述由于 Redis 是单线程在处理用户的命令,而 Keys 命令会一次性遍历所有 Key, ...

  2. Linux 下面删除指定日期之前文件的办法

    1. Linux 下面最近有一个需求 需要只更新2020年4月10号之后补丁的需求 2. rsync 能够拉取所有的补丁文件  没找到能够按照日期进行拉取的办法. 所以想了一个折中的办法 先拉取 再按 ...

  3. K8S 使用loki 监控 应用日志的搭建办法

    1. 背景 这几天一直在用k8s部署分SU的测试环境,开发反馈看日志比较麻烦. 昨天晚上在家里本来想搭建ELK 发现比较重, 又说有一个比较轻量级的 loki 可以实现使用grafana进行监控和查看 ...

  4. windows10卸载小娜

    适用于2004版本往后的 win+x如图 输入如下代码 Get-AppxPackage-allusersMicrosoft.549981C3F5F10|Remove-AppxPackage 运行结束后 ...

  5. 热门数据集提供【MNIST、鸢尾花、猫狗、CIFAR10、vegetables、Ox-Flowers17、pascalvoc】

    热门数据集提供[MNIST.鸢尾花.猫狗.CIFAR10.vegetables.Ox-Flowers17.pascalvoc] 简介: 鸢尾花数据集: 约150条数据,每条样本4个属性,共3个类别 M ...

  6. 7.5 Windows驱动开发:监控Register注册表回调

    在笔者前一篇文章<内核枚举Registry注册表回调>中实现了对注册表的枚举,本章将实现对注册表的监控,不同于32位系统在64位系统中,微软为我们提供了两个针对注册表的专用内核监控函数,通 ...

  7. 最新力作,爱来自rand函数

    AWCU47EF;D5F]ET[a8a9K6G5IRHB6RS\cD8YDC:AGN<Z@6ZI3ab8D3O3La7:Sc;5_B]BS5S6Q]baWGcTE94IX7cW=9F>BJ ...

  8. 7000字详解Spring Boot项目集成RabbitMQ实战以及坑点分析

    本文给大家介绍一下在 Spring Boot 项目中如何集成消息队列 RabbitMQ,包含对 RibbitMQ 的架构介绍.应用场景.坑点解析以及代码实战.最后文末有免费领取龙年红包封面以及腾讯云社 ...

  9. 协程(Python)

    一.gevent #__author__:Kelvin #date:2020/5/13 13:34 from gevent import monkey monkey.patch_all() impor ...

  10. 如何在Visual Studio新C++项目中调用之前配置过的库?

      本文介绍在Visual Studio软件中调用C++各种配置.编译完毕的第三方库的方法.   在撰写C++代码时,如果需要用到他人撰写的第三方库(例如地理数据处理库GDAL.矩阵运算库Armadi ...