LeetCode 704
简述题目要求:使用二分查找找到目标值,并且最终返回目标值的下标
思路分析:
对此题来说二分查找有两种形式:①区间是左闭右开 ②区间是左闭右闭
为什么会有这两种形式?
简单讲就是:所取的区间会影响到最终在while循环里面的条件判断,以及当前位置在数组中到底有没有意义,即边界条件。下面接着解析。
首先来解析一下两种形式共同的判断条件:
很显然有目标值在当前值左边,右边,等于当前值这三种情况
思路简单,直接上代码
if(nums[mid]>target){ //说明在左边 right = ?; } else if (nums[mid]<target) { //说明在右边 left = ?; }else { return mid; }
ok,接下里需要解决的就是?里面到底是什么,这就涉及到二分查找的两种形式,接着分析
①区间是左闭右开
对于左闭右开,说明没有办法取到右区间的端点,既然如此,那么left=right就是没有意义,即不可能的。因此在while里无需加上‘=’
while(left<right){}
while判断完了,接下来就是?到底填什么
对于左闭右开来讲,对于right右指针的初始化是不需要对数组长度-1的,因为根本不会取到
//因为是左闭右开,right不需要-1 int right = nums.length;
那么对于?的内容也很明朗了,当目标值在当前值左边的时候,我们缩小右区间就不需要再对mid进行-1,理由同上。
//说明在左边,因为是左闭右开,无需-1 right = mid;
当目标值在当前值右边的时候,缩小左区间,此时需要对mid进行+1,进行新区间的判断
//说明在右边 left = mid+1;
齐活了,附上较完整的代码
public int search2(int[] nums, int target) {
int left = 0;
//因为是左闭右开,right不需要-1
int right = nums.length;
//注意终止条件:因为在左闭右开这里,left=right是没有意义的
while(left<right){
int mid = (left + right)/2;
if(nums[mid]>target){
//说明在左边,因为是左闭右开,无需-1
right = mid;
} else if (nums[mid]<target) {
//说明在右边
left = mid+1;
}else {
return mid;
}
}
//未找到
return -1;
}
假如直接拿代码去跑,应该是能通过的,但是,肯定会进坑里了/doge/,别着急,接着往下看
直接拿代码去跑,是可以ac的,但是忽略了一个很重要的问题,就是“溢出”
为什么?
假设left right 为1073741824
加在一起 left+right = 2147483648 超过了int的最大范围 此时就溢出了
因此我们需要再修改一下mid的赋值语句
两种防溢出的方法,挑一个即可 //int mid = left + ((right-left)/2);//int mid = left + ((right-left)>>1);
最终代码
public int search2(int[] nums, int target) {
int left = 0;
//因为是左闭右开,right不需要-1
int right = nums.length;
//注意终止条件:因为在左闭右开这里,left=right是没有意义的
while(left<right){
//防溢出:如果 left 和 right 足够大,mid = (left + right)/2
//可能会由于 left+right 导致 int 数据类型越界。
//假设left right 为1073741824
//加在一起 left+right = 2147483648 超过了int的最大范围 此时就溢出了
int mid = left + ((right-left)>>1);
if(nums[mid]>target){
//说明在左边,因为是左闭右开,无需-1
right = mid;
} else if (nums[mid]<target) {
//说明在右边
left = mid+1;
}else {
return mid;
}
}
//未找到
return -1;
}
②区间是左闭右闭
两处不同
第一:左闭右闭的话说明可以取到右端点,因此left=right是有意义的
第二:避免数组越界,要对right进行-1
附上代码
public int search1(int[] nums, int target) {
int left = 0;
int right = nums.length-1;
while(left<=right){
int mid = left + ((right-left)/2);
if(nums[mid]>target){
right = mid-1;
} else if (nums[mid]<target) {
left = mid+1;
}else {
return mid;
}
}
return -1;
}
LeetCode 24
简述题目要求:按照要求原地修改数组
只做了快慢指针版本的
思路分析:
利用两个指针来遍历同一个数组,若nums[fast] == val,那就不更改nums[slow]的值,因为快指针的元素==val后说明这个是需要删除的元素,而!=val的说明是可以保留下来的元素