【代码随想录-刷题学习JavaScript】day1-数组

按照代码随想录进行学习数组部分:(建议分两天学习)
一、数组理论基础
二、LeetCode 704. 二分查找
三、LeetCode 27. 移除元素
四、LeetCode 977.有序数组的平方
五、LeetCode 209.长度最小的子数组
六、LeetCode 59.螺旋矩阵II
七、数组总结

一、数组理论基础

文章链接

数组是存放在连续内存空间上的相同类型数据的集合。
下标索引的方式获取到下标下对应的数据。

特点:
1.下标都是从0开始的。
2.内存空间的地址是连续的。
是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址;
是连续的,所以数组的元素是不能删的,只能覆盖。

二、LeetCode 704. 二分查找

文章链接

手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找

熟悉根据 “左闭右开”,“左闭右闭” 两种区间规则 写出来的二分法
其实是一种定边界的思想,能够很好的帮助我们确定<还是<=,以及+1或者-1。
定了一种思想,就从一而终。

【左闭右开】

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    let left= 0;
    let right = nums.length;
    while (left < right) {
        let middleIndex = left + (right - left >> 1);
        let middle = nums[middleIndex];
        if (middle < target) {
            left = middleIndex + 1;
        } else if (middle > target) {
            right = middleIndex;
        } else {
            return middleIndex;
        } 
    }
    return -1;
};

【左闭右闭】

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    let left= 0;
    let right = nums.length - 1;
    while (left <= right) {
        let middleIndex = left + (right - left >> 1);
        let middle = nums[middleIndex];
        if (middle < target) {
            left = middleIndex + 1;
        } else if (middle > target) {
            right = middleIndex - 1;
        } else {
            return middleIndex;
        } 
    }
    return -1;
};

三、LeetCode 27. 移除元素

文章讲解

数组中移除元素并不容易! | LeetCode:27. 移除元素

题目比较简单,但是比较考验对数组底层原理理论的理解。
其实数组物理空间是无法删除的,只是后一位移动覆盖。

题目建议: 暴力的解法,可以锻炼一下我们的代码实现能力,建议先把暴力写法写一遍。
双指针法 是本题的精髓。 库函数是不推荐在学算法时候使用的。

【暴力解】
一层for循环用于遍历数组元素
二层for循环用于更新数组

/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function(nums, val) {
    let size = nums.length;
    for (let i = 0; i < size; i++) {
        if (nums[i] === val) {
            for (let j = i + 1; j < size; j++) {
                nums[j-1] = nums[j];
            }
            i--;
            size--;
        }
    }
    return size;
};

【双指针】
快慢指针,均是从0开始
slow慢指针:用于指向更新后的数组的下标的位置(“坑”)
quick快指针:用于寻找新数组中的元素(“萝卜”)

/**
 * @param {number[]} nums
 * @param {number} val
 * @return {number}
 */
var removeElement = function(nums, val) {
    let slow = 0;
    for (let quick = 0; quick < nums.length; quick++) {
        if (nums[quick] != val) {
            nums[slow] = nums[quick];
            slow++;
        }
    }
    return slow;
};

四、LeetCode977.有序数组的平方

文章讲解

数组有序,但是数组中有负数,可能负数平方之后就成最大数了。但是原数组按原来的位置每个数平方之后,大数一定在两边(也就是由大到小的趋势一定是向中间合拢的)。
所以可以用 双指针O(n) 的解法。 i: 起始位置,j: 终止位置。
i <= j 循环就可以一直继续下去:(i = j时候指向的元素也是需要更新的)
i++ 或 j- - 是有条件的,也就是下标对应元素较大的那一方,放入最后结果数组中后,再对应那一方i++或j- -
res数组是从最后往前排的(后大前小——从大到小排序),所以res的下标是一直- -的

双指针法经典题目 | LeetCode:977.有序数组的平方

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortedSquares = function(nums) {
    let i = 0; 
    let j = nums.length - 1;
    let k = nums.length - 1;
    let res = new Array(nums.length).fill(0);

    while (i <= j) {
        let left = nums[i] * nums[i];
        let right = nums[j] * nums[j];
        if (left < right) {
            res[k--] = right;
            j--;
        } else {
            res[k--] = left;
            i++;
        }
        // 如果直接没在res[k--],也可以在这k--
    }
    return res;
};

五、LeetCode 209.长度最小的子数组

输入:n个正整数的数组。正整数target
输出: 数组中和>=target的长度最小的连续子数组[num1, nums2…],并返回其长度(若不存在子数组则返回0)

数组target输出解释
[2, 3, 1, 2, 4, 3]72子数组是[4. 3]
[1, 4, 4]41子数组是[4]
[1, 1, 1, 1, 1, 1, 1, 8]110无子数组

题目建议: 本题关键在于理解滑动窗口,这个滑动窗口看文字讲解还挺难理解的,建议大家先看视频讲解。

拿下滑动窗口! | LeetCode 209 长度最小的子数组

文章讲解
滑动窗口O(n)(本质上还是双指针)
精髓在于:如何移动起始位置
外层循环不断移动的是 右指针的位置right
而left指针在动态不断移动(如何移动呢?一旦符合取res数组长度的条件:sum >= target,更新最后结果后,就可以left++了)
res数组的长度,最开始是最大值,在左指针的不断移动下逐渐缩小

/**
 * @param {number} target
 * @param {number[]} nums
 * @return {number}
 */
var minSubArrayLen = function(target, nums) {
    let left, right;
    left = right = 0;
    let len = nums.length;
    let sum = 0; // 滑动窗口中元素和
    let resLen = Infinity;

    while(right < len) {
        sum += nums[right];
        while (sum >= target) {
            resLen = Math.min(right - left + 1, resLen);
            sum -=  nums[left]; // left向右移动前,sum要减去一位
            left++;
        }
        right++;
    }
    return resLen === Infinity ? 0 : resLen;
};

六、LeetCode59.螺旋矩阵II

文章讲解
边界处理:想想二分搜索中的“区间”思想(循环不变量)(不变量:处理规则(左闭右开/左闭右闭))
最外层while循环,每一条边用一个遍历for。定义好边界。
以下代码示例每一个for循环:左闭右开(只处理左边界点,不处理右边界点)

输入输出
3[[1, 2, 3], [8, 9, 4], [7, 6, 5]]
1[[1]]

在这里插入图片描述

一入循环深似海 | LeetCode:59.螺旋矩阵II

题目建议: 本题关键还是在转圈的逻辑,在二分搜索中提到的区间定义,在这里又用上了。

/**
 * @param {number} n
 * @return {number[][]}
 */
var generateMatrix = function(n) {
    let nums = new Array(n).fill(0).map(() => new Array(n).fill(0));// 最后的数组
    let count = 1;  // 遍历时候计数,也是为了给每个对应位置赋值
    let startX = 0; // 每一圈的起始位置都要变,每一圈++
    let startY = 0; // 每一圈的终止位置都要变,每一圈++
    let offset = 1; // 终止位置要减去的数量,,每一圈++,终止位置就是n - offset
    let loop = Math.floor(n/2);   // 旋转圈数
    let mid = Math.floor(n/2);    // 中间位置
    
    while (loop--) {
        let row = startX, col = startY;
        // 上行从左到右(左闭右开)
        for (;col < startY + n - offset; col++) {
            nums[row][col] = count++;
        }

        // 右列从上到下(左闭右开)
        for (;row < startX + n - offset; row++) {
            nums[row][col] = count++;
        }

        // 下行从右到左(左闭右开)
        for (;col > startY; col--) {
            nums[row][col] = count++;
        }

        // 左列做下到上(左闭右开)
        for (;row> startX; row--) {
            nums[row][col] = count++;
        }

        startX++;
        startY++;
        offset += 2;
    }

    // 如果n是奇数,最中间的数其实也是确定的
    if (n % 2 === 1) {
        nums[mid][mid] = count;
    }

    return nums;
};

七、数组小结

1、数组特点
2、常见算法题
(1)二分:
常见场景关键词:搜索、查找、位置、有序数组、平方数、平方根、
注意:边界思想(左闭右开、左闭右闭)

(2)双指针:
①从0开始-快慢指针
常见场景关键词:移除、删除、移动、

②起始终止(区间)-滑动窗口
for循环中的j是终止位置
如何移动起始位置?移动时机(一旦符合最终结果的条件),取相应区间
常见场景关键词:有序、平方、子数组

(3)模拟
常见场景关键词:螺旋矩阵

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

倏存

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值