Q:给定一个按照升序排列的整数数组 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]
A:
分别找左和右边界。
复习二分查找。
寻找左侧边界的⼆分查找:
因为我们初始化 right = nums.length,所以决定了我们的「搜索区间」是 [left, right),所以决定了 while (left < right)
同时也决定了 left = mid + 1 (left不可取mid)和 right = mid(right可取mid)
因为我们需找到 target 的最左侧索引,所以当 nums[mid] == target 时不要立即返回,而要收紧右侧边界以锁定左侧边界,最后相等时left=right,取[left,right)。
寻找右侧边界的⼆分查找
因为我们初始化 right = nums.length,所以决定了我们的「搜索区间」是 [left, right),所以决定了 while (left < right)
同时也决定了 left = mid + 1 和 right = mid,因为我们需找到 target 的最右侧索引,所以当 nums[mid] == target 时不要⽴即返回,⽽要收紧左侧边界以锁定右侧边界
⼜因为收紧左侧边界时必须 left = mid + 1,所以最后⽆论返回 left 还是 right,必须减⼀
代码:
public int[] searchRange(int[] nums, int target) {
int[] result = new int[2];
Arrays.fill(result, -1);
if (nums.length == 0)
return result;
//取出来的边界是[left,right)
int leftRound = leftRound(nums, target);
int rightRound = rightRound(nums, target);
if (rightRound == 0 || leftRound == nums.length)//右边界为-1或左边界为length
return result;
if (nums[leftRound] != target)
return result;
result[0] = leftRound;
result[1] = rightRound - 1;
return result;
}
private int rightRound(int[] nums, int target) {
int left = 0, right = nums.length;
while (left < right) {
int mid = (left + right) / 2;
if (nums[mid] == target)
left = mid + 1;
else if (nums[mid] < target)
left = mid + 1;
else
right = mid;
}
return right;//右边界取不到
}
private int leftRound(int[] nums, int target) {
int left = 0, right = nums.length;
while (left < right) {
int mid = (left + right) / 2;
if (nums[mid] == target)
right = mid;
else if (nums[mid] < target)
left = mid + 1;
else
right = mid;
}
return left;//左边界可取到
}