剑指 Offer 53 - 在排序数组中查找数字

剑指 Offer 53. 在排序数组中查找数字

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

示例 1:

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

示例 2:

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

限制:

0 <= 数组长度 <= 50000

解一:找到出现的数字再找到数字的左右端点

  • 使用二分查找法找到数字后,再循环找到左右两端点

  • 时间复杂度:二分查找的时间复杂度为O(logn),但是当数组中元素全是要寻找的数字时,左右两端点寻找的时间复杂度O(n)。可以考虑改进二分查找优化速度

  • 空间复杂度:O(1)

class Solution {
    public static int search(int[] nums, int target) {
        int res = 0;
        int left = 0;
        int right = nums.length - 1;
        int mid = (left + right) / 2;
        if(nums.length==1 && nums[0]==target){
            return 1;
        }
        boolean flag = false;
        while (left <= right) {
            if (nums[mid] > target) {
                right = mid - 1;
                mid = (left + right) / 2;
            }else  if(nums[mid]<target){
                left=mid+1;
                mid = (left + right) / 2;
            }else{
                flag=true;
                break;
            }
        }
        if(flag){
            left=mid;
            right=mid;
            while(right<nums.length-1 && nums[right]==nums[right+1] ){
                right++;
            }
            while(left>0 && nums[left]==nums[left-1]){
                left--;
            }
            res=right-left+1;
        }
        return res;
    }
}

解二:优化二分查找(迭代)

使用二分查找直接查找数字的左右边界,以左边界为例

  • 令返回值res初值为-1

  • 当查到mid==target时,说明左边界在mid左边或者是mid本身

  • 如果mid已经是第一个数,或者mid-1的数不等于target,说明左边界是mid本身,res=mid

  • 否则,说明左边界在mid左边,令right=mid-1,并退出循环

  • 如果res==-1,说明数字中不包含该数字

时间复杂度为二分查找的复杂度:O(logn);空间复杂度O(1)

class Solution {
    public static int search(int[] nums, int target) {
        int left = searchLeft(nums, target);
        int right = searchRight(nums, target);
        int res = 0;
        if(left>-1 && right>-1){
            res=right-left+1;
        }
        return res;
    }
    public static int searchLeft(int[] nums, int target) {
        int left = 0;
        int res = -1;
        int right = nums.length - 1;
        int mid;
        while (left <= right) {
            mid = (left + right) / 2;
            if (target == nums[mid]) {
                if (mid == 0 || nums[mid - 1] != target) {
                    res = mid;
                    break;
                } else {
                    right = mid - 1;
                }
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return res;
    }

    public static int searchRight(int[] nums, int target) {
        int left = 0;
        int res = -1;
        int right = nums.length - 1;
        int mid;
        while (left <= right) {
            mid = (left + right) / 2;
            if (target == nums[mid]) {
                if (mid == nums.length - 1 || nums[mid + 1] != target) {
                    res = mid;
                    break;
                } else {
                    left = mid + 1;
                }
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return res;
    }
           
}

解三:优化二分查找(递归)

class Solution {
     public static int search(int[] nums, int target) {
        int left = searchLeft(nums, target, 0, nums.length - 1);
        int right = searchRight(nums, target, 0, nums.length - 1);
        int res = 0;
        if (left > -1 && right > -1) {
            res = right - left + 1;
        }
        return res;
    }

    public static int searchLeft(int[] nums, int target, int left, int right) {
        if (left > right) {
            return -1;
        }
        int mid = (left + right) / 2;
        if (target == nums[mid]) {
            if (mid == 0 || nums[mid - 1] != target) {
                return mid;
            } else {
                right = mid - 1;
            }
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }
        return searchLeft(nums, target, left, right);
    }

    public static int searchRight(int[] nums, int target, int left, int right) {
        if (left > right) {
            return -1;
        }
        int mid = (left + right) / 2;
        if (target == nums[mid]) {
            if (mid == nums.length - 1 || nums[mid + 1] != target) {
                return mid;
            } else {
                left = mid + 1;
            }
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else {
            left = mid + 1;
        }

        return searchRight(nums,target,left,right) ;
    }
           
}

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

限制:

1 <= 数组长度 <= 10000

在缺失数字之前,数组中的数字等于其下标;在缺失数字之后,数组中的数字等于下标+1

  • 所以要找到第一个值和下标不相等的元素,它的下标就是缺失的数字
  • 当数组中的所有数字和元素下标都相等时,说明缺失的数字是在最后,所以应该返回数组中最后一个元素的下标+1

时间复杂度为二分查找的复杂度:O(logn);空间复杂度O(1)

class Solution {
    public int missingNumber(int[] nums) {
        int mid;
        int res=0;
        int left=0;
        int right=nums.length;
        while(left<=right){
            mid=(left+right)/2;
            if(mid==nums.length){
                res=mid;
                break;
            }else if(nums[mid]==mid){
                left=mid+1;
            }else{
                if(mid==0 || nums[mid-1]==mid-1){
                    res=mid;
                    break;
                }else{
                    right=mid-1;
                }
            }
        }
        return res;
    }
}
posted @ 2021-04-07 10:34  五斗橱哪位啊v  阅读(78)  评论(0)    收藏  举报