二分法,就是从数组中间切一刀获得一个值mid,用这个值和所要求的值(key)做对比,如果mid比key大,那就从mid左边的中间再切一刀(反之从mid的右边再切一刀)。如上反复切、对比,直到所切的那一刀mid的值等于key的值,就可以return回这个值(是mid,也是key)了。
package _2_1递归;
public class 二分法 {//只走一条路
/*等价于:
* 1、左边找(递归)
* 2、中间比
* 3、右边找(递归)
*
* 注意:左查找和右查找只选其一
* */
public static void main(String args[]) {
int[] arr = {0,2,4,6};
System.out.print(f(arr,0,arr.length,0));
}
public static int f(int[] arr,int low,int high,int key) {
if(low>high) {
return -1;
}
int mid = low+((high-low)>>1);//(low+high)>>>1;//防止溢出,移位更高效,相当于(low+high)/2
int midVal = arr[mid];
if(midVal<key) {
return f(arr,mid+1,high,key);
}
else if(midVal>key) {
return f(arr,low,mid-1,key);
}
else {
return mid;//key found
}
}
}
👇二分法改造例题1

递增排序即顺序排序,顺序排序就想到二分法(降低复杂度)。本题给的例子{3,4,5,1,2}中,{3,4,5}、{1,2}都是有序的,所以本题用二分法求解。
依旧看本题的例子,利用二分法,
从中间切一刀,mid=((low+high)>>>1)=2,所以中间值为arr[2]=5
5的左边为{3,4,5},有序;右边为{5,1,2},无序。
用无序的一边继续二分,{5,1}无序;{1,2}有序。
{5,1}中,可以确定,这种从顺序数组中分到最后只剩两个的,肯定都是大的在左,小的在右,就可以得出最小值了。
package _2_1递归;
//
public class _实战提2_旋转数组的最小数字_改造二分法 {
public static void main(String args[]) {
int[] arr= {3,4,5,1,2};
System.out.print(f(arr));
}
public static int f(int[] arr) {
int begin=0;
int end=arr.length-1;
while(begin+1<end) {//当begin和end指向相邻元素时,退出
int mid=((begin+end)>>>1);
//要么左侧无序,要么右侧无序
//判断哪侧无序的方法,就是把中间值和中间值右边的值比较大小
if(arr[mid]>=arr[mid+1]) {//右侧无序
begin=mid;
}else {//左侧无序
end=mid;
}
}
return arr[end];
}
}
此法有一个问题,就是{1,0,1,1,1}这种会分析不出来,输出为1,原因出在第15行的“>=”号上,会判断成右侧无序。
解决方法:在12、13行之间先判断是否mid的左右两边是否相等,如果一样,就放弃这个算法,改用扫描法。
👇二分法改造例题2
package _2_1递归;
//有个排序后的字符串数组,其中散布着一些空字符串(“”),编写一个方法,
//找出给定字符串(肯定不是空字符串)的索引
public class _实战题3_在有空字符串的有序字符串数组中查找_改造二分法 {
//例如本题中要查找的是“b”,中间的字符串是“”(空)。
public static void main(String args[]) {
String[] arr= {"a","","ac","","ad","b","","ba"};
System.out.print(f(arr,"abc"));
}
public static int f(String[] arr,String key) {
//定中(mid),但是中间那个不能是空字符串(无法比较),所以就得将mid左/右移,找到非空字符串来进行比较
//字符串比较:CompareTo()方法
int begin=0;
int end=arr.length-1;
while(begin<=end) {
int mid=((begin+end)>>>1);
while(arr[mid].equals("")) {//如果中值为空串,就左/右移
mid++;
if(mid>end) {
return -1;
}//要注意,当mid的值左右移动超过end的值时,就说明没找到,要return -1,否则会造成死循环
}
if(arr[mid].compareTo(key)>0) {//如果中值mid比目标值key大
end=mid-1;
}else if(arr[mid].compareTo(key)<0){//如果中值mid比目标值key小
begin=mid+1;
}else {
return mid;
}
}
return -1;
}
}