假设我们有一个有序数组 arr = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19],并且我们想要查找数字 7 是否在这个数组中。
折半查找步骤
初始化:设置查找范围的上下界,low = 0,high = len(arr) - 1 = 9。
第一次查找:
计算中间位置 mid = (low + high) // 2 = (0 + 9) // 2 = 4。 检查 arr[mid] = arr[4] = 9 是否等于目标值 7。 因为 9 > 7,所以调整查找范围为左半部分,即 high = mid - 1 = 4 - 1 =
3。 第二次查找:
重新计算中间位置 mid = (low + high) // 2 = (0 + 3) // 2 = 1。 检查 arr[mid] = arr[1] = 3 是否等于目标值 7。 因为 3 < 7,所以调整查找范围为右半部分,即 low = mid + 1 = 1 + 1 = 2。
第三次查找:
重新计算中间位置 mid = (low + high) // 2 = (2 + 3) // 2 = 2(注意,这里因为 low 和 high 相等,所以 mid 也会是这个值)。 检查 arr[mid] = arr[2] = 5 是否等于目标值 7。 因为 5 < 7,但此时 low 已经等于 mid,且 mid 小于 high(即 2 < 3),所以直接更新 low 为 mid + 1,即 low = 3。 注意,此时 low 已经大于 high(3 > 3),循环结束。但在实际实现中,通常会在循环条件中检查 low <= high,因此这里实际上不会执行另一次循环迭代。不过,为了说明过程,我们假设循环体仍会执行一次检查。
检查是否找到:
由于 low 已经大于 high,循环结束。此时,我们检查 low(或 high,因为它们现在是相同的)所指向的元素是否为目标值。但在这个例子中,由于 low(或 high)指向的是 arr[3],即 7 的右侧,所以我们需要检查 arr[low - 1](或 arr[high],因为在这个特定情况下它们是相等的)。然而,在标准的二分查找实现中,如果循环结束时没有返回结果,则意味着元素不存在于数组中。 但在这个具体例子中,由于我们知道 low 在最后一次迭代前是 2,且 arr[2] = 5 < 7,而 arr[3] = 7 正是我们要找的值,但由于我们的循环条件(low <= high)阻止了额外的检查,我们需要稍微调整逻辑来确保在找到 7 时能够返回其索引。
正确的二分查找实现
下面是一个正确的二分查找实现,它会在找到目标值时立即返回索引:
def binary_search(arr, target):
low, high = 0, len(arr) - 1
while low <= high:
mid = (low + high) // 2
if arr[mid] == target:
return mid # 找到目标值,返回索引
elif arr[mid] < target:
low = mid + 1 # 调整查找范围到右半部分
else:
high = mid - 1 # 调整查找范围到左半部分
return -1 # 未找到目标值,返回-1
# 示例
arr = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
target = 7
result = binary_search(arr, target)
print(f"元素{target}在数组中的索引为:{result}") # 输出:元素7在数组中的索引为:3
在这个实现中,当 arr[mid] == target 时,函数立即返回 mid,因此不需要额外的检查来确定是否找到了目标值。