剑指 Offer 53-Ⅰ. 在排序数组中出现的次数

158 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情

一、题目描述

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

示例

示例1:

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

示例2:

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

提示

  • <= nums.length <= 105
  • -109 <= nums[i] <= 109
  • nums 是一个非递减数组
  • -109 <= target <= 109

难度:简单

此题为简单题,兄弟们可以重拳出击!

二、思路分析

众所周知,力扣上,简单题是真简单,中等题有些困难,困难题看不懂答案。

题目解析

  1. 统计一个数字在排序数组中出现的次数,题目甚是简约,如果没有其他思路,可以直接使用遍历查询目标出现次数,具体流程为:

    • 使用遍历操作,并在遍历过程中比较元素是否等于目标值 target,
    • 如果相等,则开始记录目标值出现的次数,直到遍历到下一个非目标值的元素为止,并返回统计次数
    • 如果遍历完成也没有出现目标值,则返回 0
    • 简单题目在性能和数据量上要求不太严格,可以直接实现。
  2. 对于一个有序数组寻找目标值出现次数,还可以通过二分法先找到目标值位置,在此基础上寻找目标值出现次数

    • 有序数组可以对中间位置判断是否等于目标值来丢弃一半的数据量,提升查找效率
    • 可以一次二分法先找到任意一个目标值位置后,向前向后分别遍历记录重复次数
    • 还可以通过两次二分法,获取第一次出现位置和最后一次出现位置,计算位置之间数量

三、AC 代码

二分法+部分迭代

  • 二分法查找指定数,找到之后遍历左侧和右侧重复个数
public int search(int[] nums, int target) {
    if(nums.length == 0) return 0;

    int l = 0, r = nums.length-1;
    while(l <= r){
        int mid = (l+r) / 2;
        if(nums[mid] == target){
            int min = mid-1, max =mid+1;
            while(min >= 0 && nums[min] == target) min--;
            while(max <= r && nums[max] == target) max++;
            return max-min-1;
        }else if(nums[mid] < target){
            l = mid+1;
        }else{
            r = mid-1;
        }
    }
    return 0;

}

image.png

两次二分法

  • 使用两次二分法,先找最左侧的目标值,再找最右侧的目标值,最后返回位置做差 +1
  • 两次二分法可以整合为一个函数进行两次调用,根据参数判断是寻找第一个或者第二个
public int search(int[] nums, int target) {
    int minIndex = 0, maxIndex = -1;
    int l = 0, r = nums.length-1;
    while(l <= r){
        int mid = (l+r) / 2;
        if(nums[mid] == target && (mid==0 || nums[mid-1] < target)){
            // 最左侧目标值
            minIndex = mid;
            break;
        }else if(nums[mid] == target && nums[mid-1] == target || nums[mid] > target){
            r = mid-1;
        }else{
            l = mid+1;
        }
    }

    l = minIndex;
    r = nums.length-1;
    while(l <= r){
        int mid = (l+r) / 2;
        if(nums[mid] == target && (mid==r || nums[mid+1] > target)){
            // 最右侧目标值
            maxIndex = mid;
            break;
        }else if(nums[mid] == target && nums[mid+1] == target || nums[mid] < target){
            l = mid+1;
        }else{
            r = mid-1;
        }
    }
    return maxIndex-minIndex+1;

}

image.png

四、总结

知识点

  • 有序数组中寻找目标值,可以使用二分法
  • 二分法边界问题需要考虑清楚,如寻找目标值第一次出现和最后一次出现循环停止条件

最后

阳春三月,算法刷起来!LeetCode 剑指 Offer

简单题,不需要考虑太多,开干就是了。