题目链接
题目:
分析:
- 如果我们查找元素的第一个位置, 随便假设一个位置为x,
- 如果这个数>target, 说明 [left,x-1] 是我们要找的位置, [x,right] 可以舍去, 让right =mid-1,
- 如果这个数==target, 说明[left,x] 是我们要找的位置, [x+1,right] 可以舍去, 让right = mid,
- (因为当这个数>= target时, 我们都移动right, 所以可以合并, 但是right只能=mid)
- 如果这个数<target, 说明[left,x] 可以舍去, [x+1,right] 是我们要找的位置, 让left = mid+1
- 此时说明, 数组具有"二段性", 说明可以使用"二分查找"
- 但是如果是用第一种二分查找: 朴素的二分查找, 发现我们不能确定当nums[mid] == target时, 它一定是我们要找的第一个位置或最后一个位置
- 细节:
- 1. 判断循环的条件, 一定是left < right , 不能写=
- 原因1: 因为我们要找元素的第一个位置, 如果当left == right 时, 说明此时的位置, 一定最终的结果, 无需再移动left和right
- 原因2: 如果当left == right 时, mid也==right == left, 如果进入循环, 那么如果此时这个数就是target, right = mid, 那么right的位置并没有变化, 此时还满足left<=right的条件, 就会形成死循环!!
- 2. 求中点的操作, 一定是 int mid = left + (right - left)/2
- 我们知道求中点我们有两种选择, 一种是 int mid = left + (right-left)/2 , 一种是 int mid = left + (right-left+1)/2, 这两种方式唯一的区别是当数组的大小为偶数时, /2之后是向下取整还是向上取整
- 那么此时, 如果我们选择向上取整, 即int mid = left + (right-left+1)/2, 那么当下面这种情况出现时, 此时mid指向right, 如果此时mid的位置==target, 应该让right = mid, right并没有发生变化, 此时判断left还是<right, 就会形成死循环!!!
- 1. 判断循环的条件, 一定是left < right , 不能写=
- 上述, 就是二分查找的第二种类型: 查找元素的第一个位置
- 同理, 要找元素的最后一个位置,假设一个位置为x,
- 如果这个数>target, 说明 [left,x-1] 是我们要找的位置, [x,right] 可以舍去, 让right =mid-1,
- 如果这个数==target, 说明[left,x-1] 可以舍去, [x,right] 是我们要找的位置, 让left = mid,
- 如果这个数<target, 说明[left,x] 可以舍去, [x+1,right] 是我们要找的位置, 让left = mid+1
- (因为当这个数<= target时, 我们都移动left, 所以可以合并, 但是left只能=mid)
- 细节:
- 1. 判断循环的条件, 一定是left < right , 不能写= 原因同上
- 2. 求中点的操作, 一定是 int mid = left + (right - left + 1)/2
- 那么此时, 如果我们选择向下取整, 即int mid = left + (right-left)/2, 那么当下面这种情况出现时, 此时mid指向left, 如果此时mid的位置==target, 应该让left = mid, left并没有发生变化, 此时判断left还是<right, 就会形成死循环!!!
- 上述, 就是二分查找的第三种类型: 查找元素的最后一个位置
代码:
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] ret = new int[2];
ret[0] = -1;
ret[1] = -1;
if(nums.length == 0) return ret;
int left = 0;
int right = nums.length-1;
while(left < right){
int mid = left + (right-left)/2;
if(nums[mid] >= target){
right = mid;
}else{
left = mid + 1;
}
}
if(nums[left] == target){
ret[0] = left;
}
left = 0;
right = nums.length-1;
while(left < right){
int mid = left + (right-left+1)/2;
if(nums[mid] <= target){
left = mid;
}else{
right = mid-1;
}
}
if(nums[left] == target){
ret[1] = left;
}
return ret;
}
}