题目描述
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
题目分析
本题目要求返回的结果为目标值在数组中的开始位置和结束位置,是一个区间范围。这是和一般的二分查找题目的区别。
在这个题目中,我们需要分开来确定开始位置和结束位置。在确定开始位置和结束位置过程中,均是一次二分查找的过程。在这个过程中有两点:1、在开始位置的左边一定不存在结束位置;2、在结束位置的右边一定不存在开始位置。
确定开始位置,当mid
位置的元素小于target
,那mid
位置一定不是结果区间中的位置,即使得left = mid + 1
,反之使得right = mid
;
确定结束位置,与确定开始位置的判断正好相反。当mid
位置的元素大于target
时,那mid
位置一定不是结果区间中的位置,即使得right = mid - 1
,反之使得left = mid
。在此时,计算mid
值时,需要向上取整,即int mid = (left + right + 1) / 2
。
参考代码
public class SearchRange {
public int[] searchRange(int[] nums, int target) {
int length = nums.length;
if (length == 0) {
return new int[]{-1, -1};
}
int[] result = new int[]{-1, -1};
int prePosition = findPrePosition(nums, target);
if (prePosition == -1) {
return result;
}
int lastPosition = findLastPosition(nums, target);
if (lastPosition == -1) {
return result;
}
result[0] = prePosition;
result[1] = lastPosition;
return result;
}
/**
* 查找范围区间的开始位置
*
* @param nums
* @param target
* @return
*/
private int findPrePosition(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
// 严格小于一定不是解
if (nums[mid] < target) {
left = mid + 1;
} else {
// nums[mid] >= target;
right = mid;
}
}
if (nums[left] == target) {
return left;
}
return -1;
}
private int findLastPosition(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left + 1) / 2;
// 严格大于目标值,也一定不是右区间
if (nums[mid] > target) {
right = mid - 1;
} else {
// num[mid] <= target
left = mid;
}
}
if (nums[right] == target) {
return right;
}
return -1;
}
public static void main(String[] args) {
SearchRange searchRange = new SearchRange();
System.out.println(JSON.toJSONString(searchRange.searchRange(new int[]{5,7,7,8,8,10}, 8)));
}
}