题目描述
面试题53 - I. 在排序数组中查找数字 I
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
限制
0 <= 数组长度 <= 50000
方法
1.二分查找法
思路
有序递增数组,题解往往涉及到二分查找,题目分类为简单,但是更加重要的是掌握二分法思想
通过二分查找法,分别找到target区间的左右边界值left,right(左边界=稍小于target值,右边界=稍大于target值),target出现的个数=right-left-1。
代码
public int search(int[] nums, int target) {
int left = findLeftBound(nums, target);
int right = findRightBound(nums, target);
return right - left - 1;
}
2. 二分查找法总结
2.1 二分查找元素
private static int findKey(int[] nums, int target){
int i=0;
int j=nums.length-1;
while(i<=j){
int mid = i + (j-i)/2; // 避免数值溢出
if(nums[mid]==target)
return mid;
else if(nums[mid]<target){
i = mid+1;
}
else if(nums[mid]>target){
j = mid-1;
}
}
return -1;
}
思想是 在一个闭区间 [ i , j ] [i, j] [i,j]中去做二分。将区间中值 n u m s [ m i d ] nums[mid] nums[mid]与target比较,如果相等则返回,如果在左半区间,则将右边界收缩至mid-1;如果target在右半区间,则将左边界收缩值mid+1。直到收缩区间为空
2.找出target的左、右边界
同样的思路:
对于找出右边界,如果右边界点在nums[mid]的右半区间,则收缩左边界至left = mid+1。如果右边界在nums[mid]的左半区间,则收缩右边界至right=mid-1。最后,i,j同时指向右边界点,再执行一次判断,j向左移位,返回i,即右边界点。
对于找左边界点类似,但最后i,j会同时指向第一个target值,再执行一次判断,j向左移位,返回j值。
找左、右边界代码
private static int findLeftBound(int[] nums, int target){
int i=0;
int j=nums.length-1;
while(i<=j){
int mid = (i+j)/2;
if(nums[mid]==target)
j = mid -1;
else if(nums[mid]<target)
i = mid + 1;
else
j = mid - 1;
}
return j;
}
private static int findRightBound(int[] nums, int target){
int i = 0;
int j = nums.length-1;
while( i<= j ){
int mid = (i+j)/2;
if(nums[mid]==target)
i = mid+1;
else if(nums[mid]<target)
i = mid+1;
else
j = mid - 1;
}
return i;
}