【基础算法】二分查找的多种写法

前言

在算法竞赛中,二分查找使用的频率是非常高的,对于C++选手而言,有STL中自带的lower_bound和upper_bound二分查找,可以很方便的进行二分查找。但是非C++选手、或者需要自定义多条件查找的情况需要自己写一个二分,本文对几种常见的二分查找写法进行讲解。

本文使用的编程语言为Java,但代码比较好理解其他语言选手也可以查看本文来学习二分相关内容。


二分查找

二分查找,也称折半查找,是一种可以在有序序列上进行快速查找的算法,时间复杂度是O(logn)

常见的二分查找类型,有以下两种:

  • 查找目标值在序列中不小于目标值的第一个元素(同lower_bound,以此查到第一个位置)。

  • 查找目标值在序列中第一个大于目标值的元素(同upper_bound,以此查找到最后一个出现的位置)。

在设置区间的时候,根据写法的不同分为下面几种:

  • 左闭右闭。

  • 左闭右开。

根据最终获取答案的写法,又分为下面两种:

  • 在左右指针处取值。

  • 设置额外变量取值。

本文将分不同情况,写出不同情况下的二分查找模板,帮助大家理解学习。


查找目标值在序列中不小于目标值的第一个元素

也就是实现一个STL中的lower_bound。

左闭右闭版

java 复制代码
/**
 * [l,r]左闭右闭区间 lower_bound
 * @param nums 递增序列
 * @param target 目标值
 * @return 返回不小于目标值的第一个元素下标
 */
int lowBs(int[] nums,int target){
    int l=0;
    int r=nums.length-1;
    while(l<=r){
        int mid=l+(r-l>>1);
        if(nums[mid]<target){
            l=mid+1;
        }else{
            r=mid-1;
        }
    }
    return l;
}

左闭右开版

java 复制代码
/**
 * [l,r)左闭右闭区间 lower_bound
 * @param nums 递增序列
 * @param target 目标值
 * @return 返回不小于目标值的第一个元素下标
 */
int lowBs(int[] nums,int target){
    int l=0;
    int r=nums.length;
    while(l<r){
        int mid=l+(r-l>>1);
        if(nums[mid]<target){
            l=mid+1;
        }else{
            r=mid;
        }
    }
    return l;
}

查找目标值在序列中第一个大于目标值的元素

也就是实现一个STL中的upper_bound。

左闭右闭版

java 复制代码
/**
 *  [l,r]左闭右闭 upper_bound
 * @param arr 递增序列
 * @param target 目标值
 * @return 返回第一个大于目标值的位置
 */
int highBs(int[] arr,int target){
    int l=0;
    int r=arr.length - 1;
    while(l<=r){
        int mid=l+(r-l>>1);
        if(arr[mid]<=target){
            l=mid+1;
        }else{
            r=mid-1;
        }
    }
    return l;
}

左闭右开版

java 复制代码
/**
 *  [l,r)左闭右开 upper_bound
 * @param arr 递增序列
 * @param target 目标值
 * @return 返回第一个大于目标值的位置
 */
int highBs(int[] arr,int target){
    int l=0;
    int r=arr.length;
    while(l<r){
        int mid=l+(r-l>>1);
        if(arr[mid]<=target){
            l=mid+1;
        }else{
            r=mid;
        }
    }
    return l;
}

易错问题和常见问题总结

一、需要注意区间定义和终止条件是否匹配正确

  • 左闭右闭:初始化 r = nums.length-1,终止条件 while (l <= r)

  • 左闭右开:初始化 r = nums.length,终止条件 while (l < r)

二、指针更新是否正确

  • 左闭右闭:r = mid - 1l = mid + 1

  • 左闭右开:r = mid

三、运算符优先级

应写为:int mid = l + ( r - l >> 1)

?为什么不能写为 l + r >> 1

如果可以保证l+r不会溢出的话,其实这样写也可以,但是为了保证不出现数据溢出,更加推荐写上面的形式。

相关推荐
shandianchengzi2 小时前
【小白向】错位排列|图文解释公考常见题目错位排列的递推式Dn=(n-1)(Dn-2+Dn-1)推导方式
笔记·算法·公考·递推·排列·考公
I_LPL2 小时前
day26 代码随想录算法训练营 回溯专题5
算法·回溯·hot100·求职面试·n皇后·解数独
Yeats_Liao2 小时前
评估体系构建:基于自动化指标与人工打分的双重验证
运维·人工智能·深度学习·算法·机器学习·自动化
怒放吧德德2 小时前
后端 Mock 实战:Spring Boot 3 实现入站 & 出站接口模拟
java·后端·设计
only-qi2 小时前
leetcode19. 删除链表的倒数第N个节点
数据结构·链表
cpp_25012 小时前
P9586 「MXOI Round 2」游戏
数据结构·c++·算法·题解·洛谷
浅念-2 小时前
C语言编译与链接全流程:从源码到可执行程序的幕后之旅
c语言·开发语言·数据结构·经验分享·笔记·学习·算法
biyezuopinvip2 小时前
基于Spring Boot的企业网盘的设计与实现(任务书)
java·spring boot·后端·vue·ssm·任务书·企业网盘的设计与实现
脸大是真的好~2 小时前
EasyExcel的使用
java·excel
小宋10212 小时前
Java 项目结构 vs Python 项目结构:如何快速搭一个可跑项目
java·开发语言·python