1.题目要求:
给定一个 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]之间。
2.解题思路:
如果数组是有序的,而且也没有重复的元素(元素重复的话会使返回的下标不唯一)。那么就满足了使用二分查找的前提条件。我们可以使用二分法查找某个目标值。
我们先定义两个变量存放数组首尾元素的下标:
first和end,
first = 0然而!end的取值却可以有两种:
第一种:
int end = nums.size() - 1;这种方法让我们在左闭右闭的区间
[first,end]里寻找目标值target第二种:
int end = nums.size();这种方法让我们在左闭右开的区间
[first,end)里寻找目标值target这两种end的取值关系到边界条件的设定,即区间的定义。
这两种区间又各自决定着循环或选择语句该怎么写:
即到底是
while(first < end)还是while(first <= end),到底是first = middle呢,还是要first = middle - 1等等。然后就是中间变量
middle的定义,即int middle = first + ((end - first)/2);在这里,并不推荐使用语句
middle = (first + end) / 2,因为当first和end都过大时,可能会造成数据溢出,进而导致middle出错。所以我们推荐使用语句int middle = first + ((end - first)/2);来定义middle。接下来就是循环条件和判断条件的编写了
每次判断,都把无效的边界点去掉,逐渐逼近目标值。在循环中,要么找到了目标值,返回下标
middle。要么没有找到目标值,返回-1。
3.解题方法一:
C++解题:
分析:
int end= nums.size() - 1;- 定义了
target在区间[first,end]中,即左闭右闭的区间上
while (first <= end)- 当
first等于end时,区间[first,end]仍有效,所以用**<=**
if (nums[middle] > target) end = middle - 1;- 这表明
target在数组的左半边,所以更新区间为:[first , middle - 1]
else if (nums[middle] < target) first = middle + 1;- 这表明
target在数组的右半边,所以更新区间为:[middle + 1, end]
else return middle;- 这表明数组中找到目标值,直接返回下标,即
nums[middle] == target
- 在循环外头
return -1表明在循环里未找到目标值,所以返回-1。
4.解题方法二:
分析:
int end= nums.size();- 定义了
target在区间[first,end)中,即左闭右开的区间上
while (first < end)- 因为在这里
first不会等于end,所以我们使用 < 来作为循环条件。
if (nums[middle] > target) end = middle;- 这里的
end并不是赋值为middle-1
else if (nums[middle] < target) first = middle + 1;- 这表明
target在数组的右半边,所以更新区间为:[middle + 1, end)
else return middle;- 这表明数组中找到目标值,直接返回下标,即
nums[middle] == target
- 在循环外头
return -1表明在循环里未找到目标值,所以返回-1。
5.总结:
使用二分查找的前提条件:
- 元素有顺序地存储
- 元素按关键字顺序存储
基本思想:
- 数组a中的元素按顺序排列
- 将数组分为两半,个数相同
- 取中间数值a[n/2]与想要查找的元素x作比较
- 若a[n/2]大于x,那么继续在左区间里查找x
- 若a[n/2]小于x,那么继续在右区间里查找x
- 若a[n/2]==x,则找到了x