【LeetCode数组#5行为模拟】螺旋矩阵II+I
螺旋矩阵II
给定一个正整数 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的更多相关文章
- LeetCode(59):螺旋矩阵 II
Medium! 题目描述: 给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵. 示例: 输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, ...
- 代码随想录训练营day 3|59.螺旋矩阵II 加 数组总结篇
59.螺旋矩阵II 题目链接:59.螺旋矩阵II 题目描述:给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵. 示例: 输入: 3 输出: [ [ 1 ...
- 【LeetCode】59.螺旋矩阵II
59.螺旋矩阵II 知识点:数组: 题目描述 给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix . 示例 输入:n = 3 ...
- Java实现 LeetCode 59 螺旋矩阵 II
59. 螺旋矩阵 II 给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵. 示例: 输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ...
- leetcode 54. 螺旋矩阵 及 59. 螺旋矩阵 II
54. 螺旋矩阵 问题描述 给定一个包含 m x n 个元素的矩阵(m 行, n 列),请按照顺时针螺旋顺序,返回矩阵中的所有元素. 示例 1: 输入: [ [ 1, 2, 3 ], [ 4, 5, ...
- LintCode-381.螺旋矩阵 II
螺旋矩阵 II 给你一个数n生成一个包含1-n^2的螺旋形矩阵 样例 n = 3 矩阵为 [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ] 标 ...
- 【LeetCode-面试算法经典-Java实现】【059-Spiral Matrix II(螺旋矩阵II)】
[059-Spiral Matrix II(螺旋矩阵II)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given an integer n, generate a ...
- 代码随想录第二天| 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II
2022/09/22 第二天 第一题 这题我就直接平方后排序了,很无脑但很快乐啊(官方题解是双指针 第二题 滑动窗口的问题,本来我也是直接暴力求解发现在leetCode上超时,看了官方题解,也是第一次 ...
- [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 ...
- LeetCode 59. 螺旋矩阵 II(Spiral Matrix II)
题目描述 给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵. 示例: 输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7 ...
随机推荐
- Python学习之三: 编译二进制
Python学习之三: 编译二进制 摘要 每次使用python 执行py文件其实是比较麻烦的 主要是还得安装python的虚拟机,以及安装对应的pip包. 感觉比较繁杂 理论上最快捷的方式是编译成 二 ...
- [转帖]ipset命令介绍与基本使用
一. 介绍 ipset命令是用于管理内核中IP sets模块的,如iptables之于netfilter.ipset字面意思是一些IP地址组成一个集合(set).但是ipset用于用于存储IP地址,整 ...
- [转帖]linux中top命令显示不全怎么解决
https://www.yisu.com/zixun/697775.html 这篇"linux中top命令显示不全怎么解决"文章的知识点大部分人都不太理解,所以小编给大家总结了以下 ...
- 编译打包rabbitmq然后一键部署的简单方法
摘要 之前总结过一版,但是感觉不太全面 想着本次能够将使用中遇到的问题总结一下. 所以本次是第二版 介质下载 rabbitmq 不区分介质的打包文件 rabbitmq-server-generic-u ...
- 给无网络的CentOS服务器下载rpm包的一个解决办法
很多公司的服务器为了安全都在内网, 是无法直接连接互联网的, 无法连接互联网就无法使用yum等的包管理器安装rpm包等. 有时候一些rpm包还是能很好的提高性能的, 所以可以使用多种方式获取rpm包进 ...
- 【JS 逆向百例】房天下登录接口参数逆向
声明 本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 目标:房天下账号密码登录 主页:https://passpo ...
- svn忽略某个目录后update出现fetching
忽略某个子目录 在svn udpate一个大目录时忽略特定的子目录,主要是子目录下内容已经单独拉取过,并且这个大目录对于程序来说,可以是只读的. 操作方法:选中要忽略的目录,右键 svn - Unve ...
- SqlSugar更新数据
1.根据实体对象更新 所谓按实体对象更新就是:db.Updateable(参数对象) 有参数的重载 db.Updateable(实体或者集合).ExecuteCommand() //右标题1 下面的所 ...
- Paddlenlp之UIE模型实战实体抽取任务【打车数据、快递单】
项目连接:可以直接fork使用 Paddlenlp之UIE模型实战实体抽取任务[打车数据.快递单] 0.背景介绍 本项目将演示如何通过小样本样本进行模型微调,快速且准确抽取快递单中的目的地.出发地.时 ...
- vue实现简易瀑布流
实现效果 1.不同屏幕尺寸下根据可视区域宽度判断 应该 放几列,这里用onresize演示 2.鼠标滚动到已加载数据的底部时再次请求数据,重新计算哪一列高度最小,push到最小的那一列 实现思路 1. ...