提示:努力生活,开心、快乐的一天
题目链接:977.有序数组的平方
暴力解法
:每个数平方后进行排序
时间复杂度高:O(nlogn)
双指针解法
:数组为 非递减顺序整数数组,符合双指针的使用规则。
思考两个指针如何使用?
因为数组中是非递减整数数组,可能存在负数,所以平方以后,最大值不是在最左边就是在最右边,肯定不会在中间,所以可以 将两个指针放在数组的开头与结尾
定义一个新数组, 起始位置(i)
与 结束位置(j)
平方处理后进行比较,哪个数值大,将哪个值放在新数组的末端(新数组,要求也按 非递减顺序 排序),然后将该指针进行移动, 如果i大, 移动i,即i++
;如果j大, 移动j,即j--
。
各种排序忘了,需要重新复习,此次暴力解法使用了sort()函数
新数组每次获取到新的值,都需要对新数字的下标➖1,即(k–)
暴力解法
var sortedSquares = function (nums) {
//暴力解法
for (let i = 0; i < nums.length; i++) {
nums[i] = nums[i] * nums[i]
}
let newArr = nums.sort((a, b) => { return a - b })
return newArr
};
双指针解法
var sortedSquares = function (nums) {
//双指针解法
let i = 0;
let k = j = nums.length - 1;
let arr = new Array(nums.length).fill(0);
while (i <= j) {
let left = nums[i] * nums[i]
let right = nums[j] * nums[j]
if (left < right) {
arr[k--] = right;
j--;
} else {
arr[k--] = left;
i++
}
}
return arr
};
非递减顺序整数数组,我理解为递增顺序整数数组
,另外该题是对平方值进行排序,其实也就是绝对值
,那最大的值一定在数组的两端
,所以双指针分别放在数组两端进行,另外重新定义一个新的数据
,来承接两个指针对比出的结果,即两者相对大的值
,与暴力解法对比,相当于用空间换时间啦!
题目链接:209.长度最小的子数组
暴力解法
:双层for循环,第一层,子数组的末端;第二层循环,子数组的前端,时间复杂度: O(N^2),空间复杂度:O ( 1 )双指针解法
:定义前后两个指针,i
指针代表子数组的最后一个元素,j
指针代表子数组的第一个元素,所以这也决定了 i 会比j走的更快
,i每走一步,都向sum(子数组的和)中添加该元素,即 nums[i]
,当 sum>=targe
t时,获取此时子数组的长度,即 j-i+1
;然后sum减去nums[j], j
指针前进一步,即 j++
暴力解法
:在第一层循环中,第二层循环前,没有将sum的值归0双指针解法
:获取到一个子数组的长度后,没有将sum总值减去子数组的第一位,也没有将j指针后移一位暴力解法
var minSubArrayLen = function (target, nums) {
//暴力解法
let sum = 0;//子数组的和
let result = Infinity;//子数组的长度,此处定义一个无穷大
for (let i = 0; i < nums.length; i++) {
sum = 0//错误点
for (let j = i; j < nums.length; j++) {
sum += nums[j]
if (sum >= target) {
let newLen = j - i + 1;
console.log(newLen, 'newLen')
result = Math.min(result, newLen)
break;
}
}
}
return result === Infinity ? 0 : result;
};
双指针解法
var minSubArrayLen = function (target, nums) {
//双指针
let i = j = 0;//两个指针,i表示头指针,j表示尾指针
let sum = 0;//子数组的和
let result = Infinity;//子数组的长度,此处定义一个无穷大
for (; i < nums.length; i++) {
sum += nums[i]
while (sum >= target) {
let newLen = i - j + 1;
result = Math.min(result, newLen);
sum -= nums[j]//犯错的地方-忘记是while循环,没有给循环条件
j++//犯错的地方
}
}
return result === Infinity ? 0 : result;
};
双指针用法,在此处也称为滑动窗口,所谓滑动窗口
,就是不断的调节子序列的起始位置和终止位置
,从而得出我们要想的结果。
实现滑动窗口,主要确定如下三点:
窗口内是什么?
如何移动窗口的起始位置?
如何移动窗口的结束位置?
题目链接:59.螺旋矩阵II
循环不变量原则
填充上行从左到右
;填充右列从上到下
;填充下行从右到左
;填充左列从下到上
var generateMatrix = function (n) {
let startX = 0;
let startY = 0;//起始位置
let loop = Math.floor(n / 2);//需要转的圈数
let mid = Math.floor(n / 2);//中间位置;n为奇数时
let count = 1;//填充的数据
let offset = 1;//因为遵循左闭右开原则,所以每一边有会少遍历1个值,第二圈就是少遍历1+2个
let res = new Array(n).fill(0).map(() => new Array(n).fill(0));//创建1个二维数组
while (loop--) {
let i = startX;
let j = startY;
//上行,左——>右,左闭右开
//需要从起始位置走,所以需要+startX
for (; j < startY + n - offset; j++) {
res[i][j] = count++
}
//右列,上——>下,左闭右开
for (; i < startX + n - offset; i++) {
res[i][j] = count++
}
//下行,右——>左,左闭右开
for (; j > startY; j--) {
res[i][j] = count++
}
//左列,下-—>上,左闭右开
for (; i > startX; i--) {
res[i][j] = count++
}
startX++
startY++
offset += 2//上一圈的前后两个元素
}
if (n % 2 === 1) {
res[mid][mid] = count
}
return res
};
边界值
offset的取值
理解:一开始设为1
,是因为遵循左闭右开原则,所以每一边有会少遍历1个值,见下图;后来一圈结束,offset+=2
,是因为第二圈开始的时候,外层已经有一圈了,外层这一圈有左右(上下)两个值不需要去遍历。上行和右列为什么需要在判断条件中+startX和startY?
因为这两个的代表每一圈的起始 位置,所有的遍历都是从其实位置开始的,第一圈的时候两个其实位置为(0,0)
,即便不加 可能也没影响,但 第二圈起始位置为(1,1)
,不加的话就会报错好累,今天好忙
今天的题,挺难的,cpu都干烧了,不过也很有收获,理解不好,那就多看几遍,多写几遍 ,一定会有所收获,数组的总结周末再补上吧,加油,欣欣崽