代码随想录算法训练营第一天| 数组理论基础、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
    评论
第二十二天的算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值