二分查找

二分查找

一、基本原理

二分查找又名折半查找,是一种效率较高的查找方法,时间复杂度是O(logn),要求待查找序列必须有序。

二、传统代码模板

public int binarySearch(int[] nums, int target) {
     
    int low = 0;
    int high = nums.length - 1;
    while (low <= high){
     
        int mid = (low + high) / 2;
        if (nums[mid] == target){
     
            return mid;
        }else if (nums[mid] < target){
     
            low = mid + 1;
        }else {
     
            high = mid - 1;
        }
    }
    return low;
}

存在的问题如下:

1、当待查找序列的长度非常大的时候,取mid时可能出现整型溢出。

2、退出while循环后,需要考虑到底是返回low还是high。

三、改进版

1、中位数取法

int mid = low + (high - low) / 2;

或者

int mid = (low + high) >>> 1;

2、while循环条件修改为low,当退出循环的时候,low==high成立,此时返回值就可以任选其一。

public int binarySearch(int[] nums, int target) {
     
    int low = 0;
    int high = nums.length;
    int mid = 0;
    while (low < high){
     
        mid = low + (high - low) / 2;
        if (judge()){
     
            low = mid + 1;
        }else {
     
            high = mid;
        }
    }
    return low;
}

public int searchInsert(int[] nums, int target) {
     
    int low = 0;
    int high = nums.length;
    int mid = 0;
    while (low < high){
     
        mid = low + (high - low + 1) / 2;
        if (judge()){
     
            high = mid - 1;
        }else {
     
            low = mid;
        }
    }
    return low;
}

四、例题

1、LeetCode35 搜索插入位置

class Solution {
     
    public int searchInsert(int[] nums, int target) {
     
        int low = 0;
        int high = nums.length;
        int mid = 0;
        while (low < high){
     
            mid = low + (high - low) / 2;
            //当nums[mid]
            //但是当nums[mid]>target时,却不能直接更新上边界high=mid-1,因为mid可能为插入位置
            if (nums[mid] < target){
     
                low = mid + 1;
            }else {
     
                high = mid;
            }
        }
        return low;
    }
}

2、LeetCode69 x的平方根

class Solution {
     
    public int mySqrt(int x) {
     
        if (x == 0 || x == 1){
     
            return x;
        }
        int low = 0;
        int high = x / 2;
        while (low < high){
     
            int mid = low + (high - low + 1) / 2;
            //根据题意,最后结果是向下取整,所以当mid>x/mid时,直接更新上边界,因为mid的平方不可能比x大
            //但是当mid
            if (mid > x / mid){
     
                high = mid - 1;
            }else {
     
                low = mid;
            }
        }
        return low;
    }
}

3、LeetCode162 寻找峰值

class Solution {
     
    public int findPeakElement(int[] nums) {
     
         if (nums.length == 1){
     
            return 0;
        }
        if (nums.length == 2){
     
            if (nums[0] > nums[1]){
     
                return 0;
            }else {
     
                return 1;
            }
        }
        int start = -1;
        int end = nums.length;
        while (start < end){
     
            int mid = start + (end - start) / 2;
            int left = mid - 1 < 0 ? Integer.MIN_VALUE : nums[mid - 1];
            int right = mid + 1 == nums.length ? Integer.MIN_VALUE : nums[mid + 1];
            //如果nums[mid]的值大于左右值,那么mid即为峰值位置
            //如果nums[mid]的值比左边大,比右边小,说明nums[mid]处于一个上升的序列当中,那么需要更新下边界,继续寻找峰值,但是不能直接start=mid+1,因为此时只能确定[start,mid-1]区间是要舍弃的区间,需要去[mid,end]区间中寻找峰值。更新上边界的原理与此相似。
            if (nums[mid] > left && nums[mid] > right){
     
                return mid;
            }else if (nums[mid] > left && nums[mid] < right){
     
                start = mid;
            }else {
     
                end = mid;
            }
        }
        return 0;
    }
}

你可能感兴趣的:(等待开学)