Python 二分查找教程(0基础,通俗易懂)

1. 什么是二分查找?

二分查找是一种高效的查找算法,用于在有序数组中查找目标值。它的核心思想是通过不断缩小查找范围,快速定位目标值的位置。

  • 特点
    • 只能用于有序数组
    • 时间复杂度为 O(log n),比线性查找(O(n))快得多。

2. 二分查找的基本原理

假设你有一本字典,要查一个单词,你会从中间打开字典:

  1. 如果中间的单词正好是你想找的,就找到了!
  2. 如果目标单词在中间单词之前,就只看前半部分。
  3. 如果目标单词在中间单词之后,就只看后半部分。
  4. 重复以上步骤,直到找到目标单词或确定它不存在。

3. 二分查找的实现步骤

我们用一个例子来说明:在有序数组 [1, 3, 5, 7, 9, 11] 中查找数字 7

步骤

  1. 找到数组的中间位置,计算中间索引:
    mid = (left + right) // 2
    初始时,left = 0right = len(array) - 1
  2. 比较中间值与目标值:
    • 如果中间值等于目标值,返回索引。
    • 如果目标值小于中间值,继续在左半部分查找(更新 right = mid - 1)。
    • 如果目标值大于中间值,继续在右半部分查找(更新 left = mid + 1)。
  3. 重复上述过程,直到找到目标值或查找范围为空。

4. Python代码实现

以下是二分查找的完整代码实现:

def binary_search(arr, target):
    # 定义左右边界
    left, right = 0, len(arr) - 1

    while left <= right:
        # 计算中间索引
        mid = (left + right) // 2
        
        # 检查中间值是否为目标值
        if arr[mid] == target:
            return mid  # 找到目标值,返回索引
        elif arr[mid] < target:
            left = mid + 1  # 目标值在右半部分
        else:
            right = mid - 1  # 目标值在左半部分
    
    # 如果循环结束仍未找到,返回 -1 表示未找到
    return -1

# 测试代码
arr = [1, 3, 5, 7, 9, 11]
target = 7
result = binary_search(arr, target)
print(f"目标值 {target} 的索引是: {result}")

运行结果

目标值 7 的索引是: 3

5. 二分查找的关键点

  1. 数组必须有序:如果数组无序,先对数组进行排序(例如使用 sorted().sort() 方法)。
  2. 查找范围的更新:每次比较后,更新 leftright,确保范围不断缩小。
  3. 终止条件:当 left > right 时,表示查找范围为空,结束查找。

6. 二分查找的应用场景

  • 在有序数据中快速查找某个值的位置。
  • 查找某个值是否存在(返回布尔值)。
  • 查找第一个大于或等于目标值的位置(变体问题)。

7. 二分查找的变体问题

有时我们需要解决一些特殊的二分查找问题,比如:

  1. 查找第一个大于等于目标值的位置

    def find_first_greater_or_equal(arr, target):
        left, right = 0, len(arr) - 1
        while left <= right:
            mid = (left + right) // 2
            if arr[mid] >= target:
                right = mid - 1  # 继续向左查找更小的满足条件的值
            else:
                left = mid + 1
        return left  # 返回第一个大于等于目标值的位置
    
  2. 查找最后一个小于等于目标值的位置

    def find_last_less_or_equal(arr, target):
        left, right = 0, len(arr) - 1
        while left <= right:
            mid = (left + right) // 2
            if arr[mid] <= target:
                left = mid + 1  # 继续向右查找更大的满足条件的值
            else:
                right = mid - 1
        return right  # 返回最后一个小于等于目标值的位置
    

8. 常见错误与注意事项

  1. 数组是否有序:二分查找的前提是数组必须有序,否则结果不正确。
  2. 边界条件:注意 leftright 的更新,避免死循环。
  3. 整数溢出:在某些语言中(如C++),(left + right) // 2 可能导致溢出,建议使用 left + (right - left) // 2

9. 小练习

  1. 给定一个有序数组 [2, 4, 6, 8, 10, 12],使用二分查找找出数字 8 的索引。
  2. 修改二分查找代码,使其返回目标值是否存在(返回布尔值)。
  3. 实现一个函数,找到数组中第一个大于等于目标值的位置。

总结

二分查找是一种简单但非常高效的查找算法,适合处理有序数组。只要记住以下几点,就能轻松掌握:

  • 有序数组是前提。
  • 不断缩小范围是关键。
  • 更新边界条件要准确。

希望这篇教程对你有帮助!如果有任何疑问,请随时提问! 😊

5 条评论

  • @ 2025-4-9 19:08:19

    • @ 2025-4-9 19:02:56

      二分查找每次把查找区间缩小一半,在最坏情况下,查找次数满足公式 k=log2nk = \lceil\log_2 n\rceilkk 是查找次数,nn 是数据规模,\lceil\rceil 表示向上取整 ) 。以下是不同数据规模下10次二分查找的情况分析:

      • 100次:因为 log21006.64\log_2{100} \approx 6.64 ,向上取整为7次 ,10次二分查找远超过查找100个数据所需次数。
      • 1000次log210009.97\log_2{1000} \approx 9.97 ,向上取整为10次 ,10次二分查找最多可查找约1000个数据,刚好满足。
      • 10万次log210000016.61\log_2{100000} \approx 16.61 ,向上取整为17次 ,10次二分查找不能满足查找10万个数据的需求。
      • 100万次log2100000019.93\log_2{1000000} \approx 19.93 ,向上取整为20次 ,10次二分查找远不能满足查找100万个数据的需求 。
      • 1000万次log21000000023.26\log_2{10000000} \approx 23.26 ,向上取整为24次 ,10次二分查找远不能满足查找1000万个数据的需求 。
      • 1亿次log210000000026.58\log_2{100000000} \approx 26.58 ,向上取整为27次 ,10次二分查找远不能满足查找1亿个数据的需求 。

      所以10次二分查找最多能处理约1000个数据规模,仅在1000次这个量级符合要求,其他量级(100次远不需要10次查找,10万、100万、1000万、1亿次则超出了10次查找能处理的范围 )不符合。

      • @ 2025-4-5 10:06:24

        Python 递归版本的二分查找教程(0基础,通俗易懂)

        1. 什么是递归版本的二分查找?

        递归版本的二分查找是基于函数调用自身来实现的二分查找算法。它和普通版本的二分查找逻辑相同,但通过递归的方式来不断缩小查找范围。

        • 优点
          • 代码简洁直观。
          • 符合二分查找的“分治”思想。
        • 缺点
          • 可能会占用更多的栈空间(对于非常大的数组,可能导致栈溢出)。

        2. 递归的基本思想

        递归的核心思想是将大问题分解成小问题,直到问题变得足够简单可以直接解决。对于二分查找来说:

        1. 找到数组的中间值。
        2. 如果目标值等于中间值,直接返回索引。
        3. 如果目标值小于中间值,在左半部分继续查找。
        4. 如果目标值大于中间值,在右半部分继续查找。
        5. 重复上述过程,直到找到目标值或查找范围为空。

        3. 递归版本的二分查找步骤

        我们用一个例子来说明:在有序数组 [1, 3, 5, 7, 9, 11] 中查找数字 7

        步骤

        1. 定义递归函数,传入数组、目标值、当前的左右边界。
        2. 计算中间索引:
          mid = (left + right) // 2
        3. 比较中间值与目标值:
          • 如果中间值等于目标值,返回索引。
          • 如果目标值小于中间值,递归调用函数,查找左半部分(更新 right = mid - 1)。
          • 如果目标值大于中间值,递归调用函数,查找右半部分(更新 left = mid + 1)。
        4. 如果 left > right,表示查找范围为空,返回 -1 表示未找到。

        4. Python代码实现

        以下是递归版本的二分查找完整代码:

        def binary_search_recursive(arr, target, left, right):
            # 基本条件:如果查找范围为空,返回 -1
            if left > right:
                return -1
        
            # 计算中间索引
            mid = (left + right) // 2
        
            # 检查中间值是否为目标值
            if arr[mid] == target:
                return mid  # 找到目标值,返回索引
            elif arr[mid] < target:
                # 目标值在右半部分,递归查找右半部分
                return binary_search_recursive(arr, target, mid + 1, right)
            else:
                # 目标值在左半部分,递归查找左半部分
                return binary_search_recursive(arr, target, left, mid - 1)
        
        # 测试代码
        arr = [1, 3, 5, 7, 9, 11]
        target = 7
        result = binary_search_recursive(arr, target, 0, len(arr) - 1)
        print(f"目标值 {target} 的索引是: {result}")
        

        运行结果

        目标值 7 的索引是: 3
        

        5. 代码解析

        1. 递归终止条件
          • left > right 时,表示查找范围为空,直接返回 -1
        2. 递归调用
          • 根据中间值与目标值的比较结果,递归调用函数,更新查找范围。
        3. 返回值
          • 如果找到目标值,返回其索引。
          • 如果未找到,返回 -1

        6. 递归版本的关键点

        1. 递归终止条件:必须明确什么时候停止递归(即 left > right)。
        2. 参数传递:每次递归调用时,需要正确传递新的 leftright 边界。
        3. 避免栈溢出:递归深度受限于系统栈大小,因此不适用于非常大的数组。

        7. 递归版本的应用场景

        • 查找某个值是否存在。
        • 查找某个值的位置(返回索引)。
        • 解决一些变体问题(如第一个大于等于目标值的位置)。

        8. 小练习

        1. 给定一个有序数组 [2, 4, 6, 8, 10, 12],使用递归版本的二分查找找出数字 8 的索引。
        2. 修改递归版本的代码,使其返回目标值是否存在(返回布尔值)。
        3. 实现一个递归版本的函数,找到数组中第一个大于等于目标值的位置。

        9. 总结

        递归版本的二分查找虽然代码更简洁,但也需要注意以下几点:

        • 递归终止条件是关键,否则会导致无限递归。
        • 数组必须有序,否则结果不正确。
        • 对于非常大的数组,建议使用非递归版本以避免栈溢出。

        希望这篇教程对你有帮助!如果有任何疑问,请随时提问! 😊

        • @ 2025-4-5 9:17:37

          Python 递归版本的二分查找教程。

          一、递归的概念

          在开始讲解递归版本的二分查找之前,我们先来了解一下什么是递归。递归就是在函数的定义中调用函数自身的方法。一个递归函数通常包含两个部分:

          1. 基线条件:也叫终止条件,是递归结束的条件,防止函数无限调用自身导致栈溢出错误。
          2. 递归条件:函数调用自身的部分,每次调用会使问题规模逐渐减小,最终达到基线条件。

          二、二分查找的基本思路回顾

          二分查找是一种在有序数组中查找特定元素的高效算法。它的基本思路是将有序数组分成两部分,通过比较中间元素和目标元素的大小关系,来决定继续在数组的左半部分还是右半部分查找,不断缩小查找范围,直到找到目标元素或者确定目标元素不存在。

          三、递归版本二分查找的实现步骤

          1. 基线条件:当左边界大于右边界时,说明目标元素不在数组中,此时返回 -1。
          2. 递归条件
            • 计算中间位置 mid
            • 如果中间元素等于目标元素,返回中间位置 mid
            • 如果中间元素大于目标元素,递归调用函数在左半部分查找。
            • 如果中间元素小于目标元素,递归调用函数在右半部分查找。

          四、Python 代码示例

          def binary_search_recursive(arr, target, left, right):
              # 基线条件:左边界大于右边界,说明目标元素不存在
              if left > right:
                  return -1
          
              # 计算中间位置
              mid = (left + right) // 2
          
              # 如果中间元素等于目标元素,返回中间位置
              if arr[mid] == target:
                  return mid
              # 如果中间元素大于目标元素,在左半部分继续查找
              elif arr[mid] > target:
                  return binary_search_recursive(arr, target, left, mid - 1)
              # 如果中间元素小于目标元素,在右半部分继续查找
              else:
                  return binary_search_recursive(arr, target, mid + 1, right)
          
          
          # 示例用法
          arr = [1, 3, 5, 7, 9, 11, 13]
          target = 7
          result = binary_search_recursive(arr, target, 0, len(arr) - 1)
          if result != -1:
              print(f"目标元素 {target} 在数组中的索引是 {result}")
          else:
              print(f"目标元素 {target} 不在数组中")
          

          五、代码解释

          1. 函数 binary_search_recursive 接收四个参数:arr 是要查找的有序数组,target 是目标元素,left 是当前查找范围的左边界,right 是当前查找范围的右边界。
          2. 首先判断基线条件 if left > right:,如果左边界大于右边界,说明目标元素不在当前查找范围内,返回 -1。
          3. 计算中间位置 mid = (left + right) // 2
          4. 然后比较中间元素和目标元素:
            • 如果 arr[mid] == target,表示找到了目标元素,返回 mid
            • 如果 arr[mid] > target,说明目标元素在左半部分,递归调用 binary_search_recursive(arr, target, left, mid - 1)
            • 如果 arr[mid] < target,说明目标元素在右半部分,递归调用 binary_search_recursive(arr, target, mid + 1, right)
          5. 最后通过示例用法展示了如何调用这个递归函数进行二分查找,并根据返回结果输出相应的信息。

          希望这个教程能让你理解 Python 递归版本的二分查找,通过不断练习,你可以更好地掌握递归和二分查找的应用。

          • @ 2025-4-5 8:48:05

            二分查找概述

            二分查找(Binary Search)也被叫做折半查找,它是一种在有序数组里高效查找特定元素的算法。其核心思想是不断把搜索区间一分为二,依据中间元素和目标元素的大小关系,排除掉一半的搜索区间,进而逐步缩小搜索范围,直至找到目标元素或者确定目标元素不存在。

            适用条件

            二分查找只能用于有序数组,即数组里的元素按照升序或者降序排列。

            算法步骤

            1. 初始化:设定左右边界,左边界 left 指向数组首个元素,右边界 right 指向数组末尾元素。
            2. 计算中间位置:算出中间位置 mid,公式为 mid = (left + right) // 2(在 Python 里使用 // 进行整除操作)。
            3. 比较中间元素和目标元素
              • 若中间元素等于目标元素,就找到了目标元素,返回中间位置 mid
              • 若中间元素大于目标元素,说明目标元素在左半部分,更新右边界 right = mid - 1
              • 若中间元素小于目标元素,说明目标元素在右半部分,更新左边界 left = mid + 1
            4. 重复步骤 2 和 3:只要左边界小于等于右边界,就持续执行上述操作。
            5. 未找到目标元素:若左边界大于右边界,表明目标元素不在数组中,返回 -1。

            Python 代码实现

            def binary_search(arr, target):
                # 初始化左边界
                left = 0
                # 初始化右边界
                right = len(arr) - 1
            
                while left <= right:
                    # 计算中间位置
                    mid = (left + right) // 2
            
                    if arr[mid] == target:
                        # 找到目标元素,返回中间位置
                        return mid
                    elif arr[mid] > target:
                        # 中间元素大于目标元素,更新右边界
                        right = mid - 1
                    else:
                        # 中间元素小于目标元素,更新左边界
                        left = mid + 1
            
                # 未找到目标元素,返回 -1
                return -1
            
            # 示例用法
            arr = [1, 3, 5, 7, 9, 11, 13]
            target = 7
            result = binary_search(arr, target)
            if result != -1:
                print(f"目标元素 {target} 在数组中的索引是 {result}")
            else:
                print(f"目标元素 {target} 不在数组中")
            
            

            复杂度分析

            • 时间复杂度:每次迭代都会把搜索区间缩小一半,所以时间复杂度是 O(logn)O(log n),这里的 nn 是数组的长度。
            • 空间复杂度:只使用了常数级别的额外空间,空间复杂度为 O(1)O(1)

            注意事项

            • 二分查找仅适用于有序数组,若数组无序,需先对其排序。
            • 在计算中间位置时,为避免整数溢出,可采用 mid = left + (right - left) // 2 替代 mid = (left + right) // 2

            通过上述教程,你应该对二分查找算法有了基本的认识,并且能够使用 Python 实现简单的二分查找功能。

            • 1