代码随想录算法训练营第一天| 数组理论基础、LeetCode704. 二分查找、LeetCode27. 移除元素

704. 二分查找 
题目链接: 力扣

一开始的思路:递归,二分搜索,但是每次递归创建新的数组会带来额外的时间和空间开销。

二分搜索法(LeetCode704)

        搜索数组找到target,有则返回下标,无则返回-1. (时间复杂度O(logn)、空间复杂度O(n))

易错点:

1、循环条件:left < right 还是left <= right?

2、更新区间时,right = middle还是right = middle - 1?

解决方法:明确区间定义,(左闭右闭 [left ,right] or 左闭右开 [left ,right)),

                  边界规则由区间定义决定,定义了什么样的区间,就决定了边界应该怎么写

left = 0;

1、左闭右闭 [left,right ]:

right = num.size - 1;// 包含右边界

while( left <= right ){

    middle = (right-left)/2; //注意int越界情况

    if(nums[middle]>target){ 
    //更新左区间的右边界
    //左闭右闭,middle所在的值大于target,num[middle]一定不是搜索的值,接下来区间不包含该数值,  
        right = middle-1;

    } else if(nums[middle]<target){
    //更新右区间的左边界
    //左闭右闭,middle所在的值小于target,num[middle]一定不是搜索的值,接下来区间不包含该数值,  
        left = middle+1;
    } else {
        return middle;
    }
}
return -1;

2、左闭右开 [left,right ):

right = num.size;// 不包含右边界

while( left < right ){

    middle = (right-left)/2; //注意int越界情况

    if(nums[middle]>target){ 
    //更新左区间的右边界
    //左闭右开,不包含右边界的数值,
    //middle所在的值大于target,num[middle]一定不是搜索的值,接下来搜索区间不包含该数值,  
        right = middle;

    } else if(nums[middle]<target){
    //更新右区间的左边界
    //左闭右开,包括左边界的数值
    //middle所在的值小于target,num[middle]一定不是搜索的值,接下来区间不包含该数值,  
        left = middle+1;
    } else {
        return middle;
    }
}
return -1;

一种安全的方法来计算两个整数的中间值(mid)

这种方法通过将加法和减法结合起来,可以避免整数溢出的风险。

在传统的二分查找算法中,我们经常使用 (left + right) / 2 来计算中间值。然而,如果 leftright 非常大,相加的结果可能会超过整数类型的表示范围,导致溢出。

通过改用 left + (right - left) / 2 的方式,可以避免溢出的问题。这种方法首先计算 right - left,得到一个相对较小的差值,然后再除以 2。这样,即使 rightleft 的差值非常大,减法操作也不会导致溢出。

总结起来,使用 left + (right - left) / 2 取中间值是一种更安全的方法,可以防止整型溢出的问题。这在编写涉及大整数范围的算法时是一个有用的技巧。


什么时候使用库函数?如果一道题目能直接被一行库函数解决,就不要用;如果使用库函数只是解决问题中的一小步,并且我们知道库函数内部的实现过程、它的时间复杂度,那可以用。


27. 移除元素

一开始的思路:暴力法

移除元素(LeetCode27):搜索数组找到target,有则返回下标,无则返回-1.

在数组中移除元素实际上是覆盖操作,数组实际物理大小不变

1、暴力法(时间复杂度O(n^2)、空间复杂度O(1)):

        一层循环遍历数组,一层循环迁移元素实现覆盖。

2、双指针法(时间复杂度O(n)、空间复杂度O(1)):

  • 快指针:获取新数组(删除目标值后的数组)里所需要更新的元素,
  • 慢指针:获取新数组中需要更新的位置

将快指针获取到的值赋给慢指针即可,实际都是在一个数组上进行操作

  1. 当快指针指向的元素不等于val时,这个元素才是新数组所需要的元素,此时要更新数组:将快指针获取到的值,赋给慢指针指向的新数组所对应的下标的位置。慢指针向后移动一位。
  2. 当快指针指向的元素等于val时,这个元素不是新数组所需要的元素,此时不用更新数组,忽略掉这个元素,快指针向后移动一位,慢指针不动。
  3. 快指针遍历完成后停止,慢指针此时所指向下标正好是当前数组大小(该下标不存在数组元素)
  4. return 慢指针,正好是新数组的大小
public int removeElement(int[] nums, int val) {

    int slowIndex = 0;
    for (int fastIndex = 0; fastIndex<nums.length; fastIndex++){
        if(nums[fastIndex] != val){
            nums[slowIndex++] = nums[fastIndex];
        }
    }
    return slowIndex;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值