day01/60
给定一个 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]之间。
解题思路
本题的关键在于利用二分查找在有序且无重复元素的数组中定位目标值。二分查找法是一种高效的搜索算法,其前提是数组必须有序,且数组中的元素不重复。当遇到满足这些条件的题目时,应优先考虑使用二分查找。
二分查找虽然逻辑简单,但边界条件的处理却需要细心。常见的困惑在于如何设定循环条件和更新边界值。为了解决这些问题,我们需要明确一个核心概念:循环不变量,即在循环过程中保持不变的性质或规则。
在二分查找中,我们通常定义两种区间:左闭右闭[left, right]或左闭右开[left, right)。下面,我将基于左闭右闭区间来阐述二分查找的实现。
二分法实现(左闭右闭区间)
我们定义目标值target位于一个左闭右闭的区间[left, right]。这个定义对后续的代码实现至关重要。
由于target被定义在[left, right]区间内,因此我们的循环条件应为while (left <= right)。这里的“<=”确保了当left和right相等时,我们仍然会检查该位置的元素。
在每次循环中,我们计算中间位置middle,并检查nums[middle]与目标值target的关系。如果nums[middle]大于target,说明target应该在middle的左侧,因此我们将right更新为middle - 1。这里的“-1”是因为nums[middle]已经被检查过且不等于target,所以我们需要将搜索范围缩小到middle的左侧。
例如,在数组1,2,3,4,7,9,10中查找元素2时,我们会不断地将搜索范围缩小,直到找到目标值或确定目标值不存在于数组中。
通过这样的步骤,我们可以清晰地理解并实现二分查找算法,同时确保代码的正确性和可读性。
Python代码如下:
左闭右闭
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) - 1 # 定义target在左闭右闭的区间里,[left, right]
while left <= right:
middle = left + (right - left) // 2
if nums[middle] > target:
right = middle - 1 # target在左区间,所以[left, middle - 1]
elif nums[middle] < target:
left = middle + 1 # target在右区间,所以[middle + 1, right]
else:
return middle # 数组中找到目标值,直接返回下标
return -1 # 未找到目标值
左闭右开
class Solution:
def search(self, nums: List[int], target: int) -> int:
left, right = 0, len(nums) # 定义target在左闭右开的区间里,即:[left, right)
while left < right: # 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
middle = left + (right - left) // 2
if nums[middle] > target:
right = middle # target 在左区间,在[left, middle)中
elif nums[middle] < target:
left = middle + 1 # target 在右区间,在[middle + 1, right)中
else:
return middle # 数组中找到目标值,直接返回下标
return -1 # 未找到目标值