剑指 offer 面试题 53 在排序数组中查找数字 I(二分查找优化、必须重点回顾)

博客探讨了在排序数组中查找特定数字的算法,主要使用二分查找方法,包括基本的二分查找及其优化版,优化后的算法能在目标数字重复时仍保持O(logn)的时间复杂度。同时,还提到了一个相关的题目——寻找0~n-1中缺失的数字II,同样使用二分查找策略解决。博主强调了二分查找的易错点,并提醒注意避免过多的if-else-if结构。
摘要由CSDN通过智能技术生成

在排序数组中查找数字I

个人博客


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

题解
  • 暴力(罢了)

  • 二分查找

    • 算法思想
      • 使用二分查找,找到算法中target 存在的一个位置,然后再向 target 两边遍历,直到小于或大于 target,返回索引
    • 复杂度分析
      • 时间复杂度 O(logn):但是在 target 个数接近于 n 的时候复杂度会退化为 O(n) (待优化)
      • 空间复杂度 O(1)
    class Solution {
        int[] nums;
        public int search(int[] nums, int target) {
            this.nums = nums;
            int left = 0,right = nums.length - 1,length = nums.length;
            while(left <= right){
                int mid = (left + right)/2;
                if(nums[mid] > target)right = mid-1;
                else if(nums[mid] < target)left = mid + 1;
                else return searchTarget(mid,target,length);
            }
            return 0;
        }
        int searchTarget(int index,int target,int length){
            int left = index-1,right = index,res = 0;
            while(left >= 0){
                if(nums[left] == target)res++;
                else break;
                left--;
            }
            while(right < length){
                if(nums[right] == target)res++;
                else break; 
                right++;
            }
            return res;
        }
    }
    
  • 优化的二分查找

    • 算法思想
      • 通过二分查找直接找到第一个和最后一个 target 的位置
      • 重点是使用二分查找找到第一个和最后一个 target 的位置
    • 复杂度分析
      • 时间复杂度 O(logn)
      • 空间复杂度 O(1)
    • 缺点
      • 较为复杂,容易写错
    class Solution {
        public int search(int[] nums, int target) {
            int left = 0,right = nums.length - 1;
            int first = getFirstK(nums,left,right,target);
            int last = getLastK(nums,left,right,target);
            if(first != -1)return last - first + 1;
            else return 0;
        }
        int getFirstK(int[] nums,int left,int right,int target){
            if(left > right)return -1;
            int mid = left + (right - left)/2;
            if(nums[mid] < target)left = mid + 1;
            else if(nums[mid] > target)right = mid -1;
            else{
              	//此处为查找 target 第一个 target 索引的逻辑,如果后面的位置与前面相同,则不是最后一个
                if(mid > 0 && nums[mid] == target && nums[mid-1] == target)right = mid-1;
                else return mid;
            }
            return getFirstK(nums,left,right,target);
        }
        int getLastK(int[] nums,int left,int right,int target){
            if(left > right)return -1;
            int mid = left + (right - left)/2;
            if(nums[mid] < target)left = mid + 1;
            else if(nums[mid] > target)right = mid -1;
            else{
                if(mid < nums.length - 1 && nums[mid] == target && nums[mid+1] == target)left = mid + 1;
                else return mid;
            }
            return getLastK(nums,left,right,target);
        }
    }
    

0~n-1中缺失的数字 II

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

输入: [0,1,3]
输出: 2
题解
  • 二分查找

    • 算法思想
      • 在索引为 n 并且 n 前没有缺失数字的情况下,nums[n] = n
      • 如前缺失数字,nums[n] = n+1
      • 利用此特征进行二分查找
    • 复杂度分析
      • 时间复杂度 O(logn)
      • 空间复杂度 O(1)
    class Solution {
        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){
                    if(mid == 0 || nums[mid-1] == mid -1)return mid;
                    else right = mid -1;
                }
                if(nums[mid] == mid)left = mid + 1;
            }
          	//当缺失的数字是最后一个时,left 会等于 nums.length
            if(left == nums.length)return left;
          
          	//当没有缺失时,无效输入
            return -1;
        }
    }
    
总结
  • 二分搜索容易写错呀哈哈哈哈,记住二分搜索在 target 有重复的时候可以进行优化

  • 二分查找真的要小心,非常非常容易写错

  • if else if 不要使用太多

    • 如(这样很容易出错,最好换成下面的方式)

      if
      else if
      else if
      else if
      
      if{
      	if()
      	else
      }
      else
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值