二分查找_边界值的判定

278. First Bad Version

Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad.

给定一组数[1,2,...,n]如果一个数为bad则它后续的所有数都为bad.设计算法找出第一个为bad的位置。

public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int left=1,right=n;
        while(left<right){//注意当且仅当left=right的时候退出循环,返回边界值left或right
            int mid=left+(right-left)/2;
            if (isBadVersion(mid)) right=mid;//当mid为bad时,将right赋值为mid,因为mid可能恰好就是第一个bad,最后right停留在第一个为bad的节点,
            else left=mid+1;//当不是bad时,下一个为bad的节点一定在后面,将left=mid+1;
        }
        return right;//当仅剩两个数时,这时计算得到mid=left+(right-left)/2=left;而根据此前left一定不是bad,因此最后肯定是在left=mid+1退出循环。
    }//返回left和right均可以。
}

不仅是left=mid+1时,left=righ退出循环这种方式,还有当left就是指向第一个为bad时的位置,而right为第二个bad节点,之后right会指向第二个bad节点位置。



public class Solution extends VersionControl {
    public int firstBadVersion(int n) {
        int left=1,right=n;
        while(left<=right){//注意当left=right时此时一定处在非bad位置,当left比right大1时退出循环
            int mid=left+(right-left)/2;
            if (isBadVersion(mid)) right=mid-1;//当mid为bad时,right=mid-1,意味着当mid恰好为第一个bad时而此时right已经成为bad的前一个数。
            else left=mid+1;//当不是bad时,下一个为bad的节点一定在mid后面。
        }//当left=right时,因为right始终保持在mid为bad节点的前一节点,此时left=right的节点一定是第一个bad节点之前的一个节点,不为bad,此时left+1,退出循环
        return left;//于是返回left恰好为第一个为bad节点。
    }//这里返回right不可行,因为right节点为第一个为bad节点的前一节点。
}

34. Search for a Range

Given an array of integers sorted in ascending order, find the starting and ending position of a given target value.

Your algorithm's runtime complexity must be in the order of O(log n).

If the target is not found in the array, return [-1, -1].

For example,
Given [5, 7, 7, 8, 8, 10] and target value 8,
return [3, 4].

题意:给定升序数组,给定目标值,在数组找到目标值的起始和结束位置,若没找到返回[-1,-1]。

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] res={-1,-1};
        if (nums==null) return res;
    //找左边界
int l1=0,r1=nums.length-1;
 while(l1<=r1){
int m=(l1+r1)/2; 
if(nums[m]>=target) r1=m-1;//当nums[m]>=target时r1-1,一直减1,到最后边界m恰好为左边界第一个target,r1仍然减1,此时r1为左边界前一节点。后保持不变。 
else l1=m+1;//否则l1=m+1,当l1与r1相等时,为左边界前前一点,最后当l1=m+1时退出循环,返回l1为左边界。
} 
int l2=0,r2=nums.length-1;
while(l2<=r2){ 
int m=(l2+r2)/2; 
if(nums[m]>target) r2=m-1;//nums[m]>target时r2-1,当到达边界值时,r2=m-1,此时r2为右边界。 
else l2=m+1;//当nums[m]<=target时,l2=m+1,最后当l2=r2时为右边界,最后当l2=m+1退出循环,此时l2已经大于target,因此返回l2 
}//因为当nums只有一个数时,且nums不包含target时,会返回res[0]=l1=0。因此需要对l1<=r2条件下,对res重新赋值。 
if (l1<=r2){ 
res[0]=l1; res[1]=r2; 
} return res;
}
}


旋转数组的最小数字


    public int minNumberInRotateArray(int [] array) {
    	if(array.length<1) return 0;
        int low=0;
        int hi=array.length-1;
        int mid=0;
        while(hi-low!=1){
            mid=(hi+low)/2;
            if(array[mid]>=array[low]){
                low=mid;
                //之前代码是while(low<hi)         low=mid+1;采用这种方式退出循环,但是当low=mid+1,可能会越界
                //不能加1可能mid恰好为最后一个最大元素,加1会越界,要保证low始终在边界右侧,high始终在边界左侧
                //要保证最后退出循环的时候low指向左侧最大元素,hi指向右侧最小元素,可以采用while(hi-lo!=1)来退出循环
            }
            if(array[mid]<=array[hi]){
                hi=mid;
            }
        }
        return array[hi];
    }


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值