「剑指Offer 53.在排序数组中查找数字」
题目描述(level 简单)
统计一个数字在排序数组中出现的次数。(数组为非递减数组)
示例
- 示例1
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
- 示例2
输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
思路分析(哈希表)
这里提一下,关于排序数组的问题,首先需要想到的是二分法解决,时间复杂度为O(logN)。当然使用哈希表
仅仅是为了拓展一下思路,培养一下解决问题的感觉。回到正题,求数组中给定数字出现的次数,在遍历数组时,将数组中数字存入到哈希表
中,value
为出现的次数。有一个可以优化的点。
既然是非递减数组,目标数字
target
如果比数组中最大值还大target >nums[nums.length -1]
或者target
比数组中最小值还小target<nums[0]
则可以直接返回0
代码实现(哈希表)
class Solution {
public int search(int[] nums, int target) {
if (null == nums || nums.length < 1) {
return 0;
}
if (target > nums[nums.length - 1] || target < nums[0]) {
return 0;
}
Map<Integer, Integer> map = new HashMap<>();
for (int i : nums) {
if (map.containsKey(i)) {
map.put(i, map.get(i) + 1);
} else {
map.put(i, 1);
}
}
if (map.containsKey(target)) {
return map.get(target);
}
return 0;
}
}
思路分析(二分查找)
充分利用题意,既然是非递减数组(可以看作为递增数组,而递增量为n>=0),既然是有序的数组,那么首选考虑二分法来解决,统计某一个数字在数组中出现的次数,假设给定数组为 [1,2,2,2,4,5,5,6],target
为 2则:
- 将2,2,2视为一个小区间**[2,2,2]**,区间的左右边界分别为
i=1
,右边界j=3
,求 2出现的次数则可以转换为左右边界之差j-i+1=count
。 - 使用二分边界分别找到左右边界的值
i,j
。 - 先处理中间下标
index =(i + j)/2
,当nums[m]<=target
时,target
在右侧区间,则I=m+1。 - 当
nums[m]>target
时,target
在左侧区间,j=m-1
。 - 由于已经给定了
target
,且数组为非递减的数组,只要求出target-1
对应的右边界,两个右边界的值相减即可得出结果。
代码实现(二分法)
class Solution {
public int search(int[] nums, int target) {
if (null == nums || nums.length < 1) {
return 0;
}
if (target > nums[nums.length - 1] || target < nums[0]) {
return 0;
}
//找到target的右边界与target-1的右边界,得出二者差值即为所的结果
return findTargetIndex(nums, target) - findTargetIndex(nums, target - 1);
}
private int findTargetIndex(int[] nums, int target) {
if (null == nums) {
return 0;
}
int i = 0, j = nums.length - 1;
while (i <= j) {
int m = (i + j) >>> 1;
if (nums[m] <= target) {
i = m + 1;
} else {
j = j - 1;
}
}
return i;
}
}
复杂度
- 哈希表
时间复杂度O(N):遍历整个数组,而哈希表的添加、获取操作均为常量,整体与数组长度线性相关。
空间复杂度O(N):引入哈希表存储空间。
- 二分法
时间复杂度O(logN):二分法查找为对数级别时间复杂度。
空间复杂度O(1):未使用额外空间。