统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public:
int search(vector<int>& nums, int target) {
map<int,int> record;
for(auto num:nums)
{
record[num]++;
}
return record[target];
}
};
或者采用二分法。下面是对于官方的二分法的解析
class Solution {
public:
int search(vector<int>& nums, int target) {
//获取这个数据的最左侧出现的位置
int smallerIndex=binarysearch(nums,target,true);
//获取这个数据最右侧出现的位置
int biggerIndex=binarysearch(nums,target,false)-1;
//判断是否存在越界的问题
if(smallerIndex<=biggerIndex&&biggerIndex<nums.size()&&smallerIndex>=0&&nums[smallerIndex]==target&&nums[biggerIndex]==target)
{
//返回该数据出现的次数
return (biggerIndex-smallerIndex+1);
}
return 0;
}
int binarysearch(vector<int>&nums ,int target,bool lower)
{
//定义我们的左边界
int left=0;
//定义我们的右边界
int right=nums.size()-1;
//思考下面的result我们为什么要将初始值定位nums.size()?
//定义我们的查找目标的初始值是nums.size()
//也就是有效的查找索引的后一个
//这样定义的话,是为了应对我们想要查找的数据只有一个,且整个数组中的元素个数也只有一个的情况
//比方说对于数组[1]我们想要查找1的个数,对于我们查找那个较大的下标,
//我们的result并不会在下面的while循环中得到更新,所以会直接按照初始赋值将我们的result传出去
//而我们的较大的下标默认输出的数据就是目标查询数据的较大索引的下一个位置,
//所以我们这样的赋初始值就很好地满足了我们的条件
int result=nums.size();
//二分查找,我们的左指针小于我们的右指针
while(left<=right)
{
//计算出中间元素的下标
int mid=(left+right)/2;
//下面是需要将我们的查找范围限定在左边半个的判断条件
//情况一:如果我们中间的元素大于我们目标的查询值,也就是说
//我们要查询的值在我们的左半区域,所以我们要将我们的查询范围限定到左半区域
//情况二:如果我们想要查询的是较小的那个下标的话,并且我们的当前的mid所对应的数据
//大于等于目标,我们也可以将我们的查询范围限定到我们的左半区域,从而查询到较小的索引位置
//那么对于我们查询较大的索引下标,我们传入的lower值一定是false,也就是说
//只有nums[mid]>target的时候我们才能够将查询范围限定到左半区域
//如果不满足这个条件的话,我们只能将查询的范围限制在右半区域
//也就是说我们最终在查询较大索引的时候,如果mid等于了target,我们只能限定查询范围为右半区间,没办法跳出
//所以我们最终跳出的结果是在最后一个目标的下一个位置的索引。
//而对于我们查询较小位置的索引的时候
//由于我们在mids索引对应的数据元素>=target的时候都是可以左移的,
//也就是说我们最终会在目标查询元素的最左边那个数据的再左一个位置跳出。
if(nums[mid]>target||(lower&&nums[mid]>=target))
{
right=mid-1;
result=mid;
}
else
{
left=mid+1;
}
}
return result;
}
};
比方说对于下面这个数组的查询3的出现个数
0
1
2
3
4
5
6
7
1
3
3
3
6
7
8
9
我们在查询较大的3的位置索引的话
我们的left=0,right=7,mid=3,读取到的索引对应的数据为3
但是按照我们上面的代码中的分析,我们只能将查找的区间限定在右半部分,所以我们只能将left=mid+1,也就是left转到4的位置。
然后left=4,right=7,新的mid为5,但是5对应的数据为7,大于了我们目标查询元素3,所以我们right=mid-1,也就是4。
这个时候right==left,但是还是满足我们的循环条件的,还要继续,
所以我们的mid变成了4,然后4对应的元素为6,大于了我们目标查询的元素,所以我们的right=mid-1,也就是3,right<left然后现在才能够跳出循环,返回我们的mid结果也就是4
所以我们返回的3的最大的索引下标是4,也就是我们全部3的最后的一个索引下标的下一个位置。
我们在查询较小的3的位置索引的话
0
1
2
3
4
5
6
7
1
3
3
3
6
7
8
9
我们的left=0,right=7,mid=3,按照我们上面的代码分析,对于查询较小的目标索引位置的话,我们的的mid对应的数据为3,我们是可以限定查询的范围为左半区的。
所以我们right= mid-1, 也就是2,
所以left=0,right=2,mid=1,而我们mid=1所对应的数据也是3,也是可以限定查找范围在左半区的。
所以我们right= mid-1, 也就是0,此时我们的left==right=0,还是满足循环的条件,所以还要继续进行循环。当前的mid=0,0对应的元素为1,比我们的目标查询元素要小,所以我们的left=mid+1=1,left>right,跳出循环所以我们返回的较小的索引下标为mid=0
那么对于查询较小的下标有没有可能返回-1的情况呢?
这里我们换了一组数据
0
1
2
3
3
3
4
5
对于查询较小的目标元素的下标
left=0,right=3,mid=1
mid对应的元素为3,按照我们上面的代码分析,我们可以将查询范围限定在左半部分
right=mid-1=0
这时候left=0,right=0,mid=0,mid=0对应的元素还是3, 按照我们上面的代码分析,我们依旧可以将查询范围限定在左半部分。
right=mid-1=-1
但是这时候mid并没有更新,但是left=0,right=-1已经不满足循环的条件了,可以跳出了,所以我们返回的mid还是0,并不会是-1
由于我们上述得出的较大的索引下标位置是所有目标查询元素的下一个位置,我们的较小的索引下标位置时所有的目标查询元素的前一个位置,所以在大索引下标减去小索引下标,还需要-1才能得出我们的最终目标查询元素的个数。