剑指Offer 53、二分查找的应用

98 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

题目:53题对应了两题,两题的思路都是二分查找,首先第一题:

  1. 在排序数组中查找数字,统计所需查找数字的次数。
  2. 找到长度为n-1的数组中缺失的数字,数组中的元素范围在0-n-1

解题思路

两题存在一个共同点,都是数组已经是有序数组,那解决有序数组最常用的方法就是二分法,本题是否可以用到呢?两题都是查找有序数组中的某个数字,因此必然可用二分法。

对于第一题,统计数字的次数,常规的思路是遍历一遍数组,每次比较元素的值,统计次数即可,这样时间复杂度为O(n)O(n),并且毫无逻辑思路,在面试时肯定是不可取的。因为数组已经有序,因此我们可以对数组按照中间进行切分,这样比较中间元素和target的大小即可判断最终查找的元素在数组的哪一部分,之后递归即可得出结果,可得代码如下:

private int count = 0;

public int search(int[] nums, int target) {
    binarySearch(nums, target, 0, nums.length-1);
    return count;
}

public void binarySearch(int[] nums, int target, int left, int right){
    if(left>right) return;
    int mid = left + (right-left) / 2;
    if(nums[mid]<target){
        binarySearch(nums, target, mid+1, right);
    }else if(nums[mid]>target){
        binarySearch(nums, target, left, mid-1);
    }else {
        count++;
        binarySearch(nums, target, left, mid-1);
        binarySearch(nums, target, mid+1, right);
    }
}

回溯法思路正确但略显繁琐,实际上我们只需要不断更新左右边界即可,而当中间元素等于target时如何更新边界呢?此时可通过不断收缩左右边界的方式得到最终的边界,可得如下代码:

public int search3(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;
        } else {
            if(nums[left]!=target) {
                left++;
            }else if(nums[right]!=target) {
                right--;
            }else {
                break;
            }
        }
    }
    return right-left+1;
}

这样时间复杂度即为log(n)log(n)

对于第二题,同样可根据二分法,首先进行二分,判断中间元素的值是否正确,如果正确则说明不正确的元素应该在数组的另一侧,此时更新左边界即可。但如果不正确,则代表右边的部分必然是正确的,不正确的数只可能出现在左边,更新边界即可,可得代码如下:

public int missingNumber(int[] nums) {
    int left = 0, right = nums.length-1;
    while(left<right){
        int mid = left + (right-left) / 2;
        if(nums[mid] == mid){
            left = mid + 1;
        }else {
            right = mid - 1;
        }
    }
    return left;
}

最终时间复杂度为log(N)log(N)