菜单
×
   ❮   
HTML CSS JAVASCRIPT SQL PYTHON JAVA PHP HOW TO W3.CSS C C++ C# BOOTSTRAP REACT MYSQL JQUERY EXCEL XML DJANGO NUMPY PANDAS NODEJS R TYPESCRIPT ANGULAR GIT POSTGRESQL MONGODB ASP AI GO KOTLIN SASS VUE DSA GEN AI SCIPY AWS CYBERSECURITY DATA SCIENCE
     ❯   

DSA 二分查找


二分查找

二分查找算法通过在一个数组中搜索并返回其查找值的索引。

速度

当前值: {{ currVal }}

{{ msgDone }}
{{ index }}

运行模拟,看看二分查找算法是如何工作的。

要查看当找不到某个值时会发生什么,请尝试查找值 5。

二分查找比线性查找快得多,但需要一个已排序的数组才能工作。

二分查找算法通过检查数组中间的值来工作。如果目标值较小,则下一个要检查的值位于数组左半部分的中心。这种搜索方式意味着搜索区域始终是前一个搜索区域的一半,这就是二分查找算法如此之快的原因。

这个将搜索区域减半的过程一直持续到找到目标值,或者直到数组的搜索区域为空。

工作原理

  1. 检查数组中的中间值。
  2. 如果目标值较小,则搜索数组的左半部分。如果目标值较大,则搜索右半部分。
  3. 继续执行步骤 1 和 2,对数组的新缩减部分进行搜索,直到找到目标值或搜索区域为空。
  4. 如果找到该值,则返回目标值的索引。如果未找到目标值,则返回 -1。

手动演练

让我们手动尝试一下搜索过程,以便在实际用编程语言实现之前,能更清楚地理解二分查找是如何工作的。我们将搜索值 11。

步骤 1: 我们从一个数组开始。

[ 2, 3, 7, 7, 11, 15, 25]

步骤 2: 数组中索引为 3 的中间值,它等于 11 吗?

[ 2, 3, 7, 7, 11, 15, 25]

步骤 3: 7 小于 11,所以我们必须在索引 3 的右侧搜索 11。索引 3 右侧的值为 [ 11, 15, 25]。下一个要检查的值是中间值 15,位于索引 5。

[ 2, 3, 7, 7, 11, 15, 25]

步骤 4: 15 大于 11,所以我们必须在索引 5 的左侧搜索。我们已经检查了索引 0-3,因此只有索引 4 还有值需要检查。

[ 2, 3, 7, 7, 11, 15, 25]

我们找到了!

在索引 4 处找到值 11。

返回索引位置 4。

二分查找完成。


运行下面的模拟,以动画形式查看以上步骤。

{{ msgDone }}
[
{{ x.dieNmbr }}
]

手动运行:发生了什么?

起初,算法有两个变量“left”和“right”。

“left”为 0,表示数组中第一个值的索引,“right”为 6,表示数组中最后一个值的索引。

\((left+right)/2=(0+6)/2=3\) 是用于检查中间值(7)是否等于目标值(11)的第一个索引。

7 小于目标值 11,因此在下一个循环中,搜索区域必须限制在中间值的右侧:[ 11, 15, 25],位于索引 4-6。

为了限制搜索区域并找到一个新的中间值,“left”更新为索引 4,“right”仍然是 6。4 和 6 是新搜索区域的第一个和最后一个值的索引,即前一个中间值的右侧。新的中间值索引是 \((left+right)/2=(4+6)/2=10/2=5\)。

检查索引 5 处的新中间值:15 大于 11,所以如果目标值 11 存在于数组中,它必定在索引 5 的左侧。新搜索区域是通过将“right”从 6 更新为 4 来创建的。现在,“left”和“right”都是 4,\((left+right)/2=(4+4)/2=4\),因此只剩下索引 4 需要检查。在索引 4 处找到目标值 11,因此返回索引 4。

总而言之,二分查找算法就是通过不断地将数组搜索区域减半,直到找到目标值。

当找到目标值时,将返回目标值的索引。如果未找到目标值,则返回 -1。


二分查找实现

要实现二分查找算法,我们需要

  1. 一个包含要搜索值的数组。
  2. 一个要搜索的目标值。
  3. 一个只要 left 索引小于或等于 right 索引就一直运行的循环。
  4. 一个 if 语句,用于比较中间值与目标值,并在找到目标值时返回索引。
  5. 一个 if 语句,用于检查目标值是否小于或大于中间值,并更新“left”或“right”变量以缩小搜索区域。
  6. 循环结束后,返回 -1,因为此时我们知道目标值未找到。

二分查找的结果代码如下:

示例

def binarySearch(arr, targetVal):
    left = 0
    right = len(arr) - 1

    while left <= right:
        mid = (left + right) // 2

        if arr[mid] == targetVal:
            return mid
        
        if arr[mid] < targetVal:
            left = mid + 1
        else:
            right = mid - 1

    return -1

myArray = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
myTarget = 15

result = binarySearch(myArray, myTarget)

if result != -1:
    print("Value",myTarget,"found at index", result)
else:
    print("Target not found in array.")
运行示例 »

二分查找时间复杂度

有关时间复杂度的一般性解释,请访问此页面

有关插入排序时间复杂度的更全面详细的解释,请访问此页面

每次二分查找检查一个新值以确定它是否是目标值时,搜索区域都会减半。

这意味着,即使在最坏的情况下,二分查找也找不到目标值,它仍然只需要 \( \log_{2}n \) 次比较即可搜索完一个包含 \(n\) 个值的已排序数组。

二分查找的时间复杂度是

\[ O( \log_{2} n ) \]

注意: 在使用大 O 符号写时间复杂度时,我们也可以只写 \( O( \log n ) \),但 \( O( \log_{2} n ) \) 提醒我们数组搜索区域每次新比较都会减半,这是二分查找的基本概念,所以在这里我们保留了以 2 为底的表示。

如果我们画出二分查找在包含 \(n\) 个值的数组中查找值所需的时间与线性查找的比较图,我们会得到这张图

Binary Search Time Complexity

运行下面的二分查找模拟,使用不同数量的值 \(n\) 的数组,并观察二分查找需要多少次比较才能找到目标值

{{ this.userX }}

操作次数:{{ operations }}
未找到!

 

正如你在运行二分查找模拟时所看到的,即使数组很大且我们正在寻找的值未找到,搜索也只需要很少的比较。


DSA 练习

通过练习来测试自己

练习

什么类型的数组?

For the Binary Search algorithm to work,
the array must already be .

开始练习



×

联系销售

如果您想将 W3Schools 服务用于教育机构、团队或企业,请发送电子邮件给我们
sales@w3schools.com

报告错误

如果您想报告错误,或想提出建议,请发送电子邮件给我们
help@w3schools.com

W3Schools 经过优化,旨在方便学习和培训。示例可能经过简化,以提高阅读和学习体验。教程、参考资料和示例会不断审查,以避免错误,但我们无法保证所有内容的完全正确性。使用 W3Schools 即表示您已阅读并接受我们的使用条款Cookie 和隐私政策

版权所有 1999-2024 Refsnes Data。保留所有权利。W3Schools 由 W3.CSS 提供支持