977.有序数组的平法
文章链接
第一想法
题目要求是在有序数组中每个元素平方后从小到大排列,首先我想到了sort函数,代码如下:
var sortedSquares = function(nums) {
for(let i=0;i<nums.length;i++){
nums[i]=nums[i]**2
}
nums.sort((a,b)=>a-b)
return nums
}
for循环的时间复杂度为,而sort函数的时间复杂度为,所以时间复杂度为,即
代码质量
看完文章后讲解的思路
文章通过双指针的方法来解决这个问题,由于平方后绝对值越接近0的元素越小,数组两侧的元素越来越大,所以从两侧向中间循环,从大到小填满数组:
var sortedSquares = function(nums) {
let len=nums.length
let res=new Array(len).fill(0)
for(let i=0,j=nums.length-1;i<=j;){
if(nums[i]**2<nums[j]**2){
res[len-1]=nums[j]**2
len--
j--
}else{
res[len-1]=nums[i]**2
i++
len--
}
}
return res
};
由于只有一个for循环,所以时间复杂度为
代码质量
思考
对于我来说双指针写法不容易想到,但是双指针写法一般可以把二维变为一维,让时间复杂度变小,从或者等变为,总体上来说还是对双指针写法不熟练。
209. 长度最小的子数组
文章链接
第一想法
看到这道题,我首先想到了滑动窗口(不理解滑动窗口的可以看卡哥的这个视频),不断改变起始位置和末尾位置,使得sum>=target,通过比较滑动窗口的大小来寻找最小值。代码如下:
var minSubArrayLen = function(target, nums) {
let j=0 //滑动窗口的起始位置
let sum=0 //总和
let len=Number.MAX_SAFE_INTEGER //长度初始为最大值 滑动窗口的大小
for(let i=0;i<nums.length;i++){
sum+=nums[i] //算滑动窗口的总和
if(sum>=target){ //如果sum>=target就比较记录最小长度
len=Math.min(i-j+1,len) //比较记录最小长度
while((sum-nums[j]>=target)){ //这里是我想多了,我这里保证了sum>=target,其实可以不用,想多了(流汗)
len=Math.min(i-j,len)
sum-=nums[j]
j++
}
}
}
return len==Number.MAX_SAFE_INTEGER?0:len//这里是防止整个nums的和也小于target,小于时应该返回0
};
注意时间复杂度是,我以为是,这里留个坑,等我找找对应的文章看看(流汗)
代码质量
看完文章后讲解的思路
emmm,这道题我居然暴力没想到(大意了没有闪),看了文章的思路,发现和我都第一想法类似,但是又优化了我的想法,代码如下:
var minSubArrayLen = function(target, nums) {
let j=0
let sum=0
let len=Number.MAX_SAFE_INTEGER
for(let i=0;i<nums.length;i++){
sum+=nums[i]
while(sum>=target){ //这里直接while就可以,不用想那么多,因为i~j如果大于target,则i~j+n肯定大于target,只需要改变初始位置就可以
len=Math.min(i-j+1,len)
sum-=nums[j]
j++
}
}
return len==Number.MAX_SAFE_INTEGER?0:len
};
代码质量:
思考
这道题主要考察滑动窗口,需要理解如何设置滑动窗口的起始位置和终止位置,而且要想清理念,这道题我是想到了,但是想多了,没有完全想清楚,所以遇到这种得还要思路理一理,还遇到了问题,我以为for循环套一层while的时间复杂度为,结果发现代码随想录中时间复杂度为,今天事情比较多,等我看看相关文章再把这个坑补掉。
59. 螺旋矩阵 II
文章讲解
第一思考
看到这题的第一眼发现已知的方法都不能用,那咋办。只好老老实实模拟呗,按照题目要求一圈一圈的模拟,需要判断哪个点是拐点,想法是先把res都设置为0,只要超过边界或者下一个位置不是0,则需要拐弯,尝试一下,发现可以通过:
var generateMatrix = function(n) {
let res=new Array(n)
for(let i=0;i<n;i++){
res[i]=new Array(n).fill(0)
}
res[0][0]=1//初始化
let dis=[[0,1],[1,0],[0,-1],[-1,0]] //设置拐点
let num1=0
let num2=0
let count=0
for(let i=2;i<=n**2;i++){
if(res?.[num1+dis[count][0]]?.[num2+dis[count][1]]==undefined||res?.[num1+dis[count][0]]?.[num2+dis[count][1]]!=0) count=(count+1)%4; //如果碰到边界或者下一个位置是0,则count改变(也就是拐点)
res[num1+=dis[count][0]][num2+=dis[count][1]]=i //在一行或一列填充数字
}
return res
};
感觉js在这里用起来很方便,解决了拐点的一大难题
代码质量
看完文章后讲解的思路
代码随想录中对于这道题也是模拟,发现还是大佬模拟的好,条件比较清晰,附一张代码随想录当中的图:
通过按圈循环来填充数字,代码中的offset就是来控制每一边的填充个数,通过图可以看出,每一行或者每一列的最后一个会作为下一行或者下一列的开头,也就是左闭右开。代码以及讲解如下:
var generateMatrix = function(n) {
let res=new Array(n)
for(let i=0;i<n;i++){ //初始化
res[i]=new Array(n).fill(0)
}
let num=Math.floor(n/2) //判断转几圈
let mid=Math.floor(n/2)//如果n为奇数,则最中间的下标为[mid][mid]
let offset=1 //因为按圈算,所以每圈都需要设置每一边的减量(控制每一圈的填入的个数)
//设置起始位置
let startx=0
let starty=0
//设置填充的数字
let count=1
while(num--){//按圈数循环填充
let i=startx;
let j=starty;
for(;j<starty+n-offset;j++){ //上行从左到右 这里是n-offset+starty的原因是每行或每列的最后一个位置算下一条边的第一个位置 offset控制每行的个数,starty用于确定每行的起始位置,从起始位置开始填充n-offset位
res[i][j]=count++
}
for(;i<startx+n-offset;i++){ //右行从上到下
res[i][j]=count++
}
for(;j>starty;j--){ //下行从右到左 //这里大于starty是因为返回途中填充的数字的下标一定要大于起始位置的下标
res[i][j]=count++
}
for(;i>startx;i--){ //左行从下到上
res[i][j]=count++
}
//更新起点坐标
starty++
startx++
//更新offset 循环以前填充个数应该减两个(左右各一个)
offset+=2
}
if(n%2==1) res[mid][mid]=count
return res
};
代码质量
思考
对于模拟问题我练习的不多,遇到的也不知道能不能解出来,这道题能够解出来是利用了js的优势,以后遇到这种题首先还是要认真思考,如何进行模拟以及模拟要有一定的规律,要把各个条件搞清。同时一定要注重循环不变量原则
总结
今天是三道关于数组的题,分别考察了双指针,滑动窗口以及模拟行为,总得来说每道题都做出来了,但有些代码的思路或者写法不完全正确,双指针的题老是想不到用双指针,一般用其他方法解决后才后知后觉可以用双指针,滑动窗口对我来说还是比较陌生的,这次算是初次较量。模拟行为听完卡哥视频讲的之后就清晰了很多,也留下了一个坑,对于时间复杂度的分析。不算写博客今天花费的时间大概2.30小时吧。