二分是针对一个有序的数组中的查找,每次搜索都可以将搜索空间减半;效率可见一斑。
但是,写出一个不出bug,简洁,优雅的代码并不容易,接下来,我们来分析一下:
中间下标:mid
对于一个数组来说,比如:
[1, 4, 5, 6] 或 [1, 4, 5, 6, 7] 我们定义中位数有两种:
第一种为 下中位数:(length-1)/ 2;
第二种为 上中位数:length / 2;
我们分析一下,对于下中位数来说,当长度为奇数,比如5,mid 就会是 2;
当长度为偶数,比如4,mid 就会是 1;
总结就是一句话,下中为数在可以中分的情况下,取正中间;不可以中分的情况下取靠左的数。
上中位数在可以中分时也取正中间,不可以中分时取靠右。
你每次在循环取中位数的时候就用 mid = (left + right) / 2;
为了防止溢出,你要这样写: mid = left + (hight - left) / 2。
判断找到元素的返回终结条件
left > right ( 即下一轮的搜索空间:无)
返回值可以直接为 left, 为什么?
left 在遇到比目标值小的mid 前进为 mid; 否则保持原位;如果在数组中找到了这个元素直接返回mid;
若没有找到,直接返回 left;
这个left就是该元素应该插入数组中的位置。
[1, 2] 找 3;
第一次取mid = 0;
target比1大;要更新 left
left = left+1 = 1;
target 比 2 大,又要更新left
left = left +1 比 right 大 ,故返回 left;为 2;即插入的位置是2;
Java 代码:
package temp0;
public class Solution {
public static void main(String[] args){
Solution solution = new Solution();
int[] nums = {1,2,3};
solution.searchInsert(nums, 2);
}
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length-1;
int mid = 0;
while(left<=right){
mid = left +(right-left)/2;
if (target>nums[mid]){
left = mid+1;
}
if(target<nums[mid]){
right = mid-1;
}
if(target==nums[mid]){
System.out.println(mid);
return mid;
}
}
System.out.println(left);
return left;
}
}
第二种情况,有重复元素的?
[ 1, 2, 3, 4, 4, 4, 5 , 6,7] 查找 4;
为了找到第一个4,在mid<target时正常操作;
在mid>=target 时,说明应该向左查找;right 向左逼近;
同样等到 left 比 right还大的时候,说明 left 是最适合放4的位置,
Java 代码如下:
package temp0;
public class Solution {
public static void main(String[] args){
Solution solution = new Solution();
int[] nums = {1};
solution.searchInsert(nums, -1);
}
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length-1;
int mid = 0;
while(left<=right){
mid = left +(right-left)/2;
if (target>nums[mid]){
left = mid+1;
}
else{
right = mid-1;
}
}
System.out.println(left);
return left;
}
}
这段代码对 :数组长度,元素是否重复,无要求。
但是注意这里的元素都是闭区间的。
核心技能:
以上