• 【算法系列 | 8】深入解析查找算法之—二分查找


    序言

    心若有阳光,你便会看见这个世界有那么多美好值得期待和向往。

    决定开一个算法专栏,希望能帮助大家很好的了解算法。主要深入解析每个算法,从概念到示例。

    我们一起努力,成为更好的自己!

    今天第8讲,讲一下查找算法的二分查找

    1 基础介绍

    查找算法是很常见的一类问题,主要是将一组数据按照某种规则进行排序。

    以下是一些常见的查找算法及其应用场景:

    1. 布隆过滤器(Bloom Filter):适用于判断一个元素是否存在于一个大规模的数据集中,时间复杂度为O(1),但有一定的误判率。
    2. 二分查找(Binary Search):适用于有序数组中查找元素,时间复杂度为O(log n);
    3. 哈希表查找(Hash Table:适用于快速查找和插入元素,时间复杂度为O(1),但需要额外的存储空间;
    4. 线性查找(Linear Search):适用于无序数组中查找元素,时间复杂度为O(n);
    5. 插值查找(Interpolation Search):适用于有序数组中查找元素,时间复杂度为O(log log n),但是对于分布不均匀的数据集效果不佳;
    6. 斐波那契查找(Fibonacci Search):适用于有序数组中查找元素,时间复杂度为O(log n),但需要额外的存储空间;
    7. 树表查找(Tree Search):适用于快速查找和插入元素,时间复杂度为O(log n),但需要额外的存储空间;
    8. B树查找(B-Tree):适用于大规模数据存储和查找,时间复杂度为O(log n),但需要额外的存储空间;

    一、二分查找介绍

    1.1 原理介绍

    二分查找算法(Binary Search)是一种用于在有序数据集合中查找目标元素的高效搜索算法。

    它的实现原理基于分而治之(Divide and Conquer)的思想,通过将查找范围逐渐缩小一半来快速定位目标元素。

    下面详细讲解二分查找算法的实现原理:

    前提条件

    • 数据集合必须是有序的通常是升序排列的。
    • 数据集合应为静态,不应频繁插入或删除元素。

    步骤

    1. 初始化指针:首先,确定查找范围,通常是整个数据集合。然后,初始化两个指针:

      • 左指针left)指向查找范围的起始位置,通常是0。
      • 右指针right)指向查找范围的结束位置,通常是数据集合的最后一个元素的索引。
    2. 查找中间元素:计算左右指针的中间位置,即 (left + right) / 2

    3. 比较中间元素:将目标元素与中间位置的元素进行比较。

      • 如果目标元素等于中间位置的元素,则找到了目标元素,查找结束。
      • 如果目标元素小于中间位置的元素,则更新右指针为中间位置的前一个位置,将查找范围缩小为左半部分。
      • 如果目标元素大于中间位置的元素,则更新左指针为中间位置的后一个位置,将查找范围缩小为右半部分。
    4. 重复步骤2和步骤3:不断重复计算中间位置和比较中间元素,直到以下任一条件满足:

      • 找到目标元素,即目标元素等于中间位置的元素。
      • 左指针大于右指针,表示查找范围为空,目标元素不存在。

    示例: 假设有一个有序数组 arr,要查找目标元素 target

    arr = [1, 3, 5, 7, 9, 11, 13, 15, 17] target = 9

    初始时,left 指向0,right 指向8,中间元素为 arr[4],即9。

    1. 目标元素与中间元素相等,查找结束,找到了目标元素。

    二分查找算法的关键在于每一次迭代都将查找范围缩小一半,这导致算法的时间复杂度为 O(log n),其中 n 是数据集合的大小。这使得二分查找非常高效,特别适用于大规模的有序数据集合。但需要注意的是,由于它要求数据集合必须是有序的,因此如果数据无序,需要先进行排序操作。

    1.2 优缺点

    优点:

    1. 高效性:二分查找的时间复杂度为 O(log n),其中 n 是数组的长度。由于每次迭代都将搜索范围减半,因此算法的时间复杂度相对较低。在大型有序数组中,二分查找比线性查找要快得多。
    2. 简单易实现:二分查找的实现相对简单,只需按照一定的步骤进行比较和调整指针即可。

    缺点:

    1. 仅适用于有序数组:二分查找要求数组必须是有序的,否则无法保证正确的查找结果。如果数组未排序,需要先进行排序操作,这可能会增加额外的时间复杂度。
    2. 内存占用较大:二分查找通常需要占用较大的内存空间,因为它需要存储整个数组。在某些情况下,这可能会成为一个问题,特别是当处理大型数组时。
    3. 难以处理插入和删除操作:二分查找适用于静态数组,即不经常进行插入和删除操作的数组。如果需要频繁地插入或删除元素,由于需要保持数组有序性,可能需要进行额外的操作,导致效率降低。

    所以,二分查找算法在查找有序数组中的元素时非常高效。它的主要优点是高效性和简单易实现。

    然而,它的缺点是仅适用于有序数组、内存占用较大以及难以处理插入和删除操作。

    因此,在选择使用二分查找时,需要根据具体的问题和数据结构的特点综合考虑其优缺点。

    1.3 复杂度

    这种算法的效率很高,时间复杂度为O(log n),适用于大规模数据集合。

    1.4 使用场景

    二分查找适用于以下场景:

    1. 有序数组:二分查找要求数组是有序的,因此当我们需要在一个已排序的数组中查找某个元素时,可以使用二分查找来提高查找效率。

    2. 查找静态数据:如果数据集合是静态的,即不会频繁地插入、删除或修改元素,而是在固定的数据集上进行查找操作,那么二分查找是一个很好的选择。

    3. 大型数据集合:二分查找的时间复杂度为 O(log n),其中 n 是数组的长度。相比线性查找的 O(n) 时间复杂度,二分查找在大型数据集合中的查找效率更高。

    4. 查找边界值:由于二分查找可以快速定位有序数组中的中间元素,因此它在查找边界值或者某个特定范围内的元素时非常有用。例如,在一个有序整数数组中查找某个元素第一次出现的位置或最后一次出现的位置。

    5. 数值区间判断:对于满足某种数值规律的数组,可以使用二分查找进行区间判断。例如,对于一个有序的数值范围数组,可以使用二分查找确定某个数值是否在某个区间内。

    二、代码实现

    2.1 Python实现

    代码示例

    1. def binary_search(arr, target):
    2. left = 0
    3. right = len(arr) - 1
    4. while left <= right:
    5. mid = (left + right) // 2
    6. if arr[mid] == target:
    7. return mid
    8. elif arr[mid] < target:
    9. left = mid + 1
    10. else:
    11. right = mid - 1
    12. return -1 # 如果未找到目标元素,返回 -1
    13. # 示例使用
    14. arr = [1, 3, 5, 7, 9, 11, 13]
    15. target = 7
    16. result = binary_search(arr, target)
    17. if result != -1:
    18. print("目标元素在数组中的索引为:", result)
    19. else:
    20. print("目标元素不在数组中")

    代码讲解

    在这个示例中,我们定义了一个名为 binary_search 的函数,它接受一个有序数组 arr 和目标元素 target 作为输入参数。算法使用两个指针 left 和 right 来表示搜索的区间。开始时,left 指向数组的起始位置,right 指向数组的末尾位置。

    然后,算法进入一个循环,当 left 小于等于 right 时,持续执行以下步骤:计算中间元素的索引 mid,并将其与目标元素进行比较。如果 arr[mid] 等于 target,则找到了目标元素,返回 mid。如果 arr[mid] 小于 target,则说明目标元素可能在 mid 的右侧,将 left 更新为 mid + 1

    1. 如果 arr[mid] 大于 target,则说明目标元素可能在 mid 的左侧,将 right 更新为 mid - 1
    2. 如果循环结束时仍未找到目标元素,说明目标元素不存在于数组中,返回 -1。

    运行结果

    运行上述代码的结果将会是:

    目标元素在数组中的索引为: 3
    

    根据给定的有序数组 [1, 3, 5, 7, 9, 11, 13] 和目标元素 7,经过二分查找算法,找到了目标元素在数组中的索引位置为 3。

    2.2 Java实现

    代码示例

    1. public class BinarySearch {
    2. public static int binarySearch(int[] arr, int target) {
    3. int left = 0;
    4. int right = arr.length - 1;
    5. while (left <= right) {
    6. int mid = left + (right - left) / 2;
    7. if (arr[mid] == target) {
    8. return mid;
    9. } else if (arr[mid] < target) {
    10. left = mid + 1;
    11. } else {
    12. right = mid - 1;
    13. }
    14. }
    15. return -1; // 如果未找到目标元素,返回 -1
    16. }
    17. // 示例使用
    18. public static void main(String[] args) {
    19. int[] arr = {1, 3, 5, 7, 9, 11, 13};
    20. int target = 7;
    21. int result = binarySearch(arr, target);
    22. if (result != -1) {
    23. System.out.println("目标元素在数组中的索引为: " + result);
    24. } else {
    25. System.out.println("目标元素不在数组中");
    26. }
    27. }
    28. }

    代码讲解

    在这个示例中,定义了一个名为 BinarySearch 的类,其中包含了一个静态方法 binarySearch。该方法接受一个有序数组 arr 和目标元素 target 作为输入参数,并返回目标元素在数组中的索引。

    在 binarySearch 方法中,使用两个指针 left 和 right 来表示搜索的区间。开始时,left 指向数组的起始位置,right 指向数组的末尾位置。

    然后,算法进入一个循环,当 left 小于等于 right 时,持续执行以下步骤:

    1. 计算中间元素的索引 mid,并将其与目标元素进行比较。
    2. 如果 arr[mid] 等于 target,则找到了目标元素,返回 mid
    3. 如果 arr[mid] 小于 target,则说明目标元素可能在 mid 的右侧,将 left 更新为 mid + 1
    4. 如果 arr[mid] 大于 target,则说明目标元素可能在 mid 的左侧,将 right 更新为 mid - 1
    5. 如果循环结束时仍未找到目标元素,说明目标元素不存在于数组中,返回 -1。

    运行结果

    运行上述 Java 代码的结果将会是:

    目标元素在数组中的索引为: 3
    

    根据给定的有序数组 [1, 3, 5, 7, 9, 11, 13] 和目标元素 7,经过二分查找算法,找到了目标元素在数组中的索引位置为 3。

    三、图书推荐

    清华社【秋日阅读企划】领券立享优惠

    IT好书 5折叠加10元 无门槛优惠券:活动

    活动时间:9月4日-9月17日,先到先得,快快抢

    图书名称:

    • 《Python从入门到精通(第3版)(软件开发视频大讲堂)》

    图书介绍

    《Python从入门到精通(第3版)》从初学者角度出发,通过通俗易懂的语言、丰富多彩的实例,详细介绍了使用Python进行程序开发应该掌握的各方面技术。

    全书共分27章,包括初识Python、Python语言基础、运算符与表达式、流程控制语句、列表和元组、字典和集合、字符串、Python中使用正则表达式、函数、面向对象程序设计、模块、文件及目录操作、操作数据库、使用进程和线程、网络编程、异常处理及程序调试、Pygame游戏编程、推箱子游戏、网络爬虫开发、火车票分析助手、数据可视化、京东电商销售数据分析与预测、Web编程、Flask框架、e起去旅行网站、Python自动化办公、AI图像识别工具等内容。

    书中所有知识都结合具体实例进行介绍,涉及的程序代码都给出了详细的注释,读者可轻松领会Python程序开发的精髓,快速提升开发技能。 

     

    参与方式 

    图书数量:本次送出 3 本   !!!⭐️⭐️⭐️
    活动时间:截止到 2023-09-16 12:00:00

    抽奖方式:

    • 评论区随机抽取小伙伴!

    留言内容,以下方式都可以:

    • 根据文章内容进行高质量评论

    参与方式:关注博主、点赞、收藏,评论区留言 

    中奖名单 

    🍓🍓 获奖名单🍓🍓

     中奖名单:请关注博主动态

    名单公布时间:2023-09-16 下午

  • 相关阅读:
    线程池[重点]
    2.5 表达式
    【排错/运维】修复HDFS丢失、损坏以及副本数的问题
    微服务-gateway【服务网关入门】
    Android11修改自动允许连接到建议的WLAN网络
    网络与信息安全基础知识 (软件设计师笔记)
    容器化 | 在 Rancher 中部署 MySQL 集群
    GitLab 卸载步骤 - 完全卸载
    视频媒介VS文字媒介
    day14-HTTP01
  • 原文地址:https://blog.csdn.net/weixin_36755535/article/details/132862454