您的位置:首页 > 其它

二分查找_边界值的判定

2017-10-11 17:35 211 查看
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];
}


                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: