二分查找及变形题目(Java)

二分查找的思想

二分查找也称折半查找,是一种效率较高的查找方法。但是,折半查找要求线性表必须是顺序存储结构,而且表中元素按关键字有序排列。
查找过程:
首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。它的时间复杂度为O(log2n)。

示例代码

public static int binarySearch(Integer[] srcArray, int des) {
    //定义初始最小、最大索引
    int start = 0;
    int end = srcArray.length - 1;
    //确保不会出现重复查找,越界
    while (start <= end) {
        //计算出中间索引值
        int middle = (end + start)>>>1 ;//防止溢出
        if (des == srcArray[middle]) {
            return middle;
        //判断下限
        } else if (des < srcArray[middle]) {
            end = middle - 1;
        //判断上限
        } else {
            start = middle + 1;
        }
    }
    //若没有,则返回-1
    return -1;
}

相信大家对二分查找已经很熟悉了,这里就不再多说,下面看看几个二分查找的变形题目。

变形题目

排序数组

题目描述:给出一个排序数组,找到目标值返回其索引,找不到返回插入位置。例如:给出数组{2,5,7,8},若目标为5,则返回1,若目标为6,则返回2。

思路解析:这是一道典型的二分查找的变形题目,我们先按照二分查找的思路进行查找,找到返回其下标。如果没有找到,判断此时基准位置和目标元素的大小,如果基准大于目标值,则需要判断此时基准下标位置是否为0,这一步主要是为了防止数组越界。若基准小于目标值,则可以直接返回基准位置下标加1。
代码如下:

public static int search(int[] arr,int target){
        int low = 0;
        int high = arr.length-1;
        int mid = 0;
        while (low<=high){
            if(arr[mid]>target){
                low = mid+1;
            }else if(arr[mid]<target){
                high = mid-1;
            }else return mid;
        }
        if(arr[mid]>target){     //防止出现数组越界
            return mid -1>=0?mid:0;
        }else return mid+1;
    }

贪吃的小Q

题目描述:小Q的父母要出差N天,走之前给小Q留下了M块巧克力。小Q决定每天吃的巧克力数量不少于前一天吃的一半,但是他又不想在父母回来之前的某一天没有巧克力吃,请问他第一天最多能吃多少块巧克力。

思路解析:因为巧克力是整数,第一天吃的巧克力在1到M之间,我们可以利用二分查找找出第一天吃的数目。这里我们可以假设第一天吃s个,可以计算出总数是多少。然后利用二分查找将基准当作s传入计算所得出的总数是否与M相等,便可以找出第一天最多吃多少巧克力。
代码如下

public static int sum(int s,int n){

        int sum =  0;
        for (int i=0;i<n;i++){
            sum+=s;
            s=(s+1)/2;
        }
        return sum;
    }
   public static int Q(int N,int M){
        if(N==1) return M;
        int low = 1;
        int high = M;

        while (low<=high){
           int mid = (low+high+1)/2;
         if(sum(mid,N)==M) {
             return mid;
         }
            else if(sum(mid,N)>M){
                low = mid+1;
            }else if(sum(mid,N)<M){
                high = mid-1;
            }
        }
        return high;
   }

旋转数组的最小值

题目描述: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

思路解析:虽然这道题不是完全有序,但是可以堪称两个有序序列,我们还是可以考虑使用二分法。首先,设置低位位置为0,高位为length-1。如果此时低位小于高位,说明数组并未旋转,直接返回低位元素。如果低位位置大于高位,说明最小值肯定在右边数组,则让低位位置加一。如果相等,则高位减一。其他情况则说明此时基准在右边数组,但最小值在基准左边,则让高位等于基准,最终返回低位元素。

代码如下:

 public static int min(int[] arr){
        int low = 0;
        int high = arr.length-1;
        while (low<high){
            if(arr[low] <arr[high]){
                return arr[low];
            }
            int mid = low+(high-low)/2;
            if(arr[mid]>arr[high]){
                low = mid+1;
            }else if(arr[mid] == arr[high]){
                high = high-1;
            }else {
                high=mid;
            }
        }
        return arr[low];
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

降温vae+

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值