代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素 JavaScript实现

参考文章与视频

数组理论基础:代码随想录 (programmercarl.com)

题目一:704. 二分查找

题目描述

 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。704. 二分查找 - 力扣(LeetCode)

第一想法

我们可以按照数组的顺序依次遍历,如果在遍历过程中找到target就可以返回其对应的下标,如果遍历完整个数组还没找到目标值,就返回-1。

!但是,所要查找的数组是有序的,我们在查找过程中可以用目标值和当前的数组元素进行大小比较,如果目标值小于当前数组元素,那么若存在目标值,它的索引一定小于当前元素的索引值;如果目标值大于当前数组元素,那么若存在目标值,它的索引一定大于当前元素的索引值。因此可以设置左右指针分别指向待比较区间的最小和最大值,当两指针相遇要么找到返回目标值对应下标,要么没有找到返回-1。

需要注意两种特殊情况:要查找的值比数组最小值小;要查找的值比数组最大值大。

看完代码随想录之后的想法

while循环条件是`left<=right` or `left<right` 以及 `right = mid -1` or `right = mid`?

这取决于区间是否包含边界值:

①[left,right]: 这种情况下应该取right= mid-1 和 left = mid+1,循环条件应为left<=right。首先当前这个nums[mid]一定不是target,那么接下来要查找的左区间结束下标位置就是 mid - 1。也正因为mid-1时有可能会与left相等,所以循环条件应为left<=right。例如在[5]中查找5,如果令left<right,最终的返回结果将为-1,而不是0,显然这是错误结果。

②[left,right): 这种情况下应该取right= mid 和 left = mid,循环条件应为left<right。开区间这种情况表示right是取不到的,也就是说我们在代码中的逻辑为right=mid,nums[mid]已经和target比较了,这时下一次循环就不用再考虑本次的mid所以循环条件应为left<right。如果target不在数组中,left和right最终都会等于mid,永远满足left<=right,不会跳出循环。

var search = function(nums, target) {
    let left = 0;
    let right = nums.length-1;
    while (left <=  right) {
        let mid =parseInt((left + right)/2);
        if(target > nums[mid] ) {
            left = mid;
        }else if (target < nums[mid] ) {
            right = mid;
        } else if(target == nums[mid]) {
            return mid;
        }
    }
    return -1;
};

实现中遇到的困难

无法跳出循环

在JavaScript中要使用parseInt()等方法将`/`(除÷)的计算方法转化为整数,不然mid取值为小数2.

题目二:27. 移除元素

题目描述

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。27. 移除元素 - 力扣(LeetCode)

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。

示例 2: 给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。

第一想法

对于JavaScript而言,可以利用Array对象的操作方法直接遍历找到数组中与val相等的值进行删除。

var removeElement = function(nums, val) {
    let i =0;
    while(i<nums.length) {
        if(nums[i] == val) {
            nums.splice(i,1);
        } else {
            i = i+1;
        }
    }
    return  nums.length
};

看完代码随想录之后的想法

双指针法 - 利用快慢指针

慢指针:我们需要更新的下标。那么如何更新呢?我们用快指针来更新它。

我们令快指针正常的遍历数组中的元素,慢指针随着快指针一起遍历,nums[慢]=nums[快]。当快指针指向元素值与val相等时慢指针不动,待下一次循环快指针指向下一元素时继续更新(直到快指针指向不等于val的元素,此时更新给慢指针的元素才会不包含val),此时nums[慢]=nums[快+1],那么原来的值就被快指针指向的新元素覆盖掉了。

var removeElement = function(nums, val) {
    let s =0;
    for(let k= 0; k < nums.length;k++) {
        if(nums[k] != val) {
            nums[s++] = nums[k];            
        }
    }
    return s
};

实现中遇到的困难

1.需要注意slice方法会不改变原数组,而是创建新数组,按照题意不能创建新数组,因此不能使用slice()

2.splice删除元素后nums是新数组,后面的元素会提前,因此删除当前元素后还应该继续对当前下标指向的数组元素与val比较。

3.s++:s++返回值相当于s,nums[s++] = nums[k]相当于nums[s] = nums[k]; s++;

4.返回长度为s而不是s+1,原因同上,s已在原有基础上加了1.

学习收获

二分查找:对于二分查找而言,区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。

对于更新数组链表的需求可以考虑使用快慢指针。

猜你喜欢

转载自blog.csdn.net/qq_42101569/article/details/126978731