给定一个 n
个元素有序的(升序)整型数组 nums
和一个目标值 target
,写一个函数搜索 nums
中的 target
,如果目标值存在返回下标,否则返回 -1
。
示例 1:
输入: nums[-1,0,3,5,9,12], target=9 输出: 4 解释: 9 出现在nums中并且下标为4
示例 2:
输入: nums[-1,0,3,5,9,12], target=2 输出: -1 解释: 2 不存在nums中,因此返回 -1
提示:
- 你可以假设
nums
中的所有元素是不重复的。 n
将在[1, 10000]
之间。nums
的每个元素都将在[-9999, 9999]
之间。
思路转变:
一开始的想法是这样解题的:
class Solution {
public int search(int[] nums, int target) {
int start=0,end=nums.length;
int mid=(start+end-1)/2;
while(nums[mid]!=target){
if(target<nums[mid]){
end=mid;
}else if(target>nums[mid]){
start=mid+1;
}
mid=(start+end-1)/2;
}
return mid;
}
}
有很多错误(用的是左闭右开区间),不过没有考虑目标值在数组中找不到,返回-1的情况
改进一:A出来了,要考虑分区的不变量,我用的是左闭右闭区间[start, end]
class Solution {
public int search(int[] nums, int target) {
int start=0,end=nums.length-1;
while(start<=end){
int mid=(start+end)/2;
if(target<nums[mid]){
end=mid-1;
}else if(target>nums[mid]){
start=mid+1;
}else{
return mid;
}
}
return -1;
}
}
改进二:用下左闭右开区间[left, right)
注意点:
1)左闭右开区间一定要注意左端点的值一定能取到,而右端点的值是取不到的(本题中取右端点的值则会造成数组角标越界),这样赋值的中点值也是取不到的
中点值计算:int mid=left+(right-left)/2
或int mid=left+((right-left)>>1)
等同于:int mid=(left+right)/2,上面那样写只是为了(left+right)的值直接导致栈溢出。
2)本题一定要构思好跳出while循环的条件,如果目标值存在返回下标,否则返回 -1
。
像要是while中这样写while(nums[mid] != target){...就不知道什么时候去写return -1...},就会导致写不出来。
直接把数组下表作为返回值,因为数组是有序的——暗示用二分法去做,如果数组中目标值存在,则与之相等的数组中的值只有一个,对应的下标也只有一个,while(left<right){...return mid...} return -1
class Solution {
public int search(int[] nums, int target) {
int left=0,right=nums.length;
while(left<right){
int mid=(left+right)/2;
if(target<nums[mid]){
right=mid;
}else if(target>nums[mid]){
left=mid+1;
}else{
return mid;
}
}
return -1;
}
}
总结:这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的,这些都是使用二分法的前提条件。