第一种 基础版
二分查找就是创建两个索引,i和j取他们的中间值m,每次查找m这样就会比遍历节省一半的时间。
两个索引分别在首和尾,if判断要找的target在arr[m]的左边还是右边,如果小于arr[m]那么尾索引j就等于m+1,如果大于就改变首索引。如果等于就返回查到的索引,如果没找到就返回-1.
public static int Foundation(int [] arr,int target){
int i = 0,j = arr.length - 1;
while(i <= j){
int m = (i + j) >>> 1;
if(target < arr[m]){
j = m - 1;
}else if(target > arr[m]){
i = m + 1;
}else{
return m;
}
}
return -1;
}
第二种 改进版
在基础版上进行了三处代码的改动
第一点:
对光标j的定义从arr.length - 1改成了arr.length这样就变成了左闭右开的情况
在这种情况下永远访问不到j的位置所以循环的判断条件变成了i < j就是为了避免数组索引越界异常
第二点:
循环的判断条件
第三点:
j改变的值
public static int improvement(int [] arr,int target){
int i = 0,j = arr.length; //第一点改动
while(i < j){ //第二点改动
int m = (i + j) >>> 1;
if(target < arr[m]){
j = m; //第三点改动
}else if(arr[m] < target){
i = m + 1;
}else{
return m;
}
}
return -1;
}
第三种 平衡版
前面两种在查找次数是n的情况下,如果target在左边那么if判断的次数就是n次,因为不会加入else if进行第二次判断。但是如果target值在右边,那么if判断的次数就是2n。平衡板就是在原来的基础上优化了算法,不管在左边还是右边都是一次判断,节省了时间,但是如果是最优的情况第一次m就等于target那么效率不如上面两种,但是属于极少数情况属于也不算缺点。
在循环里面不再是找到等于target的值,而是缩小范围,当就剩一个的时候去判断是否等于target,如果等于返回该元素的索引i,否则返回-1.
public static int balance(int [] arr,int target){
int i = 0,j = arr.length;
while(1 < j - i){
int m = (i + j) >>>1;
if(target < arr[m]){
j = m;
}else{
i = m;
}
}
if(target == arr[i]){
return i;
}else{
return -1;
}
}