🐒题目
给定一个 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]之间。
🐔思路
这道题目的前提是数组为有序数组,同时题目还强调数组中无重复元素。有两种方法实现,时间都是复杂度:O(log n) 空间复杂度:O(1)
实现思路:
- 定义两个指针,左指针(left)指向数组第一个元素,右指针(right)指向数组最后一个元素
- 取数组中间(nums[mid])的值和目标值(target)比较
- 如果中值小于目标值,说明目标值在后半数组,将左指针(left)指向[mid+1];若中值大于目标值,说明目标值在前半数组,将右指针(right)指向[mid-1]。如果相等就直接返回
- 如果左指针指向的值的索引大于右指针指向的值的索引,说明已经查找完了也没找到目标值,返回-1
🐸方法一
第一种写法,我们定义了一个搜索区间为左闭右闭区间 [left, right] 。这个定义非常重要,因为它决定了我们如何更新搜索的边界。
有如下两点:
- while (left <= right) 要使用 <= ,当
left=right时,搜索区间仍然包含了一个元素,这个元素有可能是我们要找的目标值。因此,在left等于right的情况下继续搜索是有意义的。 - if (arr[mid] > target) right 要赋值为 mid - 1,说明目标值在前半数组,那么接下来要查找的右区间结束下标位置就是 mid - 1。
- if (arr[mid] < target) right 要赋值为 mid + 1,说明目标值在后半数组,那么接下来要查找的右区间结束下标位置就是 mid + 1。
// 二分查找
function binarySearch(arr, target) {
// 将数组的第一个和最后一个元素赋值给left和right
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) {
return mid;
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
const arr = [1, 3, 5, 7, 9, 11, 13];
console.log(binarySearch(arr, 7));
🦖方法二
如果说定义 target 是在一个在左闭右开的区间里,也就是 [left, right) ,那么二分法的边界处理方式则截然不同。
有如下两点:
- while (left < right),这里使用 < ,当
left等于right时,区间内实际上没有任何元素,因为右边界是开区间,不包含right。 - if (nums[middle] > target) right 更新为 middle,由于我们的搜索区间是左闭右开的,即
[left, right),我们需要将搜索范围缩小到左半部分,并且排除掉已经检查过的nums[middle]。
/**
* 在有序数组中查找目标值,如果找到则返回目标值的索引,否则返回-1
*
* @param arr 有序数组
* @param target 需要查找的目标值
* @returns 目标值的索引,如果未找到则返回-1
*/
function binarySearch(arr, target) {
// 将数组的第一个和最后一个元素赋值给left和right
let left = 0;
let right = arr.length - 1;
while (left < right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) {
return mid;
} else if (arr[mid] < target) {
left = mid;
} else {
right = mid;
}
}
return -1;
}
const arr = [1, 3, 5, 7, 9, 11, 13];
console.log(binarySearch(arr, 7));
🐳总结
二分法的核心在于通过不断地将搜索区间一分为二,来逐步缩小搜索范围,直至找到目标值或确定目标值不存在。而在这个过程中,搜索区间的定义(是左闭右闭、左闭右开,还是其他)以及相应的边界处理方式是至关重要的。
📖参考笔记:代码随想录