[Algorithm] 九章二:Binary Search

457.经典二分叉中分问题:点击打开链接

注意:二分查找模板

         牢记二分查找精髓,不断缩小范围,然后手动找出结果(并不是在缩小范围的时候就一定能找到结果)         

public class Solution {
    /**
     * @param nums: An integer array sorted in ascending order
     * @param target: An integer
     * @return an integer
     */
    public int findPosition(int[] nums, int target) {
        if(nums.length==0||nums==null){
            return -1;
        }
        int start=0, end =nums.length-1;
        
        while(start+1<end){                  
            int mid=start+(end-start)/2;     //mid的定义方法,防止stackoverflow,但是一般情况下不会
            if(nums[mid]==target){
                return mid;                  //很多时候都不能直接return的
            }else if(nums[mid]<target){
                start=mid;                   //这边的边界不加1或者减1
            }else{
                end=mid;                     //同上
            }
        }
        
        if(nums[start]==target){             //上面的while主体里不断缩小区间,直到只剩外面两种情况可判断
            return start;
        }
        if(nums[end]==target){
            return end;
        }
        return -1;      
    }
}

458.Last Position of Target:点击打开链接

注意:像这样找第一个或者最后一个的情况,两方面需要注意:

         1. while里的if,else三种条件不合并成两种,而且等于target的情况要根据实际情况看是start=mid还是end=mid。

         2. while出来之后,当条件减缩到可以从几个值中找出结果时,要根据实际情况看是先找end,还是先找start

public class Solution {
    /**
     * @param nums: An integer array sorted in ascending order
     * @param target: An integer
     * @return an integer
     */
    public int lastPosition(int[] nums, int target) {
        if(nums==null || nums.length==0){
            return -1;
        }
        
        int start=0,end=nums.length-1;
        
        while(start+1<end){
            int mid=start+(end-start)/2;
            if(nums[mid]==target){                    //找到一个target,不一定是最后位置的target,因此还要向后找,所以切掉前面的
                start=mid;                            //不是直接return的
            }else if(nums[mid]<target){
                start=mid;
            }else{
                end=mid;
            }
        }
         
        if(nums[end]==target){                        //出来只剩start和end的时候,由于是要目标最后元素,所以先看end是否符合
            return end;                               //如果end符合条件就直接return结果了
        }    
        if(nums[start]==target){
            return start;
        }     
        return -1;
    }
}

459.Closet Number in Sorted Array:点击打开链接

public class Solution {                    

    /**
     * @param A an integer array sorted in ascending order
     * @param target an integer
     * @return an integer
     */
    public int closestNumber(int[] A, int target) {
        if(A==null || A.length==0){
            return -1;
        }
        
        int start=0,end=A.length-1;
        while(start+1<end){                             //不断缩小范围
            int mid=start+(end-start)/2;
            if(A[mid]<target){
                start=mid;
            }else{
                end=mid;
            }
        }
        
        if(A[start]==target){                           //手动找出结果
            return start;
        }else if(A[end]==target){
            return end;
        }else if(target-A[start]<=A[end]-target){
            return start;
        }else{
            return end;
        }
    }
}

K Closet Numbers in Sorted Array

思路:二分法先找到start和end两点

          再start向左和end向右找其他符合条件的元素

          要注意start向左出左边界和end向右出右边界的问题

分析:为什么start向左走,end向右走?

          因为在第一步微分法找start和end时,一般找到的start和end是相邻的两点,因为都在不断向target靠近,所以如果start向右,end向左就会重复判断点

public class Solution {
    /*
     * @param A: an integer array
     * @param target: An integer
     * @param k: An integer
     * @return: an integer array
     */
    public int[] kClosestNumbers(int[] A, int target, int k) {
        if (A == null || A.length < k || k <= 0) {
            return new int[0];
        }

        int[] result=new int[k];
        int start=0;
        int end=A.length-1;

        while (start+1<end) {
            int mid = start+(end-start)/2;
            if(A[mid]<target) {
                start=mid;
            }else{
                end=mid;
            }
        }

        for (int i=0;i<k;i++) {
            int startDiff=(start < 0) ? Integer.MAX_VALUE : Math.abs(A[start]-target);
            int endDiff=(end >= A.length) ? Integer.MAX_VALUE : Math.abs(A[end]-target);
            if(startDiff<=endDiff) {       //这边的等于符号也很重要,因为题目要求最后的输出顺序如果diff等的话按小到大
                result[i]=A[start];
                start--;
            }else{
                result[i]=A[end];
                end++;
            }
        }

        return result;
    }
}

74.First Bad Version:点击打开链接

/**
 * public class SVNRepo {
 *     public static boolean isBadVersion(int k);
 * }
 * you can use SVNRepo.isBadVersion(k) to judge whether 
 * the kth code version is bad or not.
*/
class Solution {
    /**
     * @param n: An integers.
     * @return: An integer which is the first bad version.
     */
    public int findFirstBadVersion(int n) {
        int start=0,end=n;
        
        while(start+1<end){
            int mid=start+(end-start)/2;
            if(SVNRepo.isBadVersion(mid)){
                end=mid;
            }else{
                start=mid;
            }
        }
        
        if(SVNRepo.isBadVersion(start)){
            return start;
        }
        return end;
    }
}

447.Search in a Big Sorted Array:点击打开链接

注意:确定大边界的时候是先确定再进行后边的找target,而不是边找target边确定,这样做相对来讲省事很多,不用边找边判断。

/**
 * Definition of ArrayReader:
 * 
 * class ArrayReader {
 *      // get the number at index, return -1 if index is less than zero.
 *      public int get(int index);
 * }
 */
public class Solution {
    /**
     * @param reader: An instance of ArrayReader.
     * @param target: An integer
     * @return : An integer which is the index of the target number
     */
    public int searchBigSortedArray(ArrayReader reader, int target) {
        int index=0;
        while(reader.get(index)<target){                    //数组长度不断乘2扩展的想法,来确定一个右边界
           index=2*index+1;                                 //先看第0个位置是不是大于target,再看第1个,2,4,8...
        }                                                   
        int start=0, end=index;                    
        while(start+1<end){
            int mid=start+(end-start)/2;
            if(reader.get(mid)<target){
                start=mid;
            }else{
                end=mid;
            }
        }
        
        if(reader.get(start)==target){
            return start;
        }
        if(reader.get(end)==target){
            return end;
        } 
        return -1;      
    }
}

Follow up Links: http://www.cnblogs.com/grandyang/p/4325840.html

159.Find Minimum in Rotated Sorted Array:点击打开链接

思路:关于rotated sorted array题目都分成坐标轴二,四象限两条单调递增直线的想法加理解

 

public class Solution {                           //方法一
    /** 
     * @param nums: a rotated sorted array
     * @return: the minimum number in the array
     */
    public int findMin(int[] nums) {
       if(nums==null || nums.length==0){
           return -1;
       }
       
       int start=0;
       int end=nums.length-1;
       int target=nums[nums.length-1];             //先将最后一个看做target,找到第一个小于target的元素就是所求

       while(start+1<end){
           int mid=start+(end-start)/2;
           if(nums[mid]<target){
               end=mid;
           }else{
               start=mid;
           }
       }
       
       if(nums[start]<target){                    //由于是要找第一个小于target的元素,因此先判断start
           return nums[start];
       }
       return nums[end];
    }
}

 

 

 

public class Solution {                                   //方法二
    /**
     * @param nums: a rotated sorted array
     * @return: the minimum number in the array
     */
    public int findMin(int[] nums) {      
        if(nums == null || nums.length == 0) {
            return -1;
        }
        
        int start = 0, end = nums.length - 1;
        int target = nums[nums.length - 1];               //先将最后一个看做target,找到第一个小于target的元素就是所求
        
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (nums[mid] <= target) {                    //不断向左缩减
                end = mid;
            }else {
                start = mid;
            }
        }
        if(nums[start] <= target) {
            return nums[start];
        }else {
            return nums[end];
        }
    }
}

160. Find Minimum in Rotated Sorted Array II:点击打开链接

思路:关于这题的考点九章给的解释特别好,这题考的就是能不能想到最坏情况,

         [1,1,1,1...1]里有一个0,时间复杂度必须是O(n),因此写一个for循环就好

         但是也并不是所有的情况都是坏情况,也可以用一个伪二分来实现,但这题考的不是会不会二分法,而是想不想得到最坏情况

public class Solution {
    /**
     * @param num: a rotated sorted array
     * @return: the minimum number in the array
     */
    public int findMin(int[] num) {                   //最坏情况for循环
        if(num==null || num.length==0){
            return -1;
        }
        
        int min=Integer.MAX_VALUE;
        for(Integer n:num){
            min=Math.min(min,n);
        }
        return min;
    }
}
public class Solution {
    /**
     * @param num: a rotated sorted array
     * @return: the minimum number in the array
     */
    public int findMin(int[] num) {                   //伪二分
        if(num==null || num.length==0){
            return -1;
        }
        
        int start=0;
        int end=num.length-1;
        while(start+1<end){
            int mid=start+(end-start)/2;
            if(num[mid]==num[end]){                   //去除num[end]不影响,因为num[mid]和num[end]值相等
                end--;
            }else if(num[mid]<num[end]){              //num[mid]<num[end],说明最小值在前面部分,最大可能是num[mid]
                end=mid;
            }else{                                    //如果num[mid]>num[end],说明num[mid]的前面也比num[end]大
                start=mid;                            //因此要找小值还是要向后面部分找
            }
        }
        
        if(num[start]<=num[end]){
            return num[start];
        }else{
            return num[end];
        }
    }
}

62.Search in Rotated Sorted Array:点击打开链接

public class Solution {
    /** 
     *@param A : an integer rotated sorted array
     *@param target :  an integer to be searched
     *return : an integer
     */
    public int search(int[] A, int target) {
        if(A==null || A.length==0){
            return -1;
        }
        int start=0,end=A.length-1;
        while(start+1<end){
           int mid=start+(end-start)/2;
           if(A[mid]==target){
               return mid;
           }
           if(A[start]<A[mid]){                         //这里有两个要点,如果mid的位置在红线的情况
               if(A[start]<=target && target<=A[mid]){  //内部if里条件写单调递增好讨论的情况,另一种else
                   end=mid;
               }else{
                   start=mid;
               }
           }else{                                       //还有就是如果mid的位置在绿线的情况
               if(A[mid]<=target && target<=A[end]){    //内部if同上
                   start=mid;
               }else{
                   end=mid;
               }
           }
        }
        
        if(A[start]==target){
            return start;
        }
        if(A[end]==target){
            return end;
        }  
        return -1;
    }
}

585.Maximum Number in Mountain Sequence:点击打开链接

思路:此题不论是在红线还是绿线位置,都是要考虑mid+1与mid的大小关系就足以定位

         但是如果是一个先升后降的数组,就要考虑mid的位置在红线还是绿线

注意:本题而言,不会有出现nums[mid+1]越界的情况,可以用[1,3,2]简单数组举例。

public class Solution {
    /**
     * @param nums a mountain sequence which increase firstly and then decrease
     * @return then mountain top
     */
    public int mountainSequence(int[] nums) {
        int start=0, end=nums.length-1;                                                      
        while(start+1<end){
            int mid=start+(end-start)/2;
            if(nums[mid]<nums[mid+1]){
                start=mid;
            }else{
                end=mid;
            }
        }
        
        if(nums[start]<nums[end]){
            return nums[end];
        }else{
            return nums[start];
        }
    }
}

75.Find Peak Element:点击打开链接

思路:这题就是看mid的位置是在峰,谷,上升的半山腰,还是下降的半山腰上

public class Solution {   
    /**
     * @param A: An integers array.
     * @return: return any of peek positions.
     */
    public int findPeak(int[] A) {                                                   
        int start=0, end=A.length-1;
        
        while(start+1<end ){
           int mid=start+(end-start)/2; 
           if(A[mid]>A[mid-1] && A[mid]>A[mid+1]){   //在峰直接return
               return mid;
           }else if(A[mid]>A[mid-1]){                //在上升的半山腰上,后面不远处就会有峰,因此砍前面往后移动
               start=mid;
           }else if(A[mid]>A[mid+1]){                //在下降的半山腰上,不久前就是一个峰,因此砍去后面的往前移动
               end=mid;
           }else{                                    //在谷砍去哪边都一样,往哪里移动都可以
               end=mid;
           }
        }
        
        if(A[start]<A[end]){                         //最后只剩A[start]和A[end],哪个大哪个就是peak
            return end;
        }else{
            return start;
        }
    }
}

600. Smallest Rectangle Enclosing Black Pixels:点击打开链接

 

[
  "0010",
  "0110",
  "0100"
]

 

思路:找到上下左右的边界

 

public class Solution {
    /**
     * @param image a binary matrix with '0' and '1'
     * @param x, y the location of one of the black pixels
     * @return an integer
     */
    public int minArea(char[][] image, int x, int y) {
        if (image == null || image.length == 0) {
            return 0;
        }
        
        int n = image.length;
        int m = image[0].length;
        
        int left = findLeft(image, 0, y);
        int right = findRight(image, y, m - 1);
        int top = findTop(image, 0, x);
        int bottom = findBottom(image, x, n - 1);
        return (right - left + 1) * (bottom - top + 1);
    }
    
    private int findLeft(char[][] image, int start, int end) {   //以拿找左边界为例
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;                 //二叉中分0~y
            if (isEmptyColumn(image, mid)) {                     //当mid选定后,调用isEmptyColumn判断整个mid列是否都为0
                start = mid;                                     //如果mid所在的整列都为0,说明左边界还可以往右找
            } else {                                             //如果mid所在的整列有1,说明左边界还要往左找
                end = mid;
            }
        }                                                        
        if (isEmptyColumn(image, start)) {                       //直到范围缩减到只剩下start和end   
            return end;                                          //如果start所在整列都为0,说明左边界是end所在列 
        }                                                        //否则左边界就是start所在列
        return start;
    }
    
    private int findRight(char[][] image, int start, int end) {
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (isEmptyColumn(image, mid)) {
                end = mid;
            } else {
                start = mid;
            }
        }
        if (isEmptyColumn(image, end)) {
            return start;
        }
        return end;
    }
    
    private int findTop(char[][] image, int start, int end) {
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (isEmptyRow(image, mid)) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (isEmptyRow(image, start)) {
            return end;
        }
        return start;
    }
    
    private int findBottom(char[][] image, int start,int end){
        while(start+1<end){
            int mid=start+(end-start)/2;
            if(isEmptyRow(image, mid)){
                end=mid;
            }else{
                start=mid;
            }
        }
        if(isEmptyRow(image, end)){
            return start;
        }
        return end;
    }
    
    private boolean isEmptyColumn(char[][] image, int col) {
        for (int i = 0; i < image.length; i++) {
            if (image[i][col] == '1') {
                return false;
            }
        }
        return true;
    }
    
    private boolean isEmptyRow(char[][] image, int row) {
        for (int j = 0; j < image[0].length; j++) {
            if (image[row][j] == '1') {
                return false;
            }
        }
        return true;
    }
}

Total Occurence of Target

思路:一次二分,一次找左边界,一次找右边界,如果找左边界的时候没有就直接return 0了

public class Solution {
    /*
     * @param A: A an integer array sorted in ascending order
     * @param target: An integer
     * @return: An integer
     */
    public int totalOccurrence(int[] A, int target) {
        if(A==null || A.length==0){
           return 0;
        }
       
        int start=0;
        int end=A.length-1;
        while(start+1<end){
            int mid=start+(end-start)/2;
            if(A[mid]<target){
                start=mid;
            }else if(A[mid]>target){
                end=mid;
            }else{
                end=mid;
            }
        }
        int left;
        if(A[start]==target){
            left=start; 
        }else if(A[end]==target){
            left=end;
        }else{
            return 0;
        }
        
        start=0;
        end=A.length-1;
        while(start+1<end){
            int mid=start+(end-start)/2;
            if(A[mid]<target){
                start=mid;
            }else if(A[mid]>target){
                end=mid;
            }else{
                start=mid;
            }
        }
        int right;
        if(A[end]==target){
            right=end;
        }else if(A[start]==target){
            right=start; 
        }else{
            return 0;
        }
        return right-left+1;
    }
}

287. Find the Duplicate Number

class Solution {
    
    public int findDuplicate(int[] nums) { 
        //[2,5,4,3,2,2,2], 可以极端假设有好几个重复,好理解, mid是3
        int start = 0;
        int end = nums.length - 1;
        
        while(start + 1 < end)
        {
            int mid = start + (end - start)/2;
            int nTotal = count(mid, nums);
            if(nTotal <= mid)       
            {
                start = mid;
            }
            else{                   //数了一圈发现,比3小的个数要比3多,前面一定有重复
                end = mid;          //因为如果按每个数字都出现一次的话,比3小的个数应该最多是3
            }            
        }
        
        if(count(start, nums) <= start)
        {
            return end;
        }
        return start;
    }
    
    private int count(int mid, int[] nums)
    {
        int nTotal = 0;
        for(int i = 0; i < nums.length; i++)
        {
            if(nums[i] <= mid)
            {
                nTotal++;
            }
        }
        return nTotal;
    }
}

Note: 这是一道非常考验二分功底的题目, 挪动的mid是时间的index

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值