【剑指offer-C++】JZ53:数字在升序数组中出现的次数

【剑指offer-C++】JZ53:数字在升序数组中出现的次数

    • 题目描述
    • 解题思路

题目描述

描述:给定一个长度为 n 的非降序数组和一个非负数整数 k ,要求统计 k 在数组中出现的次数。

数据范围:0≤n≤1000,0≤k≤100,数组中每个元素的值满足 0≤val≤100。

要求:空间复杂度 O(1),时间复杂度 O(logn)。

输入:[1,2,3,3,3,3,4,5],3
返回值:4
输入:[1,3,4,5],6
返回值:0

解题思路

数字在升序数组中出现的次数:最直观的想法是,使用一个变量res记录k在数组中出现的次数,初始为0,从头到尾遍历数组,如果当前数组元素等于k,则将res加一,最后返回res即可。注意,由于数组是非降序的,故可以使用剪枝方法,即一旦当前数组元素大于k,则直接break。

    int GetNumberOfK(vector<int> data ,int k) {
        int res=0;
        for(int i=0;i<data.size();i++)
        {
            if(data[i]>k)
                break;
            if(data[i]==k)
                res++;
        }
        return res;
    }

优化:有序数组当然第一个想到二分法啦!但是一个数组中可能有多个k,故我们若是能够找到k出现的左界和k出现的右界,那么我们即可求出k出现的次数。再者因为数组中全是整数,因此可以考虑二分查找k-0.5和k+0.5应该出现的位置,两者相减即是k出现的次数。因为使用左边界left和右边界right找中点mid时,使用的是向下取整,最后返回的是左边界left,故此方法找到的是k在data中的第一个下标,如果k不存在的话则返回的是第一个大于目标值的下标,因为如果向下取整的mid对应的元素小于k的话,会将left设置为mid+1,而mid对应的元素是小于k的。

// 二分查找 注意是float k
int bisearch(vector<int>& data ,float k)
{
   //左边界
   int left=0;
   //右边界
   int right=data.size()-1;
   //相等时仍需要 该方法返回k在data中的第一个下标 如果k不存在则返回第一个大于目标值的下标 故3.5是4 2.5是3 相减即为3的长度
   while(left<=right)
   {
       //中间值
       int mid=left+(right-left)/2;
       //中点值小于k则移动左边界
       if(data[mid]<k)
          left=mid+1;
       //中点値大于k则移动右边界
       else if(data[mid]>k)
          right=mid-1;
    }
    // 注意返回的是left啊! 比如3.5 right变成了3 但返回的是left为4 比如2.5 right变成了2 但返回的是left为3 最后下标相减即为3出现的次数
    return left;
}
int GetNumberOfK(vector<int> data ,int k) 
{
   //分别查找k+0.5和k-0.5应该出现的位置中间部分全部是k
   return bisearch(data,k+0.5)-bisearch(data,k-0.5);
}

你可能感兴趣的:(剑指offer,c++,算法,开发语言)