二分查找专题总结 - 基础篇

二分查找 - 基础篇

前言

从一个有序的数组中,找到某元素的值,通常思路就是二分查找。二分查找是一个常考的知识点。同时,它也是非常容易出错的一道面试题。左右指针的位置,取值,比较是大于还是大于等于。里面细节很多。死记硬背往往容易出错,只有真正理解思路和多多练习,才能掌握不出错的”二分算法“。

本篇文章是二分查找的入门篇。将会介绍最传统,最容易理解与书写的二分算法。并介绍四种二分查找的进阶问题。在理解本文的基础上,后续文章将会再分享二分的各种变形和其他模板。

原题:在有序数组中查找定值

思路很简单,利用数组有序的特性,每次将数组二分,拿中间元素和目标值比较。中间元素和目标值相等,直接返回下标索引。中间元素比目标值小,则去右区间继续二分查找,否则去左区间二分查找。

代码如下,并不复杂,但有几个需要注意的细节:

  1. 循环条件

    比较大小使用 <= 小于等于号

  2. 防止死循环

    更新low,high指针分别取值mid + 1 和 mid -1,注意这里的+1 和 -1,可以让我们不用考虑死循环以及左中点,右中点情况。假如我们使用high = mid,当low = high的时候,就有可能进入high = mid的分支逻辑中,导致无限死循环。

  3. 注意溢出

    在取中间值时,很多人常用mid = (left + right) / 2的形式,当left与right数值的加和较大时 ,是有可能溢出Int的取值范围的。可以采用mid = left + (right - left) / 2的形式,结果是相同的。在JDK1.8中,采用(low + high) >>> 1,>>>是无符号右移,高位自动补0,所以当low + high溢出时变成负数,无符号右移一位又变成了正数。

  public int bsearch(int[] a, int n, int value) {
        int low = 0;
        int high = n - 1;

        while (low <= high) {
            int mid = (low + high) >>> 1;
            if (a[mid] == value) {
                return mid;
            } else if (a[mid] < value) {
                low = mid + 1;
            } else {
                high = mid - 1;
            }
        }
        return -1;
    }

变形一:查找第一个等于给定值的元素

与原题区别在于,当a[mid] == value时,还需要确认是不是第一个值等于给定值的元素。

当遍历到数组第一个数,或者左边值不同时,必定是第一个,直接返回(此处同时做了溢出校验)

当不是第一个元素时,从右到左收缩区间,继续二分查找。

  /**
     * 变形一:
     * 存在重复元素,查找第一个等于给定值的元素
     */
    public int bsearch1(int[] a, int n, int value) {
        int low = 0;
        int high = n - 1;
        while (low <= high) {
            int mid = (low + high) >>> 1;
            if (a[mid] > value) {
                high = mid - 1;
            } else if (a[mid] < value) {
                low = mid + 1;
            } else {
                //此处区别在于,当a[mid]等于需要的值时,还需要确认是不是第一个值等于给定值的元素.当遍历到数组第一个数,或者左边值不同时,必定是第一个,直接返回
                if ((mid == 0) || (a[mid - 1] != value)) {
                    return mid;
                } else {
                    high = mid - 1;
                }
            }
        }
        return -1;
    }

变形二:查找最后一个等于给定值的元素

类比变形一,变形一查找的第一个,此处查找的最后一个元素。即需要确认是不是最后一个值等于给定值的元素。

当遍历到数组最后一位,或者右边元素值不等时,必定是最后一个,返回下标。

如果不是最后一个元素,从左到右收缩区间,继续二分查找。

   /**
     * 变形二:
     * 查找最后一个值等于给定值的元素
     */
    public int bsearch2(int[] a, int n, int value) {
        int low = 0;
        int high = n - 1;
        while (low <= high) {
            int mid = (low + high) >>> 1;
            if (a[mid] > value) {
                high = mid - 1;
            } else if (a[mid] < value) {
                low = mid + 1;
            } else {
                //此处区别在于,当a[mid]等于需要的值时,还需要确认是不是最后一个值等于给定值的元素.当遍历到数组最后一位,或者右边值不同时,符合要求直接返回
                if ((mid == n - 1) || (a[mid + 1] != value)) {
                    return mid;
                } else {
                    low = mid + 1;
                }
            }
        }
        return -1;
    }

变形三:查找第一个大于等于给定值的元素

在变形一基础上,将条件从查找一个等于定值的元素 改成 第一个大于等于定值的元素,增加了一个大于的判断条件。则代码如下:

  /**,
     * 变形三:
     * 查找第一个大于等于给定值的元素
     * 如序列:3,4,6,7,19 查找第一个大于5的元素,即为6
     * 第一个大于给定值,则说明上一个小于给定值,依次判断
     */
    public int bsearch3(int[] a, int n, int value) {
        int low = 0;
        int high = n - 1;
        while (low <= high) {
            int mid = (low + high) >>> 1;
            if (a[mid] >= value) {
                if ((mid == 0) || (a[mid - 1] < value)) {
                    return mid;
                } else {
                    high = mid - 1;
                }
            } else {
                low = mid + 1;
            }
        }
        return -1;
    }

变形四:查找最后一个小于等于给定值的元素

类比上面三题,此处不再赘述。

  /**
     * 变形四:
     * 查找最后一个小于等于给定值的元素、
     * 如:3,5,6,8,9,10 最后一个小于7的元素是6
     */
    public int bsearch4(int[] a, int n, int value) {
        int low = 0;
        int high = n - 1;
        while (low <= high) {
            int mid = (low + high) >>> 1;
            if (a[mid] > value) {
                high = mid - 1;
            } else {
                if ((mid == n - 1) || (a[mid + 1] > value)) {
                    return mid;
                } else {
                    low = mid + 1;
                }
            }
        }
        return -1;
    }

Git代码地址

原文地址:https://www.cnblogs.com/icecrea/p/12071007.html

时间: 2024-08-29 03:37:32

二分查找专题总结 - 基础篇的相关文章

Qt图像处理专题之基础篇

马上转行开始做图像处理的工作了,先学习一些基础的图像处理的知识,但是有时候想要验证一个简单的算法,还是想要能够直接的看到效果,所以想要编写一个软件框架了.关于图像处理方面的算法库很多了,其中比较流行的有OpenCV,这个会在另外一个专题里面涉及.而本人是长期从事Qt软件开发的,有些简单的工作还是更加喜欢在Qt中实现,并且在有些嵌入式应用程序开发中我们并不一定有足够的资源来移植一个庞大的OpenCV库,可能简单的几行C/C++代码就能够搞定的.出于这个原因,我打算写一个专题,自己来实现一些图像处理

5、查找——数据结构【基础篇】

查找 查找的基本概念 查找的目的是从给定的同一类型的数据集合中,找出人们所需要的数据元素(或记录) 线性表的查找--静态查找 在对查找表实施静态查找时,查找表的组织结构可以是顺序表结构,也可以是单链表结构 静态查找的方法: 顺序查找: 顺序查找是用待查找记录与查找表中的记录逐个比较,如果找到相等记录,则查找成功,否则查找失败 顺序查找对关键字的是否有序没有要求,但是查找效率低下 时间复杂度为O(n) 注意:当n较大时,不宜采用顺序查找 折半查找(二分查找) 折半查找的前提条件:查找表有序且顺序存

算法:二分查找(基础)

二分查找是一个基础的算法,也是面试中常考的一个知识点. 基础二分查找 二分查找就是将查找的键和子数组的中间键做比较,如果被查找的键小于中间键,就在左子数组继续查找:如果大于中间键,就在右子数组中查找,否则中间键就是要找的元素. /** * 二分查找,找到该值在数组中的下标,否则为-1 */ static int binarySerach(int[] array, int key) { int left = 0; // 左边开始位置的下标 int right = array.length - 1;

算法——基础篇——二分查找

     二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好:其缺点是要求待查表为有序表,且插入删除困难.因此,折半查找方法适用于不经常变动而查找频繁的有序列表.     首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功:否则利用中间位置记录将表分成前.后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表.重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功

个性二维码开源专题&lt;基础篇&gt;

二维码原理介绍: 二维码为什么是黑白相间的?黑色表示二进制的"1",白色表示二进制的"0" "我们之所以对二维码进行扫描能读出那么多信息,就是因为这些信息被编入了二维码之中."黄海平说,"制作二维码输入的信息可以分成三类,文本信息,比如名片信息:字符信息,比如网址.电话号码:还有图片信息,甚至还可以包括简短的视频."数据信息是怎么被编入的呢?信息输入后,首先要选择一种信息编码的码制.现在常见的二维码都是以QR码作为编码的码制.

十大基础实用算法之归并排序和二分查找

归并排序 归并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 算法步骤: 1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列 2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置 3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置 4. 重复步骤3直到某一指针达到序列尾 5. 将另一序列剩下的所有元素直接复制到合并序列尾 用分治策略解决问题分为三步:分解

Python 迭代器&amp;生成器,装饰器,递归,算法基础:二分查找、二维数组转换,正则表达式,作业:计算器开发

本节大纲 迭代器&生成器 装饰器  基本装饰器 多参数装饰器 递归 算法基础:二分查找.二维数组转换 正则表达式 常用模块学习 作业:计算器开发 实现加减乘除及拓号优先级解析 用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式后,必须自己解析里面的(),+,-,*,/符号和公式,运算后得出结果,结果必须与真实的计算器所得出的结果一致 迭代器&

二分查找之新手篇——函数使用

首先介绍c++万能头文件 #include<bits/stdc++.h> using namespace std;(比较适合偷懒,但是不能确保不会出错)(用还是可以的,粗错了再回头来改嘛) 接下来介绍一下两个二分查找函数: upper_bound()与lower_bound(): ForwardIter lower_bound(ForwardIter first, ForwardIter last,const _Tp& val)算法(函数)返回一个非递减序列[first, last)中

分治法(实战篇之二分查找)

啥也不说,先上题!!! 二分查找 Description 给你n个整数和一个整数k.问:n个数中是否存在整数k,如果存在,输出“Yes”,否则输出“No”. Input 输入包含多组测试用例,对于每组测试用例: 输入一个数字n (1 <= n <= 10^7) 接下来输入n个不同的数字Ai( |Ai| <= 10^7). 接下来输入整数k ( |k| <= 10^7).代表要找的数字k Output n个数中是否存在整数k,如果存在输出“Yes”,否则输出“No”.每组数据占一行.