leetcode刷题记录——数组篇

170 阅读2分钟

用js开始刷力扣,坚持是第一位!!!!俾啲心机啊崽!

数组总结:

  • 数组下标都是从0开始的。
  • 数组内存空间的地址是连续的,我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
  • 数组的元素是不能删的,只能覆盖。 方法有:
  1. 二分法
  2. 双指针法: 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作
  3. 滑动窗口法:滑动窗口的精妙之处在于调节右边界直到找到满足条件,不断调节子序列的左边界找到最优。
//滑动窗口模板(背诵)
    let left = 0, right = 0;
    while (right<nums.length) {
        //不论如何窗口都是要一直增大的
        窗口数据更新
        right++;//不断扩充右边界
        while (满足缩小左边界的条件) {
             //更新数据结果
             left++;//左窗口更新
        }
    }

  1. 模拟行为

image.png

思路:

  • 当题目中有数组为有序数组,且数组中无重复元素时,应当敏锐地想到用二分法
  • 注意边界问题,此处我们选择左闭右闭,即[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;
};

image.png

思路:

  • 通过双指针法,一个快指针和慢指针,在一个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;
};

image.png 思路:

  • 由于题目要求递增顺序返回结果
  • 运用双指针法,一个在头一个在尾,申明一个填充为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;
};

image.png

思路:

  • 套滑动窗口模板,有两个边界,一个左边界,一个右边界,开始的时候,左右边界都指向数组的首位置
  • 由于目标是找出大于或等于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;
};

image.png

思路:

image.png

注意事项:

  • 坚持左开右闭的原则
  • 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;
};