二分查找 二分查找

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。

二分查找从思路上来说并不复杂,但是在细节上有很多需要注意的地方。

1.二分查找框架

int binarySearch(int[] nums, int target) {
    int left = 0, right = ...;

    while(...) {
        //这里使用left+(left+right)/2而不使用(left+right)/2是为了避免left+right结果太大导致溢出
        int mid = left+(right-left) / 2;
        if (nums[mid] == target) {
            ...
        } else if (nums[mid] < target) {
            left = ...
        } else if (nums[mid] > target) {
            right = ...
        }
    }
    return ...;
}

2.寻找一个数(基本二分搜索)

在一个有数排列中寻找一个数

int binarySearch(int[] nums, int target) {
    int left = 0, right = nums.length-1;

    while(right>=left) {
        //这里使用left+(left+right)/2而不使用(left+right)/2是为了避免left+right结果太大导致溢出
        int mid = left+(right-left) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid+1;
        } else if (nums[mid] > target) {
            right = mid-1;
        }
    }
    return -1;
}

如果排列是一个数组为[1,2,2,2,3,3,4],怎样找到数组最左侧或者最右侧的2呢?

这里我们要引出左侧边界二分搜索和右侧边界二分搜索

3.左侧边界二分搜索

主要是要理解在左侧边界搜索时,target<=nums[mid]时,right=mid-1;这一步

int left_bound(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] >= target) {
            right = mid - 1;
        } 
    }
    // 最后要检查 left 越界的情况
    if (left >= nums.length || nums[left] != target)
        return -1;
    return left;
}

4.右侧边界二分搜索

同理,理解target>=nums[mid]时,left=mid+1;

int right_bound(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] <= target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } 
    }
    // 最后要检查 right 越界的情况
    if (right < 0 || nums[right] != target)
        return -1;
    return right;
}