Offer 驾到,掘友接招!我正在参与2022春招系列活动-刷题打卡任务,点击查看活动详情。
一、题目描述
统计一个数字在排序数组中出现的次数。
示例
示例1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
提示
- <= nums.length <= 105
- -109 <= nums[i] <= 109
- nums 是一个非递减数组
- -109 <= target <= 109
难度:简单
此题为简单题,兄弟们可以重拳出击!
二、思路分析
众所周知,力扣上,简单题是真简单,中等题有些困难,困难题看不懂答案。
题目解析
-
统计一个数字在排序数组中出现的次数,题目甚是简约,如果没有其他思路,可以直接使用遍历查询目标出现次数,具体流程为:
- 使用遍历操作,并在遍历过程中比较元素是否等于目标值 target,
- 如果相等,则开始记录目标值出现的次数,直到遍历到下一个非目标值的元素为止,并返回统计次数
- 如果遍历完成也没有出现目标值,则返回 0
- 简单题目在性能和数据量上要求不太严格,可以直接实现。
-
对于一个有序数组寻找目标值出现次数,还可以通过二分法先找到目标值位置,在此基础上寻找目标值出现次数
- 有序数组可以对中间位置判断是否等于目标值来丢弃一半的数据量,提升查找效率
- 可以一次二分法先找到任意一个目标值位置后,向前向后分别遍历记录重复次数
- 还可以通过两次二分法,获取第一次出现位置和最后一次出现位置,计算位置之间数量
三、AC 代码
二分法+部分迭代
- 二分法查找指定数,找到之后遍历左侧和右侧重复个数
public int search(int[] nums, int target) {
if(nums.length == 0) return 0;
int l = 0, r = nums.length-1;
while(l <= r){
int mid = (l+r) / 2;
if(nums[mid] == target){
int min = mid-1, max =mid+1;
while(min >= 0 && nums[min] == target) min--;
while(max <= r && nums[max] == target) max++;
return max-min-1;
}else if(nums[mid] < target){
l = mid+1;
}else{
r = mid-1;
}
}
return 0;
}
两次二分法
- 使用两次二分法,先找最左侧的目标值,再找最右侧的目标值,最后返回位置做差 +1
- 两次二分法可以整合为一个函数进行两次调用,根据参数判断是寻找第一个或者第二个
public int search(int[] nums, int target) {
int minIndex = 0, maxIndex = -1;
int l = 0, r = nums.length-1;
while(l <= r){
int mid = (l+r) / 2;
if(nums[mid] == target && (mid==0 || nums[mid-1] < target)){
// 最左侧目标值
minIndex = mid;
break;
}else if(nums[mid] == target && nums[mid-1] == target || nums[mid] > target){
r = mid-1;
}else{
l = mid+1;
}
}
l = minIndex;
r = nums.length-1;
while(l <= r){
int mid = (l+r) / 2;
if(nums[mid] == target && (mid==r || nums[mid+1] > target)){
// 最右侧目标值
maxIndex = mid;
break;
}else if(nums[mid] == target && nums[mid+1] == target || nums[mid] < target){
l = mid+1;
}else{
r = mid-1;
}
}
return maxIndex-minIndex+1;
}
四、总结
知识点
- 有序数组中寻找目标值,可以使用二分法
- 二分法边界问题需要考虑清楚,如寻找目标值第一次出现和最后一次出现循环停止条件
最后
阳春三月,算法刷起来!LeetCode 剑指 Offer。
简单题,不需要考虑太多,开干就是了。