用js开始刷力扣,坚持是第一位!!!!俾啲心机啊崽!
数组总结:
- 数组下标都是从0开始的。
- 数组内存空间的地址是连续的,我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
- 数组的元素是不能删的,只能覆盖。 方法有:
- 二分法
- 双指针法: 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作
- 滑动窗口法:滑动窗口的精妙之处在于调节右边界直到找到满足条件,不断调节子序列的左边界找到最优。
//滑动窗口模板(背诵)
let left = 0, right = 0;
while (right<nums.length) {
//不论如何窗口都是要一直增大的
窗口数据更新
right++;//不断扩充右边界
while (满足缩小左边界的条件) {
//更新数据结果
left++;//左窗口更新
}
}
- 模拟行为
思路:
- 当题目中有数组为有序数组,且数组中无重复元素时,应当敏锐地想到用二分法
- 注意边界问题,此处我们选择左闭右闭,即[left, right]
- while(left<=right) 要使用 <=
- if (nums[mid] < target)中left要赋值为 mid + 1
注意事项:
- if(nums[mid] === target)要用===
- Math.floor((right - left)/2),题解里有人用了parseInt((right - left) / 2),也有人用(right - left >> 1),之所以不用Math.floor((right - left)/2),只是因为>>移位运算比除法操作性能好很多,另外就是考虑到大数溢出的情况
var search = function(nums, target) {
let left = 0, right = nums.length - 1;
while(left<=right) {
let mid = Math.floor((right - left)/2) + left;
if(nums[mid] === target){
return mid;
} else if (nums[mid] < target){
left = mid + 1;
} else {
right = mid -1;
}
}
return -1;
};
思路:
- 通过双指针法,一个快指针和慢指针,在一个for循环下完成两个for循环的工作
- 快指针
fast指向当前将要处理的元素,慢指针left指向下一个将要赋值的位置。 - 整个过程保持不变的性质是:区间[0,left) 中的元素都不等于 val 。当左右指针遍历完输入数组以后, slow 的值就是输出数组的长度。
注意事项:
- nums[slow++] 写法更简便,记住这种写法。
优化:如果要移除的元素恰好在数组的开头,例如序列 [1,2,3,4,5][1,2,3,4,5],当 val 为 11 时,我们需要把每一个元素都左移一位。因为题目中说了元素的顺序可以改变,我们可以将最后的5直接跟1进行互换得到数组[5,2,3,4],这个优化在序列中 val 元素较少时非常有效,避免了大量的移动。实现:依然使用双指针,两个指针初始时分别位于数组的首尾,向中间移动遍历该序列。当左指针 left 和右指针 right 重合的时候,左右指针遍历完数组中所有的元素。
//时间复杂度:O(n)
//空间复杂度:O(1)
var removeElement = function(nums, val) {
let slow = 0;
for(let fast = 0; fast<nums.length;fast ++){
if(nums[fast] !== val){
nums[slow++] = nums[fast];
}
}
return slow;
};
思路:
- 由于题目要求递增顺序返回结果
- 运用双指针法,一个在头一个在尾,申明一个填充为0的数组,比较两个数字,哪个最大,放哪个在队尾。
注意事项:
- 除了
let res = new Array(k+1).fill(0), 还可以用let result = []声明一个空数组,由于放是在队首放,而不是在队尾放,所以我们需要用unshift
var sortedSquares = function(nums) {
let i = 0, j = nums.length - 1, k = nums.length - 1;
let res = new Array(k+1).fill(0);
while(i <= j){
if(nums[i] * nums[i] < nums[j] * nums[j]){
res[k--] = nums[j] * nums[j];
j--;
} else {
res[k--] = nums[i] * nums[i];
i++;
}
}
return res;
};
思路:
- 套滑动窗口模板,有两个边界,一个左边界,一个右边界,开始的时候,左右边界都指向数组的首位置
- 由于目标是找出大于或等于target的最短数组;用一个值来记录窗口内数字的和 如果两个指针之间的子数组中所有数字之和小于target,那么把右边界向右移动
- 滑动窗口的总体思路是先移动右边界,让窗口中的值满足题目的解,也在是说在找到可行解的情况下
- 最后在可行解里面寻找最优解,缩小窗口的时机是,当sum >= target,此时移动左边界
注意事项:
- 注意res初值
res = nums.length+1;
var minSubArrayLen = function(target, nums) {
let left = 0, right = 0;
let sum = 0, res = nums.length+1;
while (right<=nums.length - 1) {
sum += nums[right++];//第一层while不断扩充右边界使sum大于target
while (sum >= target) {//第二层while不断缩小左边界使长度最小
res = res < right - left ? res : right - left;
sum -= nums[left++];
}
}
return res > nums.length ? 0 : res;
};
思路:
注意事项:
- 坚持左开右闭的原则
- for (; col < startY + n - offset; col++) 第一个参数省略,所有for循环共享同一个 row 和 col
var generateMatrix = function(n) {
let startX = startY = 0; // 起始位置
let loop = Math.floor(n/2); // 旋转圈数
let mid = Math.floor(n/2); // 中间位置
let offset = 1; // 控制每一层填充元素个数
let count = 1; // 更新填充数字
let res = new Array(n).fill(0).map(() => new Array(n).fill(0));
while (loop--) {
let row = startX, col = startY;
// 上行从左到右(左闭右开)
for (; col < startY + n - offset; col++) {
res[row][col] = count++;
}
// 右列从上到下(左闭右开)
for (; row < startX + n - offset; row++) {
res[row][col] = count++;
}
// 下行从右到左(左闭右开)
for (; col > startX; col--) {
res[row][col] = count++;
}
// 左列做下到上(左闭右开)
for (; row > startY; row--) {
res[row][col] = count++;
}
// 更新起始位置
startX++;
startY++;
// 更新offset
offset += 2;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if (n % 2 === 1) {
res[mid][mid] = count;
}
return res;
};
另一种个人认为更好的解法
var generateMatrix = function(n) {
let up = 0, down = n - 1, left = 0, right = n - 1, count = 1;
let res = new Array(n).fill(0).map(() => new Array(n).fill(0));
while (count <= n*n) {
for(let i = left; i <= right; i++){
res[up][i] = count++;
}
up++;
for(let i = up; i <= down; i++){
res[i][right] = count++;
}
right--;
for(let i = right; i >= left; i--){
res[down][i] = count++;
}
down--;
for(let i = down; i >= up; i--){
res[i][left] = count++;
}
left++;
}
return res;
};