转载本文章请标明作者和出处
本文出自《Darwin的程序空间》
本文题目和部分解题思路来源自《剑指offer》第二版
题目
统计一个数字在排序数组中出现的次数
示例:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
解题分析
首先我们分析一下题干,一个排序的数组,我们应该下意识的相到,排序的数组,百分之99和二分算法有关系了,再看人家要的是统计出一个数字的出现次数;
大部分的同学此时可能会想,这还不简单,我遍历一遍,把这个数字统计一下个数,完美解决,并且时间复杂度只有O(n),而且因为是排序的,统计到这个数字之后就可以终止循环,完美;
但这种解法,面试官是肯定不会认可的,因为这道题其实隐藏条件就是,时间复杂度最多是O(lgn),而据我所知,已知这种时间复杂度的算法,最出名的就是二分算法了,所以,我们要使用二分算法来解这道题;
而我们已知的二分算法是给一个排序数组和一个数,我们从这个排序数组中找出这个数的索引值,那么怎么应用到这道题里面呢?
其实满足题目我们只需要找到两个索引值即可,就是目标数第一次出现的索引值,和最后一次出现的索引值,两个索引值相减加一即是个数;
那么第一次出现怎么表示呢,就是等于目标值并且左边要不是空,要不是不等于目标值的值的元素;
最后一次呢?那就是等于目标值并且右边要不是空,要不是不等于目标值的值的元素;
所以我们只需要使用二分算法分别找出这两个元素即可,因为两次寻找的时间复杂都是O(lgn),所以加起来还是O(lgn);
代码(JAVA实现)
ps:这里笔者使用的jdk为1.8版本
class Solution {
public int search(int[] nums, int target) {
int leftIndex = findLeftIndex(nums, target);
int rightIndex = findRightIndex(nums, target);
return rightIndex - leftIndex + 1;
}
private int findLeftIndex(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + ((right - left) >> 1);
int num = nums[mid];
if (num == target && (mid == 0 || nums[mid - 1] != target)) {
return mid;
} else if (num == target || num > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return 1;
}
private int findRightIndex(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + ((right - left) >> 1);
int num = nums[mid];
if (num == target && (mid == nums.length - 1 || nums[mid + 1] != target)) {
return mid;
} else if (num == target || num < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return 0;
}
}