Java-二分法及二分法的应用场景

1.在一个有序数组中查找某数是否存在

    public static boolean select(int [] arr,int num) {
        int left = 0;
        int right = arr.length-1;
        while(left<=right) {
            int middle = left + ((right - left) >> 1);
            if(num>arr[middle]) {
                left = middle+1;
            }
            else if(num==arr[middle]) {
                return true;
            }else if(num<arr[middle]){
                right = middle-1;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        int [] arr = {1,3,5,7,9,16};
        boolean b = select(arr, 1);
        System.out.println(b);
       
}

注:int middle = left + ((right - left) >> 1);这段代码和middle=(left+right)/2等效

为什么要这么写呢?1.位运算要比直接的乘除法运算要快 2.如果直接用(left+right)>>1可能会导致它的大小超过整形的大小

注:不要搞错了 num是要和arr[middle]比较,每次修改的是middle

2.在一个有序数组中查找 大于等于某个数最左面的位置

例如:{1,1,1,3,3,3,4,5,5,6}这个数组arr中大于等于3的最左面位置即为arr[4]

关于这道题的思路,本质上还是二分法问题(左边和右边的留存问题)如果middle大于等于3就把middle右边的排除掉(说明边界在左边),如果小于等于3就把左边排除掉(说明边界在右边)

关于大于等于:如果大于num说明 num还有很多在左面 如果等于num有两种情况1.左面还有num,不是要找的,无所谓,index的值还会改变2.这就是要找的,它左面不会再有进行大于等于num的情况了 index就不会更新了 这个index就是要找的.

顺带一提当时我脑子一抽自作聪明搞了个== 结果发现根本就二分不起来

public static int selectleft(int [] arr,int num) {
        int left = 0;
        int right = arr.length-1;
        int index = -1;
        while(left<=right) {
            int middle = left + ((right - left) >> 1);
            if(arr[middle]>=num) {
                right = middle-1;
                index = middle;
            }
            else if(arr[middle]<num){
                left = middle+1;
            }
        }
        return index;
    }

    public static void main(String[] args) {
        int [] arr = {1,1,1,3,3,3,4,5,5,6};
        int b = selectleft(arr, 3);
        System.out.println(b);
       
}

本质上都是要查找的东西是在middle左面还是有面罢了

3.在一个有序数组中查找 小于等于某个数最右面的位置

逻辑同上

public static int selectleft(int [] arr,int num) {
        int left = 0;
        int right = arr.length-1;
        int index = 0;
        while(left<=right) {
            int middle = left + ((right - left) >> 1);
            if(arr[middle]>num) {
                right = middle-1;
            }
            else if(arr[middle]<=num){
                left = middle+1;
                index = middle;
            }
        }
        return index;
    }

    public static void main(String[] args) {
        int [] arr = {1,1,1,3,3,3,4,5,5,6};
        int b = selectleft(arr, 3);
        System.out.println(b);
       
}

4.找出无序且相邻不等数组中一个局部最小值

局部最小值: 和相邻的数比最小 就叫局部最小值

分三种情况讨论

1.在arr[0]处(开头处) 只要后一个元素小就是局部最小

2.在arr[arr.length-1]处(结尾处),只要比前一个元素小就是局部最小

3.在其他位置 要比前一个和后一个小才是局部最小

在一个相邻不等的无序(注意是无序不是随机,如果是随机是包含有序的可能性的)数组中总有一个局部最小

public static int selectmin(int [] arr) {
        if (arr == null || arr.length == 0) {
            return -1; // 数组为空的情况
        }
        if (arr.length == 1 || arr[0] < arr[1]) {
            return 0;
        }
        if (arr[arr.length - 1] < arr[arr.length - 2]) {
            return arr.length - 1;
        }
        int left = 1;
        int right = arr.length - 2;
        int mid = 0;
        while (left < right) {
            mid = (left + right) / 2;
            if (arr[mid] > arr[mid - 1]) {
                right = mid - 1;
            } else if (arr[mid] > arr[mid + 1]) {
                left = mid + 1;
            } else {
                return mid;
            }
        }
        return left;
    }
    

    public static void main(String[] args) {
        int [] arr = {8,2,4,3,5,7};
        int b = selectmin(arr);
        System.out.println(b);
       
}

逻辑其实很简单 如果middle不是局部最小值那就去找它的左边/右边 左半个数组和右半个数组也满足无序且相邻不等的条件

你可能会问:那 1,2,3,4,3,4,3,2,1这种情况怎么办?它整体无序但它的左半/右半不是无序的啊?

答:在开始的边界元素就已经判断完毕了 甚至都不用走后面的二分流程.....

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值