算法总结-二分搜索

一. 二分搜索(Binary Search)模板

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 == null || nums.length == 0){
            return -1;
        }
        int left = 0;
        int right = nums.length - 1;
        int mid = 0;
        while(left + 1 < right){
            mid = left + (right - left)/2;
            if(nums[mid] == target){
                return mid;
            }else if(nums[mid] < target){
                left = mid;
            }else{
                right = mid;
            }
        }
        
        if(nums[left] == target){
            return left;
        }
        if(nums[right] == target){
            return right;
        }
        
        return -1;
    }
}

注意事项: 

1). start + 1 < end (最后会剩下start, end两项);

2). mid = start + (end - start) / 2; (避免出现start + end越界情况);

3). A[mid] 依次比较 ==, <, >;

4). A[start], A[end]与target比较。


二 常见问题

2.1 基础二分问题变种

1)Search a 2D matrix i

http://www.lintcode.com/zh-cn/problem/search-a-2d-matrix/

思路:将二维矩阵看做线性序列,则为典型二分搜索问题,所以只需要线性序列与二维数组小标的对应

即: mid / n , mid % n

public class Solution {
    /*
     * @param matrix: 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 row = matrix.length;
        int column = matrix[0].length;
        int start = 0;
        int end = row * column - 1;
        while(start + 1 < end){
            int mid = start + (end - start)/2;
            int number = matrix[mid/column][mid%column];
            if(number == target){
                return true;
            }else if(number < target){
                start = mid;
            }else{
                end = mid;
            }
        }
        
        if(matrix[start/column][start%column] == target|| matrix[end/column][end%column] == target){
            return true;
        }
        return false;
    }
}

2)Search a 2D matrix ii

http://www.lintcode.com/zh-cn/problem/search-a-2d-matrix-ii/

思路:根据矩阵的性质,从左下角向右上寻找。

如果target大于当前元素,当前列上方元素可删除,所以向右走一步;

如果target小于当前元素,当前行右方元素可删除,所以向上走一步。


public class Solution {
    /*
     * @param matrix: A list of lists of integers
     * @param target: An integer you want to search in matrix
     * @return: An integer indicate the total occurrence of target in the given matrix
     */
    public int searchMatrix(int[][] matrix, int target) {
        // write your code here
        if(matrix == null || matrix.length == 0){
            return 0;
        }
        if(matrix[0] == null || matrix[0].length == 0){
            return 0;
        }
        
        int row = matrix.length;
        int column = matrix[0].length;
        int x = row - 1;
        int y = 0;
        int count = 0;
        while(x >= 0 && y < column){
            if(matrix[x][y] < target){
                y++;
            }else if(matrix[x][y] > target){
                x--;
            }else{
                x--;
                y++;
                count++;
            }
        }
        
        return count;
    }
}

2.2 first/last position 问题

1)First Position of Target

http://www.lintcode.com/en/problem/first-position-of-target/

思路:找到第一个目标值出现的位置

public class Solution {
    /**
     * @param nums: The integer array.
     * @param target: Target to find.
     * @return: The first position of target. Position starts from 0.
     */
    public int binarySearch(int[] nums, int target) {
        // write your code here
        if(nums == null || nums.length == 0){
            return -1;
        }
        int left = 0;
        int right = nums.length - 1;
        while(left + 1 < right){
            int mid = left + (right - left)/2;
            if(nums[left] == target){
                right = mid;
            }else if(nums[mid] < target){
                left = mid;
            }else{
                right = mid;
            }
        }
        
        if(nums[left] == target){
            return left;
        }
        if(nums[right] == target){
            return right;
        }
        return -1;
    }
}

2) Search insert position

http://www.lintcode.com/en/problem/search-insert-position/

思路:在数组中找到第一个大于等于target的位置(find first position)

public class Solution {
    /**
     * @param A: an integer sorted array
     * @param target: an integer to be inserted
     * @return: An integer
     */
    public int searchInsert(int[] A, int target) {
        // write your code here
        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){
                return mid;
            }else if(A[mid] < target){
                start = mid;
            }else{
                end = mid;
            }
        }
        
        if(A[start] >= target){
            return start;
        }
        else if(A[end] >= target){
            return end;
        }
        else if(A[end] < target){
            return end+1;
        }
        return -1;
    }
}

3) Find bad version

http://www.lintcode.com/en/problem/first-bad-version/

思路:找到第一个bad version(first position)

/**
 * 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.
*/

public class Solution {
    /*
     * @param n: An integer
     * @return: An integer which is the first bad version.
     */
    public int findFirstBadVersion(int n) {
        // write your code here
        int left = 1;
        int right = n;
        while(left + 1 < right){
            int mid = left + (right - left)/2;
            if(SVNRepo.isBadVersion(mid)){
                right = mid;
            }else{
                left = mid;
            }
        }
        if(SVNRepo.isBadVersion(left)){
            return left;
        }
        if(SVNRepo.isBadVersion(right)){
            return right;
        }
        
        return -1;
    }
}

4) Sqrt(x)

http://www.lintcode.com/en/problem/sqrtx/

思路:找到最后一个平方小于等于x的数

public class Solution {
    /**
     * @param x: An integer
     * @return: The sqrt of x
     */
    public int sqrt(int x) {
        // write your code here
        if(x < 0){
            return -1;
        }
        long start = 0;
        long end = x;
        while(start + 1 < end){
            long mid = start + (end - start)/2;
            if(mid * mid == x){
                return (int)mid;
            }else if(mid * mid < x){
                start = mid;
            }else{
                end = mid;
            }
        }
        
        if(end * end == x){
            return (int)end;
        }
        return (int)start;
        
    }
}

5)Search for a range(total-occurrence-of-target)

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

思路:使用两遍模板,分别找到first 和 last position,则找到对应区间

public class Solution {
    /**
     * @param A: an integer sorted array
     * @param target: an integer to be inserted
     * @return: a list of length 2, [index1, index2]
     */
    public int[] searchRange(int[] A, int target) {
        // write your code here
        if(A == null || A.length == 0){
             return new int[]{-1, -1};
        }
        
        int[] bound = new int[2];
        
        //寻找左边界,即第一次出现target值的位置
        int start = 0;
        int end = A.length - 1;
        while(start + 1 < end){
            int 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] = -1;
            bound[1] = -1;
        }
        
        //寻找右边界,即最后一次出现target值的位置
        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){
                start = mid;
            }else{
                end = mid;
            }    
        }
        
        if(A[end] == target){
            bound[1] = end;
        }
        else if(A[start] == target){
            bound[1] = start;
        }else{
            bound[0] = -1;
            bound[1] = -1;
        }
        
        return bound;
    }
}

2.3 较复杂二分搜索问题(包含旋转排序数组内搜索)

这几个问题仍然采用二分思想,但二分后删除部分集合的调节并不明显,往往需要通过画图帮助理解。

同时此类问题很多也可以转换成增加条件的first position / last position问题,从而使用二分模板。


1) Find Minimum in Rotated Sorted Array

http://www.lintcode.com/en/problem/find-minimum-in-rotated-sorted-array/

思路: 根据旋转排序数组性质,问题转化为find first element which is bigger than nums[end];

public class Solution {
    /**
     * @param nums: a rotated sorted array
     * @return: the minimum number in the array
     */
    public int findMin(int[] nums) {
        // write your code here
        if(nums == null || nums.length == -1){
            return -1;
        }
        
        int start = 0;
        int end = nums.length - 1;
        while(start + 1 < end){
            int mid = start + (end - start)/2;
            if(nums[mid] <= nums[end]){
                //此时中间数在旋转点的右分支
                end = mid;
            }else{
                start = mid;
            }
        }
        
        return nums[start] > nums[end]?nums[end]:nums[start];
    }
}

2) Search in Rotated Sorted Array

http://www.lintcode.com/en/problem/search-in-rotated-sorted-array/

思路:分情况进行“二分”的删除区间操作

首先区分target与nums[end]的关系

其次在不同区间内,根据target与nums[mid]的关系进行相关删除区间操作。

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) {
        // write your code here
        if(A == null || A.length == 0){
            return -1;
        }
        int start = 0;
        int end = A.length - 1;
        while(start + 1 < end){
            int mid = start + (end - start)/2;
            if(A[mid] == target){
                return mid;
            }
            if(A[mid] > A[end]){
                //此时start和mid在同一个递增数组上
                //接下来可以在有序数组上利用二分查找
                if(A[start] <= target && target <= A[mid]){
                    end = mid;
                }else{
                    start = mid;
                }
            }else{
                //此时mid和end在同一个递增数组上
                //接下来可以在有序数组上利用二分查找
                if(A[mid] <= target && target <= A[end]){
                    start = mid;
                }else{
                    end = mid;
                }
            }
        }
        
        if(A[start] == target){
            return start;
        }
        if(A[end] == target){
            return end;
        }
        
        return -1;
    }
}

3) Find peak element

 http://www.lintcode.com/en/problem/find-peak-element/

思路:因为题目假设了相邻位置的元素不相同,并且A[0]A[length-1].我们可以通过判断A[mid],A[mid-1],A[mid+1]的关系来去除左右半边

public class Solution {
    /*
     * @param A: An integers array.
     * @return: return any of peek positions.
     */
    public int findPeak(int[] A) {
        // write your code here
        if(A == null || A.length == 0){
            return -1;
        }
        int start = 1;
        int end = A.length - 2;
        while(start+1 < end){
            int mid = start + (end - start)/2;
            if(A[mid-1] < A[mid] && A[mid+1] < A[mid]){
                return mid;
            }
            else if(A[mid] < A[mid-1]){
                //如果左边的节点比mid大,那么我们可以继续在左半区间查找,
                //因为左边可以证明一定存在一个peak element
                end = mid;
            }else if(A[mid+1] > A[mid]){
                start = mid;
            }
        }
        
        if(A[start] < A[end]){
            return end;
        }else{
            return start;
        }
        
    }
}






你可能感兴趣的:(leetcode刷题,LintCode的刷题)