1、算法描述
二分查找算法:需求:在有序数组A内,查找值target,如果找到则返回目标值的索引号,否则返回-1
前提 | 给定一个内含有n个元素的有序数组A,满足A0<=A1<=A2<=···<=An-1,一个待查找的值target |
1 | 设置i=0,j=n-1 |
2 | 如果i>j,结束查找,没有找到 |
3 | 设置m=floor((i+j)/2),m为中间索引,floor为向下取整(也就是小于等于(i+j)/2的最大整数) |
4 | 如果target<Am,设置j=m-1,跳转到第2步 |
5 | 如果target>Am,设置i=m+1,跳转到第2步 |
6 | r如果Am=target,结束查找,找到目标值 |
2、算法实现
//Java写一个方法,传入一个数组和要查找的目标值
public static int erfenSearch(int[] a,int target){
int i=0,j=a.length-1; //设置两个指针的初值
while(i<=j){ //在i到j范围内查找
int m=(i+j)>>>1;
if(target<a[m]){ //目标在左边
j=m-1;
}else if(a[m]<target){ //目标在右边
i=m+1;
}else{ //找到了
return m;
}
reutrn -1;
}
}
3、存在的问题
1、为什么循环中写i<=j,而不是i<j?
回答:如果只是写i<j,那么只有索引为m的参与比较,i和j为索引的元素就不会参与比较,比如查找到最后一个元素时i=j了,但是此时就不会进入循环去求m,从而就没办法比较i==j时的那个元素是否是目标值,而i==j就意味着i,j指向的元素也会参与比较
2、(i+j)/2为什么写成>>>1?
回答:如果i 和 j 变成了两个很大的正整数相加,那么得到的结果就有可能为负数,在二进制中,同一个二进制数,如果把最高位视为符号位,那么得到的结果就是负的,如果不视为符号位,那么得到的结果就是正常的,改变:使用无符号右移运算符来代替除2的过程。
右移:7的二进制数8位表示为0000 0111,右移之后变成0000 0011,也就是变成了3
16的二进制0001 0000,右移之后变成0000 1000 也就是变成了8
而这就能看成除2取整,(7除2取整就是3),同一个二进制可能表现出正整数,也会表现出负数,但是通过让其右移的方式,最终得到的结果都是正确的,此外,通过这种右移的方式还能适应其他语言
=(mi+j)>>>1;
3、为什么代码中都写成小于号,有什么好处?
回答:与数组的排序有关,数组属于升序还是排序,属于升序的就写小于号,能给人一种更加直观的感觉,写出来的代码与我们的思维更加一致,不过当然你不想这样写那也没关系,自己能够看得懂就行啦。
4、改版后的二分查找算法
//改写后的二分查找算法
public static int erfenSearch(int[] a,int target){
int i=0,j=a.length; //不进行减一
while(i<j){ //没有等于
int m=(i+j)>>>1;
if(target<a[m]){
j=m; //直接等于m
}else if(a[m]<target){
i=m+1;
}else{
return m;
}
reutrn -1;
}
}
此时 j 所指向的位置不会参与运算,可以取一个递增的数组来做示范,这两种版本的二分法都需要掌握
注意:在改版后的不能在循环条件中添加等于号,否则当查找一个不存在的目标值时会陷入死循环中