/**
在一个近似递增的数组中查找指定元素,该元素可能有多个找出其出现的区间
单次普通二分只能查到一个元素且无法确定边界;对二分改进即可
左边界:当查到目标元素时,记录下标并将right迭代为mid-1,继续向左二分直到搜寻结束,得到左边界
右边界:当查到目标元素时,记录下标并将left迭代为mid+1,继续向右二分直到搜寻结束,得到右边界
即进行两次改进二分,查找左边界与右边界
优化:
查找左边界与右边界大量代码相同,仅left与right指针迭代逻辑不同
添加变量boolean isLeft,控制查找方向,一个方法即可完成左、右边界的查找
*/
class Solution {
/**
在一个近似递增的数组中查找指定元素,该元素可能有多个找出其出现的区间
单次普通二分只能查到一个元素且无法确定边界;对二分改进即可
左边界:当查到目标元素时,记录下标并将right迭代为mid-1,继续向左二分直到搜寻结束,得到左边界
右边界:当查到目标元素时,记录下标并将left迭代为mid+1,继续向右二分直到搜寻结束,得到右边界
即进行两次改进二分,查找左边界与右边界
优化:
查找左边界与右边界大量代码相同,仅left与right指针迭代逻辑不同
添加变量boolean isLeft,控制查找方向,一个方法即可完成左、右边界的查找
*/
public int[] searchRange(int[] nums, int target) {
//搜寻左边界
int left = findBound(nums, target, true);
if(left == -1) { //若为-1代表不存在该元素,无需继续搜寻
return new int[]{-1,-1};
}
//搜寻右边界
int right = findBound(nums, target, false);
return new int[]{left, right};
}
private int findBound(int[] nums, int target, boolean isLeft) {
//双指针,置于有效部分两端
int left = 0, right = nums.length - 1;
//不断迭代,记录已探明的边界
int index = -1;
while(left <= right) {
//均分数组,缩小查找范围
int mid = left + right >>> 1;
//目标值在右半区,淘汰左半区
if(nums[mid] < target) {
left = mid + 1;
}
//目标值在左半区,淘汰右半区
else if(nums[mid] > target) {
right = mid - 1;
}
//查找到元素,迭代index,并依据findLeft确定查找方向
else {
index = mid;
if(isLeft) { //向左继续二分,迭代right
right = mid - 1;
} else { //向右二分,迭代left
left = mid + 1;
}
}
}
return index;
}
}