题目
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。
进阶: 你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
分析
思路:因为数组有序,且是查找某一个元素的位置,而且最好是实现O(log n),就要想到用二分查找来。我们只需要用两次二分查找,一次找前,一次找后。
这里是关于二分的文章,可以点进去看一下
1. 循环部分
找前(findTheFirstOne):
- 如果nums[mid]>=target:说明已经出现过该目标值,而我们要找第一个出现的位置,那么我们应该去这个区间[left , mid-1]里面去找 ,那么令right =mid;
- 如果nums[mid ]< target:说明前面没有出现过target,我们要去后面找,所以目标值出现的地方应该在这个[mid+1 ,right]区间,那么left =mid;
找后(findTheLastOne):
- 如果nums[mid]>target:说明已经出现过该目标值,而我们要找最后一个出现的位置,那么我们应该去前面找[left, mid-1] ,那么令right =mid;
- 如果nums[mid ]<= target:说明前面没有出现过target或者刚好等于target,我们就要去后面找最后出现的位置,所以目标值出现的地方应该在这个[mid+1 ,right]区间,那么left =mid;
2.判断
我们设置一个result数组,因为题目说了只返回第一个出现和最后一个出现的位置,也就是两个数,所以result数组的大小为2,我们初始化该数组{-1,-1},如果nums数组中存在target元素的第一个或最后一个的下标,我们就可以直接加进来,形成答案。如果数组长度为0或者nums数组中不存在target元素,都直接返回result。
判断一:
——findTheFirstOne:
- 循环结束之后,会有left和right,若存在多个重复的target我们就要进行判断,如果nums[left]==target,则说明left指向的就是第一个出现的,否则的话,判断nums[right]==target,如果条件成立,则说明right指向的为第一个出现的位置。
- 如果两个if条件都不成立,说明没有该元素存在,result原来初始化的{-1,-1}也不会被改变的。
- 注意细节:应该先判断nums[left]==target是否成立,再判断nums[right]==target!!!因为找第一个
——findTheLastOne:
- 同理,但是这里要改为先判断nums[right]==target是否成立,再判断nums[left]==target!!——找最后一个
代码实现
static int[] result = { -1, -1 };
static public int[] searchRange2(int[] nums, int target) {
if (nums.length == 0) {
return result;
}
binarySearchToFindFirst(nums, target);
binarySearchToFindLast(nums, target);
return result;
}
static public void binarySearchToFindFirst(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left + 1 < right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid;
} else {
right = mid;
}
}
if (nums[left] == target) {
result[0] = left;
} else if (nums[right] == target) {
result[0] = right;
}
}
static public void binarySearchToFindLast(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left + 1 < right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) {
left = mid;
} else {
right = mid;
}
}
if (nums[right] == target) {
result[1] = right;
} else if (nums[left] == target) {
result[1] = left;
}
}
反思
之前看过二分查找的代码,但是没有完全搞明白,现在很清楚了。
end.