题目描述

统计一个数字在升序数组中出现的次数。
示例1
输入:[1,2,3,3,3,3,4,5],3
返回值:4

题目分析

在一个数组中统计一个数字的出现次数,首先可以直接遍历这个数组,使用一个变量count来记录数字出现的次数,碰到与目标数字相同的,则次数加1。
因为数组是升序的(有序即可),所以可以使用二分查找来提高速度。

解题思路

方法1:暴力法
遍历数组的过程中,考虑到数组是升序的,对于比目标数字大的部分直接跳过,节省时间。

方法2:二分法
二分法的统一模板为:

while(left<=right){
   int mid = (left+right)/2;
   if(nums[mid]==target) return mid;
   if(nums[mid]>target) right=mid-1;
   if(nums[mid]<target) left=mid+1;
}

在查找某个数字的场景时,使用二分***在nums[mid] == target时,直接返回中间的下标值。
而需要统计数字出现的次数时,不能直接返回,而是继续二分查找,直到找到数字的左下界或右上界。
图片说明
图片说明

代码实现

方法1:暴力法

public int GetNumberOfK(int [] array , int k) {
       // 记录出现的次数
       int count = 0;
       for(int i=0;i<array.length;i++){
            // 相等次数加1
           if(array[i] == k){
               count++;
           }else if(array[i] > k){
                // 数组有序,一旦比目标值大,直接退出
               break;
           }
       }
        return count;
    }

时间复杂度:O(n),计算次数最多需要遍历整个数组,时间复杂度为数组长度n;
空间复杂度:O(1),只需要常量个数的变量即可。

方法2:二分法

public int GetNumberOfK(int [] array , int k) {
       int left = 0;
       int right = array.length;
       int l = 0;
       int r = 0;
        // 第一次二分查找
       while(left<right){
           int mid = left + (right-left)/2;
          if(array[mid] < k){
              // 确定左下界
               left = mid+1;
           }else{
               right = mid;
           }
        }
        // 左下界坐标
        l = left;
        left = 0;
        right = array.length;
        // 第二次二分查找
        while(left<right){
           int mid = left + (right-left)/2;
          if(array[mid] <= k){
              // 确定右上界
               left = mid+1;
           }else{
               right = mid;
           }
        }
        // 右上界坐标
        r = left;
        // 中间为目标数字相同的个数
        return r-l;
    }

时间复杂度:O(logn),使用了两次二分查找,时间花费最多为 2*logn;
空间复杂度:O(1),只需要常量个数的变量即可。