IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【算法】二分查找 -> 正文阅读

[数据结构与算法]【算法】二分查找

概念

二分查找(Binary Search)是一种效率较高的查找方式,它的时间复杂度为O(logn)

二分查找要求数组按关键字有序排列,并设置两个指针leftright分别指向数组的开头和末尾,每次查找leftright的中间项[mid]

设数组非递减排列:

  1. 若目标值target等于中间项[mid],则找到元素;
  2. 若目标值target小于中间项[mid],则前往当前范围的左半部分查找,将right置为mid-1
  3. 若目标值target大于中间项[mid],则前往当前范围的右半部分查找,将left置为mid+1
  4. 若指针left跑到了right的右边,代表数组已查询完毕,且数组中没有目标值。由此可得循环的结束条件为left>right

要点

  1. 请牢记二分查找算法的时间复杂度O(log2n)

  2. 二分查找要求线性表必须以顺序结构存储,并且表中元素按关键字有序排列

  3. 二分查找一般设置2个指针,一个指针left指向数组开头另一个指针right指向数组末尾。查找循环的有效条件left<=right

    /* C++ */
    // n为数组长度
    for(int left=0, right=n-1;left<=right;) {...}
    
  4. 每次循环需要对leftright中间一位进行判断。以C++为例,由于除法运算符/会将整数除法结果自动舍去小数点,因此每次的目标为leftright的中间位(right-left结果为偶数)或中间偏左一位(right-left结果为奇数)。

    中间位mid的计算有两种方式:

    /* C++ */
    // n为数组长度
    // int left = 0;
    // int right = n - 1;
    ?
    /* 式1 */
    int mid = (right + left) >> 1;/* 式2 */
    int mid = ((right - left) >> 1) + left;
    

    将式1转化为数学公式如下:
    m i d = l e f t 2 + r i g h t 2 mid=\frac{left}{2}+\frac{right}{2} mid=2left?+2right?
    式1非常易懂,将两数相加除以2即为他们的中位数。但是这个写法已经这么简单了,为什么还会出现式2呢?

    原因是1式会产生数值溢出。我们粗暴一点,假设当前leftright皆为INT_MAX,即7FFFFFFFH,则两数相加就超出了int能表示的范围上限,最终mid的值就不正确了。将式2转换为数学公式如下:
    m i d = r i g h t 2 ? l e f t 2 + l e f t mid=\frac{right}{2}-\frac{left}{2}+left mid=2right??2left?+left

    可以看到只是进行了一次简单的改变,但式2是不会出现数值溢出问题的。

    当然1,在Python等语言中基本上是不会出现这种问题的,可以不用考虑。不过以这些语言为主力语言的小伙伴了解一下这个问题还是有益的。

    当然2,你要这么写也没人拦你(也确实能出正确结果:

    /* C++ */
    int mid = (long long(left) + long long(right)) >> 1;
    

    但万一leftright本身就是双长整型的最大值呢?即使mid定义为long long最终也会产生数值溢出的。所以无论怎样还是要用到式2的。

  5. leftright在什么时候变化可能会根据实际情况改变,这里仅记录最基础的改变时机。

    /* C++ */
    for (int left = 0, right = n - 1; left <= right;)
    {
        int mid = ...;
        if (target == nums[mid])
            return mid;
        else if (target < nums[mid])
            right = mid - 1;
        else
            left = mid + 1;
    }
    
  6. 二分查找不仅可以用于查找确切值,还能查找到比某个数大,或比某个数小的临界位置。

    (这里指的当然不是找到那个数target然后左边就比它小,右边就比它大的这种显而易见的问题。)

    在基础的算法结构上修改一些细节即可达到此目的。 参考案例[278. 第一个错误的版本]和以下案例:

    /* C++ */
    // 查找数组nums(升序排列)中第一个大于target的值。
    int left = 0, right = nums.size() - 1;
    int sub = nums.size();
    while (left <= right)
    {
        int mid = ((right - left) >> 1) + left;
        if (target < nums[mid])
        {
            sub = mid;
            right = mid - 1;
        }
        else
        {
            left = mid + 1;
        }
    }
    // 此时sub为数组nums中第一个比target的值的下标
    
    • target<nums[mid],则代表中间项大于target,第一个比target大的值要么在该项之前,要么就是它自身。由于没有变量能够,因此需要一个额外的变量sub存放当时的mid,并且置rightmid-1

    • target>=nums[mid],则代表中间项小于target,第一个比target大的值必定在该项之后,因此我们将left置为mid+1

    上述代码在循环结束后,sub为数组nums中第一个比target的值的下标。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2021-11-18 11:25:30  更:2021-11-18 11:26:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 11:43:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码