问题描述:
统计一个数字在排序数组中出现的次数。
例如输入排序数组[1, 2, 3, 3, 3, 3, 4, 5]和数字3,由于3在这个数组中出现了4次,因此输出4。
样例
- 输入:[1, 2, 3, 3, 3, 3, 4, 5] , 3
- 输出:4
使用二分搜索,只不过是经典案例的改编,经典案例是寻找一个数字,该数字并不是重复的,而本题目是寻找一个数字出现的次数,该数字会出现多次,折半查找的思路是:
1)设置初始查找区间:low=0,high=n-1
2)测试查找区间是否存在,如果不存在,则查找失败,否则
3)取中间点mid=(low+high)/2 比较k 和 rmid,有三种情况:
- k<rmid high=mid-1,查找在左半区进行,转步骤2
- k>rmid low=mid+1,查找在右半区进行,转步骤2
- k=rmid,则查找成功,返回记录在表中的位置
只不过在该题中不需要k=rmid这一步,只需要找到左右边界,然后相减即可得到该数字出现的次数。
有两种二分模板,第一种二分模板是寻找左边第一个数,第二个模板是寻找最后一个数字。要想找左边第一个数,需要找到左边与右边一个划分的性质,即nums[mid] < k ,此时是左边的拐点,要求解的值在右边区间内,因此l=mid+1;要想找最后一个数字,那么划分右边与左边的性质是nums[i] <=k ,这样说明mid在要寻找的数字区间内,需要将左区间向右移动,即l = mid. 最后注意数组个数为0的情况,求解到要找数字的左右边界,二者相减就可以得到。
class Solution {
public:
int getNumberOfK(vector<int>& nums , int k) {
if(nums.empty()) return 0;
int l=0,r=nums.size()-1;
while(l<r)
{
int mid=l+r>>1;
if(nums[mid]<k) l=mid+1;
else r=mid;
}
if(nums[l]!=k) return 0;
int left=l;
l=0,r=nums.size()-1;
while(l<r)
{
int mid= l+r+1>>1;//注意加一防止陷入死循环
if(nums[mid]<=k) l=mid;
else r=mid-1;
}
return r-left+1;
}
};