LeetCode 总结——二分查找

写在前面: 本人是某大学软件工程专业大三的学生,写这个专栏,是为了总结自己刷的LeetCode题,温故而知新,同时分享给这条路上的朋友们。由于本人水平有限,博客中难免有些错误,欢迎朋友们来指正,也很乐意与各位朋友交流学习经验。虽然现在还很菜,但是,我相信只要不断的学习、积累,总会有成为大佬的那一天。

1. 面试题53 - I. 在排序数组中查找数字 I(简单)

统计一个数字在排序数组中出现的次数。
示例 1:

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2

示例 2:

输入: nums = [5,7,7,8,8,10], target = 6
输出: 0

解题思路:

利用二分查找,先找到,最右边等于target的数组元素索引为r,再利用二分查找,找到左边第一个等于target的元素索引为l,最后数字在排序数组中出现的次数就是r-l+1。

代码

class Solution {
    public int search(int[] nums, int target) {
       if(nums == null || nums.length == 0) return 0;
        int left = 0;
        int right = nums.length - 1;
        //找到右边最后一个target
        while(left <= right){
            int mid = left + (right - left) / 2;
            //注意等于的时候是让mid+1,缩小左边界范围
            if(nums[mid] <= target){
                //下一次搜索的区间是[mid+1,right];
                left = mid + 1;
            }else if(nums[mid] > target){
                right = mid - 1;
            }
        }
        //循环结束的时候,如果数组中有target,right指向最右边那个
        //如果没有右边界,说明数组中没有等于target的元素,直接返回0,就不用去找左边界了
        if(right >= 0 && nums[right] != target) return 0;
        int r = right;
        //找到左边第一个target
        left = 0;
        right= nums.length - 1;
        while(left <= right){
            int mid = left + (right - left) / 2;
            if(nums[mid] < target){
                left = mid + 1;
            }else{
                //当nums[mid]=target的时候,让right=mid-1,缩小左边界范围
                //下一次搜索的区间是[left,right-1]
                right = mid - 1;
            }
        }
        return r - left + 1;
    }
}

时间复杂度:O(log n) 空间复杂度:O(1)
总结

注意查找右边最后一个等于target的元素和左边第一个等于target的元素的条件,很类似,唯一的不同点就在于,前者nums[mid]=target时,让left= mid+1,缩小左边界,后者是让right=mid-1,缩小有边界。还有while中的条件,这道题用的是<=,如果不带等于号的话,数组只有一个元素或者只有两个元素的情况就没有办法计算。

改进
以上代码显得比较臃肿(两轮二分查找代码冗余)。为简化代码,可将二分查找右边界 rightright 的代码 封装至函数 helper() 。
在这里插入图片描述
如上图所示,由于数组 numsnums 中元素都为整数,因此可以分别二分查找 target 和 target - 1 的右边界,将两结果相减并返回即可。
代码:

class Solution {
    public int search(int[] nums, int target) {
        return helper(nums,target) - helper(nums,target-1);
    }
    //找最右边等于target的数组元素
    public int helper(int[] nums, int target){
        int left = 0;
        int right = nums.length - 1;
        while(right >= left){
            int mid = left + (right - left) / 2;
            if(nums[mid] <= target){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        return right;
    }
}

参考
题解

2. 面试题53 - II. 0~n-1中缺失的数字(简单)

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例 1:

输入: [0,1,3]
输出: 2

示例 2:

输入: [0,1,2,3,4,5,6,7,9]
输出: 8

解题思路

利用二分查找,数组可以分成两部分,左部分是从第一个元素开始到缺失元素的前一个元素,右部分是从缺失元素到最后一个元素,左部分中,元素的值和索引值相等,右部分中,元素值大于索引值。所以,利用这个特点,使用二分查找,当数组值和索引相等的时候,说明缺失值在当前元素的右边,如果数组值大于索引,说明缺失值在当前元素的左边。

代码:

class Solution {
    public int missingNumber(int[] nums) {
       int left = 0;
       int right = nums.length - 1;
       while(right >= left){
           int mid = left + (right - left) / 2;
           //相等时,往右查找
           if(nums[mid] == mid){
               left = mid + 1;
           }else{
           //不相等,往左查找
               right = mid - 1;
           }
       }
       //结束循环的时候,left 在右半部分的第一个位置,索引等于缺失的值
       return left;
    }
}

时间复杂度:O(logN) 空间复杂度:O(1)
参考
题解

持续更新

猜你喜欢

转载自blog.csdn.net/weixin_43691723/article/details/106149882