二分法+例题

二分法,就是从数组中间切一刀获得一个值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}都是有序的,所以本题用二分法求解。

依旧看本题的例子,利用二分法,

  1. 从中间切一刀,mid=((low+high)>>>1)=2,所以中间值为arr[2]=5

  1. 5的左边为{3,4,5},有序;右边为{5,1,2},无序。

  1. 用无序的一边继续二分,{5,1}无序;{1,2}有序。

  1. {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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值