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
来计算中间值。然而,如果 left
和 right
非常大,相加的结果可能会超过整数类型的表示范围,导致溢出。
通过改用 left + (right - left) / 2
的方式,可以避免溢出的问题。这种方法首先计算 right - left
,得到一个相对较小的差值,然后再除以 2。这样,即使 right
和 left
的差值非常大,减法操作也不会导致溢出。
总结起来,使用 left + (right - left) / 2
取中间值是一种更安全的方法,可以防止整型溢出的问题。这在编写涉及大整数范围的算法时是一个有用的技巧。
什么时候使用库函数?如果一道题目能直接被一行库函数解决,就不要用;如果使用库函数只是解决问题中的一小步,并且我们知道库函数内部的实现过程、它的时间复杂度,那可以用。
27. 移除元素
一开始的思路:暴力法
移除元素(LeetCode27):搜索数组找到target,有则返回下标,无则返回-1.
在数组中移除元素实际上是覆盖操作,数组实际物理大小不变
1、暴力法(时间复杂度O(n^2)、空间复杂度O(1)):
一层循环遍历数组,一层循环迁移元素实现覆盖。
2、双指针法(时间复杂度O(n)、空间复杂度O(1)):
- 快指针:获取新数组(删除目标值后的数组)里所需要更新的元素,
- 慢指针:获取新数组中需要更新的位置
将快指针获取到的值赋给慢指针即可,实际都是在一个数组上进行操作
- 当快指针指向的元素不等于val时,这个元素才是新数组所需要的元素,此时要更新数组:将快指针获取到的值,赋给慢指针指向的新数组所对应的下标的位置。慢指针向后移动一位。
- 当快指针指向的元素等于val时,这个元素不是新数组所需要的元素,此时不用更新数组,忽略掉这个元素,快指针向后移动一位,慢指针不动。
- 快指针遍历完成后停止,慢指针此时所指向下标正好是当前数组大小(该下标不存在数组元素)
- 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;
}