1.题目
2.分析
此题通过非常简单,可以直接遍历数组,用计数器统计k出现次数即可。但是效率低,也不满足时间复杂度O(logn)
.
使用二分法效率更高,并且满足时间复杂度O(logn)
.
3.复习二分法
二分法的右起点有两种,
size-1
和size
,分别对应区间[left,right]
和[left,right)
,主要用前一种,即右起点right=size-1
,对应区间[left,right]
,循环条件(left <= right)
,因为是闭区间所以,端点更新均跳过原端点,即中间值不参与下一次查找,即right = middle - 1
,left = middle + 1
.
int search(int nums[], int size, int target) //nums是数组,size是数组的大小,target是需要查找的值
{
int left = 0;
int right = size - 1; // 定义了target在左闭右闭的区间内,[left, right]
while (left <= right) { //当left == right时,区间[left, right]仍然有效
int middle = left + ((right - left) / 2);//等同于 (left + right) / 2,防止溢出
if (nums[middle] > target) {
right = middle - 1; //target在左区间,所以[left, middle - 1]
} else if (nums[middle] < target) {
left = middle + 1; //target在右区间,所以[middle + 1, right]
} else { //既不在左边,也不在右边,那就是找到答案了
return middle;
}
}
//没有找到目标值
return -1;
}
4.解题
若查找目标数组[1,2,3,3,4,5]
中的3
,先使用二分查找分别找到首次出现3
的位置,为左边界;再找到3
出现结束后一位的位置,即4
的位置,为右边界。两边界相减即得到重复出现的次数。
首先查找左边界
- 因为最终查找到的位置落在循环后
left
的位置,所以判断条件为data[mid] < k
,满足则更新左端点,不满足则更新右端点 - 最终
left>right
,跳出循环,此时left
即为首次出现k
的位置,int first_occur=left
将其记录下来
int left=0;
int right=dataLen-1;
while(left<=right)
{
int mid=left+(right-left)/2;
if(data[mid]<k)
{
left=mid+1;
}
else
{
right=mid-1;
}
}
int first_occur=left;
再查找右边界
- 进入二分查找前先重置左右端点
- 查找右边界时循环体内判断条件为
data[mid]<=k
,满足即更新左端点,即左端点会跳过出现的所有k
值,停在k
停止出现的后一位 - 不满足判断条件时,即更新右端点,缩减查找区间
- 最终循环结束时的
left
的位置即为所需的右边界。
left=0;
right=dataLen-1;
while(left<=right)
{
int mid=left+(right-left)/2;
if(data[mid]<=k)
{
left=mid+1;
}
else
{
right=mid-1;
}
}
return left-first_occur;
}