题目来源:每日一练——第1天:在排序数组中查找元素的第一个和最后一个位置(Java语言)
同时也是:LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置
题目
给定一个按照升序排列的整数数组nums
,和一个目标值target
。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值target
,返回 [-1, -1]
。
进阶:你可以设计并实现时间复杂度为O(logn)
的算法解决此问题吗?
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
提示:
0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109
思考
暴力求解
:看到题目的第一反应是用暴力求解的方法,也就是对数组进行遍历,挨个检查数组中的每个元素是否等于target
,第一次出现等于target
时就记录下起始的位置,直到遍历到第一个大于target
的元素为止,记录下其位置的前一个位置即可。但是此时的时间复杂度显然为O(n),不符合题目中的要求。二分查找
:题目的关键字为数组有序
,且要求时间复杂度为O(logn)
,所以尝试采用二分查找
去解决问题。二分查找就是在一个有序的数组里,比较数组中间的那个元素与target
的大小关系,从而确定target
在哪一个区域里,一直二分下去,直到确定target
的位置。- 题目的难点就在于
target
在这个有序数组中可能存在多个,找到符合要求的target
的数组位置,还需要继续查找下去。那么如何继续查找下去呢?可以继续二分查找,此时可以分三种情况考虑- 当
target>nums[mid]
时,target
就应该在数组的[mid+1,right]
部分继续查找 - 当
target<nums[mid]
时,target
就应该在数组的[left,mid-1]
部分继续查找 - 但是当
target==nums[mid]
时,如果你是要找第一个出现的target
的位置,那么target就应该在数组的[left,mid]
部分继续查找;如果你是要找最后一个出现target
的位置,那么target就应该在数组的[mid,right]
部分继续查找
- 当
代码实现
public class SerachTarget {
public static void main(String[] args) {
Solution solution = new SerachTarget().new Solution();
// to test
int[] nums = {5,7,7,8,8,10};
int target = 8;
int[] result = new int[2];
result = solution.searchRange(nums, target);
System.out.println("[" + result[0] + "," + result[1] + "]");
}
public class Solution {
public int[] searchRange(int[] nums, int target) {
int len = nums.length;
if(len == 0) { // 特殊情况:数组中没有元素
return new int[]{-1,-1};
}
int firstPosition = floor(nums, target); // 查找第一个target出现的位置
if(firstPosition == -1) { // 找不到第一个target出现的位置
return new int[]{-1,-1};
}
int lastPosition = ceil(nums, target); // 查找最后一个target出现的位置
return new int[]{firstPosition, lastPosition};
}
// 查找第一个target出现的位置
private int floor(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = (left + right) >>> 1;
if (target < nums[mid]) { // 查找区间为[left,mid-1]
right = mid - 1;
} else if(target == nums[mid]){ // 查找区间为[left,mid]
right = mid;
} else { // target > nums[mid],查找区间为[mid+1,right]
left = mid + 1;
}
}
if(nums[left] == target) { // 判断能否找到target
return left;
}
return -1;
}
// 查找最后一个target出现的位置
private int ceil(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = (left + right + 1) >>> 1;
if (target > nums[mid]) { // 查找区间为[mid+1,right]
left = mid + 1;
} else if(target == nums[mid]){ // 查找区间为[mid,right]
left = mid;
} else { // target < nums[mid],查找区间为[left,mid-1]
right = mid - 1;
}
}
return left;
}
}
}
作为一只菜鸟,正在努力学习中,希望大家多多给我提建议或者指点我,感激不尽!