算法:二分法


二分法题型:

(常规的三种主流写法:(<=)对应mid+1,mid-1;    (<)对应mid+1,mid;  (+1<)对应mid,mid;) 

(无证明推测:一般找具体tar的在循环内部返回的才会用<=(指的是返回全部情况 而不是个别情况);因为用<=循环后lo!=hi了,而是lo=hi+1;

所以不是内部返回的一般都用(lo<hi),对应的下面指针变化必须要有一个是+1/-1的,不能两个都是mid;

mid偏左,则lo=mid+1;mid偏右,则hi=mid-1;) 

1.Classical Binary Search:http://www.lintcode.com/en/problem/classical-binary-search/

经典二分查找:

解法:(循环条件<=; 指针变化lo=mid+1 /hi=mid-1;)(PS: mid=lo+((hi - lo)>>1 防止溢出+移位替代除法提升速度)

public class Solution {

    public int findPosition(int[] nums, int target) {

        int lo = 0;
        int hi = nums.length - 1;
        while (lo <= hi) {
            int mid = lo + ((hi - lo) >> 1);
            if (nums[mid] == target) return mid;
            else if (nums[mid] < target) lo = mid + 1;
            else hi = mid - 1;
        }
        return -1;

    }

}
View Code
public class Solution {

    public int findPosition(int[] nums, int target) {

        int lo = 0;
        int hi = nums.length - 1;
        while (lo < hi) {
            int mid = lo + ((hi - lo) >> 1);
            if (nums[mid] == target) return mid;
            else if (nums[mid] > target) hi = mid;
            else lo = mid + 1;
        }
        return -1;
    }
}
View Code

2.First Position of Target:http://www.lintcode.com/en/problem/first-position-of-target/

查找第一个目标数:

解法1:(在while循环内,使用一个rst记录=tar的mid值;while(lo<=hi),如果>=tar,则hi=mid-1,否则lo=mid+1) 

(具体过程分析:与常规的二分查找相同,不过是当=tar的时候,1.rst记录值,防止此时的mid已经是最左了;2.hi=mid-1继续查找左边的数;)

class Solution {

    public int binarySearch(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        
        int lo = 0, hi = nums.length - 1;
        int rst = -1;
        while (lo <= hi) {
            int mid = lo + ((hi - lo) >> 1);
            if (nums[mid] >= target) {
                hi = mid - 1;
            } else {
                lo = mid + 1;
            }
            if (nums[mid] == target) rst = mid;
        }
        return rst;

    }
}
View Code

解法2:(while(lo<hi); 如果=tar,则hi=mid,大于则mid-1,小于则mid+1;最后循环外判断lo=tar?lo;)

(注意:一定是lo<hi而不能是<=;不然错误!例如[1,4],1;  1-lo/mid, 4-hi; 1-lo/mid/hi;死循环!即)

class Solution {

    public int binarySearch(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        
        int lo = 0, hi = nums.length - 1;
        while (lo < hi) {
            int mid = lo + ((hi - lo) >> 1);
            if (nums[mid] == target) {
                hi = mid;
            } else if (nums[mid] < target) {
                lo = mid + 1;
            } else {
                hi = mid - 1;
            }
        }
        if (nums[lo] == target) {
            return lo;
        }
        
        return -1;
    }
}
View Code

(或者合并=和>) (使用这种解法

循环条件(lo<hi); mid=lo+((hi-lo)>>1); 判断if(>=)hi=mid;  否则lo=mid+1; 循环外判断[lo]=tar;

class Solution {

    public int binarySearch(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        
        int lo = 0, hi = nums.length - 1;
        while (lo < hi) {
            int mid = lo + ((hi - lo) >> 1);
            if (nums[mid] < target) {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        if (nums[lo] == target) {
            return lo;
        }
        
        return -1;
    }
}
View Code

解法3:(九章模板;(+1<) + mid + 分别判断lo和hi;)

class Solution {

    public int binarySearch(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) {
                end = mid;
            } else if (nums[mid] < target) {
                start = mid;
                // or start = mid + 1
            } else {
                end = mid;
                // or end = mid - 1
            }
        }
        
        if (nums[start] == target) {
            return start;
        }
        if (nums[end] == target) {
            return end;
        }
        return -1;
    }
}
View Code

3.Find Last Position of Targethttp://www.lintcode.com/zh-cn/problem/last-position-of-target/

查找最后一个目标数: 

解法1:(与First解法1相同)

class Solution {

    public int binarySearch(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        
        int lo = 0, hi = nums.length - 1;
        int rst = -1;
        while (lo <= hi) {
            int mid = lo + ((hi - lo) >> 1);
            if (nums[mid] <= target) {
                lo = mid + 1;
            } else {
                hi = mid - 1;
            }
            if (nums[mid] == target) rst = mid;
        }
        return rst;

    }
}
View Code

解法2:

(循环条件(lo<hi); mid=lo+((hi-lo+1)>>1); 判断if(<=)lo=mid;  否则hi=mid-1; 循环外判断[hi]=tar;)

class Solution {

    public int binarySearch(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        
        int lo = 0, hi = nums.length - 1;
        while (lo < hi) {
            int mid = lo + ((hi - lo + 1) >> 1);
            if (nums[mid] <= target) {
                lo = mid ;
            } else {
                hi = mid - 1;
            }
        }
        if (nums[hi] == target) {
            return hi;
        }
        
        return -1;
    }
}
View Code

4.Search for a Range:http://www.lintcode.com/en/problem/search-for-a-range/

搜索区间(找出第一个和最后一个tar的位置):

解法1:(即整合First和Last的解法即可;)

 1 public class Solution {
 2     public int[] searchRange(int[] A, int target) {
 3         int[] rst = new int[]{-1,-1};
 4         if (A.length == 0) {
 5             return rst;
 6         }
 7         int lo = 0, hi = A.length - 1;
 8         while (lo <= hi) {
 9             int mid = lo + (hi - lo) / 2;
10             if (A[mid] >= target) {
11                 hi = mid - 1;
12             } else {
13                 lo = mid + 1;
14             }
15             if (A[mid] == target) rst[0] = mid;
16         }
17         if (rst[0] == -1) {
18             return rst;
19         }
20         lo = rst[0]; 
21         hi = A.length - 1;
22         while (lo <= hi) {
23             int mid = lo + (hi - lo) / 2;
24             if (A[mid] <= target) {
25                 lo = mid + 1;
26             } else {
27                 hi = mid - 1;
28             }
29             if (A[mid] == target) rst[1] = mid;
30         }
31         return rst;
32     }
33 }
View Code

解法2:(//First: lo+(hi-lo)/2; while(<); (>=)mid; mid+1; if(lo) //Last : lo+(hi-lo+1)/2; while(<=); (<=)mid; mid-1; if(hi);)

(注意:第二个一定要是lo+(hi-lo+1)/2;不然mid偏左则会死循环;例如[5,5],5;)

public class Solution {
    public int[] searchRange(int[] A, int target) {
        int[] rst = new int[]{-1,-1};
        if (A.length == 0) {
            return rst;
        }
        int lo = 0, hi = A.length - 1;
        while (lo < hi) {
            int mid = lo + (hi - lo) / 2;
            if (A[mid] >= target) {
                hi = mid;
            } else {
                lo = mid + 1;
            }
        }
        if (A[lo] == target) rst[0] = lo;
        else return rst;
        

        hi = A.length - 1;
        while (lo < hi) {
            int mid = lo + (hi - lo + 1)/ 2;
            if (A[mid] <= target) {
                lo = mid;
            } else {
                hi = mid - 1;
            }
        }
        if (A[hi] == target) rst[1] = hi;
        return rst;
    }
}
View Code

解法3:(九章模板)

public class Solution {
    public int[] searchRange(int[] A, int target) {
        if (A.length == 0) {
            return new int[]{-1, -1};
        }
        
        int start, end, mid;
        int[] bound = new int[2]; 
        
        // search for left bound
        start = 0; 
        end = A.length - 1;
        while (start + 1 < end) {
            mid = start + (end - start) / 2;
            if (A[mid] == target) {
                end = mid;
            } else if (A[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (A[start] == target) {
            bound[0] = start;
        } else if (A[end] == target) {
            bound[0] = end;
        } else {
            bound[0] = bound[1] = -1;
            return bound;
        }
        
        // search for right bound
        start = 0;
        end = A.length - 1;
        while (start + 1 < end) {
            mid = start + (end - start) / 2;
            if (A[mid] == target) {
                start = mid;
            } else if (A[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (A[end] == target) {
            bound[1] = end;
        } else if (A[start] == target) {
            bound[1] = start;
        } else {
            bound[0] = bound[1] = -1;
            return bound;
        }
        
        return bound;
    }
}
View Code

5.Find Minimum in Rotated Sorted Array:http://www.lintcode.com/en/problem/find-minimum-in-rotated-sorted-array/

寻找旋转排序数组中的最小值:

解法1:(while(lo<hi); 首先判断([lo]>[hi])return[lo]; 然后再判断[mid]和[start]; >=则lo=mid+1; 否则hi=mid; return[lo];)

(其实思想是找出第一个小于[lo]的值;比较[mid]和[lo];)

([5,6,7,1,2,3,4] 如果[lo]<[hi] 那么说明没有旋转; 比较mid和start,如果>=,说明旋转的在后半部分(不含mid)例如[2,1]/[2,3,1],而[2,3,4]是已经判断过了的所以不会出现;如果<,说明在前半部分(包含mid),例如[3,1,2]包含mid)

public class Solution {

    public int findMin(int[] nums) {

        int lo = 0, hi = nums.length - 1;
        while (lo < hi) {
            if (nums[lo] < nums[hi]) return nums[lo];
            int mid = lo + (hi - lo) / 2;
            if (nums[mid] >= nums[lo]) lo = mid + 1;
            else hi = mid;
        }
        return nums[lo];

    }
}
View Code

解法2:(思想:找出第一个小于[hi]的值;如果mid<=hi, 那么hi=mid;mid>hi, 那么mid那么lo=mid; 与Find First的套路一致! 其实和解法1一样……解法1更优化.

不过这里的[hi]是变化的,而九章的解法里是把hi固定为最后一位)

public class Solution {
    public int findMin(int[] nums) {
        int lo = 0, hi = nums.length - 1;
        while (lo < hi) {
            int mid = lo + (hi - lo) / 2;
            if (nums[mid] > nums[hi]) lo = mid + 1;
            else hi = mid;
        }
        return nums[lo];
    }
}
View Code

(注意:如果使用解法1,则必须判断[lo]<[hi]情况;而解法2则不需要;) 

6.Search a 2D Matrix : http://www.lintcode.com/en/problem/search-a-2d-matrix/

搜索二维矩阵:

解法1:(两次二分搜索;第一次循环必须是while(+1<),对应循环内部是mid,mid;最后分别判断[end]和[start]与tar; 第二次循环,普通的二分法;)

public class Solution {
    /**
     * @param matrix, a list of lists of integers
     * @param target, an integer
     * @return a boolean, indicate whether matrix contains target
     */
    public boolean searchMatrix(int[][] matrix, int target) {
        // write your code here
        if (matrix == null || matrix.length == 0){
            return false;
        }
        if (matrix[0] == null || matrix[0].length == 0){
            return false;
        }
        int start = 0;
        int end = matrix.length - 1;
        int row ;
        while (start + 1 < end) {
            int mid = start + (end - start)/2;
            if (matrix[mid][0] == target){
                return true;
            } else if (matrix[mid][0] < target) {
                start = mid ;
            } else {
                end = mid ;
            }
        }
        if (matrix[end][0] <= target){
            row = end;
        } else if (matrix[start][0] <= target){
            row = start;
        } else {
            return false;
        }
     
        start = 0;
        end = matrix[0].length - 1;
        while (start <= end) {
            int mid = start + (end - start)/2;
            if (matrix[row][mid] == target){
                return true;
            } else if (matrix[row][mid] < target) {
                start = mid + 1;
            } else {
                end = mid - 1;
            }
        }
        return false;
    }
}
View Code

解法2:(一次二分搜索;看作是将Matrix看成m*n长的list,mid对应的number=Matrix[mid/col][mid%col])

public class Solution {
    /**
     * @param matrix, a list of lists of integers
     * @param target, an integer
     * @return a boolean, indicate whether matrix contains target
     */
    public boolean searchMatrix(int[][] matrix, int target) {
        // write your code here
        if (matrix == null || matrix.length == 0){
            return false;
        }
        if (matrix[0] == null || matrix[0].length == 0){
            return false;
        }
        int start = 0;
        int row = matrix.length;
        int col = matrix[0].length;
        int end = row * col - 1;
        while (start <= end){
            int mid = start + (end - start) / 2;
            int num = matrix[mid / col][mid % col];
            if (num == target){
                return true;
            } else if (num < target){
                start = mid + 1;
            } else {
                end = mid - 1;
            }
        }
        return false;
    }
}
View Code

LeetCode:

1.Search in Rotated Sorted Array:https://leetcode.com/problems/search-in-rotated-sorted-array/#/description

找出旋转排序数组的tar值:

(findMin+常规Binary Search)

解法:(先找出最小值位置index:(lo<hi)三步-判断([lo]<[hi]),判断([lo]<=mid),else;index是最小值位置,也是数组旋转的位数; 再用常规二分搜索- realmid=(mid+index)%n;判断realMid和tar,指针移动为mid+1/mid-1;)

public class Solution {
    public int search(int[] nums, int target) {
        int lo = 0,  n = nums.length;
        int hi = n - 1;
        int index = findMinIndex(nums);
        while (lo <= hi) {
            int mid = lo + (hi - lo) / 2;
            int realMid = (mid + index) % n;
            if (nums[realMid] == target) return realMid;
            else if (nums[realMid] < target) lo = mid + 1;
            else hi = mid - 1;
        }
        return -1;
    }
    public int findMinIndex(int[] nums) {
        int lo = 0, hi = nums.length - 1;
        while (lo < hi) {
            if (nums[lo] < nums[hi]) return lo;
            int mid = lo + (hi - lo) / 2;
            if (nums[lo] <= nums[mid]) lo = mid + 1;
            else hi = mid;
        }
        return lo;
    }
}
View Code

2.Find Minimum in Rotated Sorted Array II:https://leetcode.com/problems/find-minimum-in-rotated-sorted-array-ii/#/description

找出旋转排序数组最小值II(含重复):

解法1:(基于Minimum的解法1;区别就是mid=lo的时候,lo++)

public class Solution {
    public int findMin(int[] nums) {
        int lo = 0, hi = nums.length - 1;
        while (lo < hi) {
            if (nums[lo] < nums[hi]) return nums[lo];
            int mid = lo + (hi - lo) / 2;
            if (nums[mid] > nums[lo]) lo = mid + 1;
            else if (nums[mid] < nums[lo]) hi = mid;
            else lo++;
        }
        return nums[lo];
    }
}
View Code

解法2:(与MinimumI解法2相同,区别就是mid=hi的时候,hi--,这样此时不会移除最小值;因为mid=hi有两种情况,一是mid-hi之间全部相等,二是mid前的所有数都等于hi;其实和解法1一样……)

 1 public class Solution {
 2     public int findMin(int[] nums) {
 3         int lo = 0, hi = nums.length - 1;
 4         while (lo < hi) {
 5             int mid = lo + (hi - lo) / 2;
 6             if (nums[mid] > nums[hi]) lo = mid + 1;
 7             else if (nums[mid] < nums[hi]) hi = mid;
 8             else hi--;
 9         }
10         return nums[lo];
11     }
12 }
View Code

3.Search Insert Position:https://leetcode.com/problems/search-insert-position/#/description

寻找插入位置(找到tar则返回改位置,否则返回插入位置):

解法:(和常规Binary Search相同;唯一区别是BS找不到返回-1,该题找不到返回lo!  [2,4]示例,lo/mid-2,hi-4;若tar-3 则mid<tar, lo=mid+1; 此时lo刚好为目标位置;若tar-1 则mid>tar,hi=mid-1,为lo的前一个位置,但是插入的位置是要替换lo的位置的,所以目标位置仍然为lo; )

public class Solution {
    public int searchInsert(int[] nums, int target) {
        int lo = 0, hi = nums.length - 1;
        while (lo <= hi) {
            int mid = lo + (hi - lo) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] > target) {
                hi = mid - 1;
            } else {
                lo = mid + 1;
            }
        }
        return lo;
    }
}
View Code
原文地址:https://www.cnblogs.com/buwenyuwu/p/6541214.html