二分查找(Binary Search) 常见问题解决方法总结

2023-11-11

缘由

今天浏览 何登成的技术博客  无意中发现了写的blog,二分查找(Binary Search)需要注意的问题,以及在数据库内核中的实现

随想总结下二分查找的常见问题。

问题背景

今年的实习生招聘考试,我出了一道二分查找(Binary Search)的题目。题目大意如下:

给定一个升序排列的自然数数组,数组中包含重复数字,例如:[1,2,2,3,4,4,4,5,6,7,7]。

问题:给定任意自然数,对数组进行二分查找,返回数组正确的位置,给出函数实现。

注:连续相同的数字,返回第一个匹配位置还是最后一个匹配位置,由函数传入参数决定。

 在这片博客中作者也详细说明了,二分查找的重要性,比如在数据库的内核实现中,二分查找是一个非常重要的逻辑,几乎99%以上的

SQL语句(所有索引上的范围扫描/等值查询/Unique查询等),都会使用到二分查找进行数据的定位。 可以看到二分查找在现实的重要性。

二分查找算法的思想

二分查找法主要是解决在“一堆数中找出指定的数”这类问题。

而想要应用二分查找法,这“一堆数”必须有一下特征:

  • 存储在数组中
  • 有序排列
所以如果是用 链表存储的,就无法在其上应用 二分查找法了。

其实二分查找算法的思想很简单,在《编程珠玑》一书中的描述:

 在一个包含x的数组内,二分查找通过对范围的跟综来解决问题。开始时,范围就是整个数组。通过将范围中间的元素

与x比较并丢弃一半范围,范围就被缩小。这个过程一直持续,直到在x被发现,或者那个能够包含t的范围已成为空。

 Donald Knuth在他的《Sorting and Searching》一书中指出,尽管第一个二分查找算法早在1946年就被发表,但第一个

没有bug的二分查找算法却是在12年后才被发表出来。

注意中间值下标的计算,如果写成(low+high)/2,low+high可能会溢出,从而导致数组访问出错。改进的方法是将计算方式

写成如下形式:low+ ( (high-low) >>1)。

常见问题解决

何登成的技术博客中的问题四 “如何查找第一个/最后一个等值”,这个大牛只是简单的说明了下,并没有详细说明怎么解决

这个问题,下面来探讨 怎么解决 当所给有序重复数组 中查找 某个值出现的 第一个 和最后一个位置。

主要是下面三个问题:

1)二分查找元素x的下标,如无 return  -1

2)二分查找返回x(可能有重复)第一次出现的下标,如无return  -1

3)二分查找返回x(可能有重复)最后一次出现的下标,如无return  -1

对于问题1 我们只需要 利用最原始的的二分查找即可。

代码如下:

/* 
bin_search 二分查找元素x的下标,如无 return -1
low,high 分别为待查元素的区间的 上下界(包含边界).
x为待查元素.
注意 low <= high 
*/
int bin_search(int *a, int low, int high, int x)
{
	if(NULL == a || low > high || low <0)
		return -1;

	int mid;
	while(low<=high)//注意是<=,若是<会找不到边界值情况
	{
		mid = low + ((high-low)>>1);
		if(x<a[mid])
			high = mid-1;
		else if(x>a[mid])
			low = mid +1;
		else
			return mid;
	}
	return -1;
}

对于问题2,二分查找返回x(可能有重复 )第一次出现的下标,如无return  -1。

我们只需找到x重复出现情况下的第一次出现的下标。则我们只需用a[mid]和元素x进行比较,当a[mid]<x时

此时待查元素肯定在待查区间的右半部分 显然此时 不包括 mid 所以有 low = mid+1, 若a[mid]>=x时, 因为我

们查找的是x第一次出现的位置,我们不关心x最后出现的位置,所以此时high下标为mid,直到 low == high 终止

循环,并且比较a[low]是否为x,若是则 找到。

总的思路是:

把有序序列分成2个序列:[first,mid][mid+1,last) 当 a[mid]<x 时 使用 使用序列[mid + 1,last)

a[mid]>=x 时 使用序列[first,mid]。

还是看代码吧。

/* 
binS_first二分查找返回x(可能有重复)第一次出现的下标,如无return -1
low,high 分别为待查元素的区间的 上下界(包含边界).
//分成2个序列:[first,mid][mid+1,last)
  x为待查元素.注意 循环结束条件,low == high */
int binS_first(int *a, int low, int high, int x)
{
    if(NULL == a || low > high || low <0) return -1;
    int mid;
    while(low<high)
    {
        mid=low+((high-low)>>1);//计算中点
        if(a[mid]<x)// <x ,调整起点或者终点
            low=mid+1;
        else // >=x
            high=mid;
    }
    if(a[low] == x)
        return low;
    return -1;
}

 
对于问题3,二分查找返回x(可能有重复)最后一次出现的下标,如无return  -1 

其实和问题2的思路差不多。

只是在 while中我们假定 low+1 < high,否则在只有两个或者一个元素时 我们只需在while循环之外判断即可。

接下来的while 情况和问题2等价。我们现在关心的是 x(可能有重复)最后一次出现的下标,所以现在我们不关心他

第一次出现下标的位置, 当 a[mid]<=x 时 low = mid, 否则 a[mid] >x 此时 high = mid -1. 代码如下:

/* 
binS_last二分查找返回x(可能有重复)最后一次出现的下标,如无return -1
low,high 分别为待查元素的区间的 上下界(包含边界).
x为待查元素.
注意 循环结束条件,low+1 == high 
*/
int binS_last(int *a, int low, int high, int x)  
{  
	if(NULL == a || low > high || low < 0 )
		return -1;
	int mid;    
	while(low+1<high)//**   
	{  
		mid=low+((high-low)>>1);  
		if(a[mid]<=x)  // <=x
			low = mid;  
		else  // >x
			high=mid-1;  
	}  
	if(a[high] == x)//先判断high
		return high;
	else if(a[low] == x)
		return low;
	return -1;
}  

查找重复元素出现的第一次 最后一次位置总结如下:

二分查找返回x(可能有重复)第一次(最后一次)出现的下标找最小的等号放>=x位置(high),找最大的等号放<=x的位置(low)。
其中a[mid]在和待查找元素x比较中带 = 的,在对low 或者high赋值时一定为 mid,其它情况(<或>)则为mid+(-)1.

总的测试程序。

#include <stdio.h>
/*
bin_search 二分查找元素x的下标,如无 return -1
low,high 分别为待查元素的区间的 上下界(包含边界).
x为待查元素.
注意 low <= high
*/
int bin_search(int *a, int low, int high, int x)
{
    if(NULL == a || low > high || low < 0 )
        return -1;

    int mid;
    while(low<=high)//注意是<=,若是<会找不到边界值情况
    {
        mid = low + ((high-low)>>1);
        if(x<a[mid])
            high = mid-1;
        else if(x>a[mid])
            low = mid +1;
        else
            return mid;
    }
    return -1;
}
/*
binS_first二分查找返回x(可能有重复)第一次出现的下标,如无return -1
low,high 分别为待查元素的区间的 上下界(包含边界).
//分成2个序列:[first,mid][mid+1,last)
x为待查元素.
注意 循环结束条件,low == high */
int binS_first(int *a, int low, int high, int x)
{
    if(NULL == a || low > high || low < 0 )return -1;
    int mid;
    while(low<high)//<
    {
        mid=low+((high-low)>>1);
        if(a[mid]<x)// <x
            low=mid+1;
        else // >=x
            high=mid;
    }
    if(a[low] == x)
        return low;
    return -1;
}
/*
binS_last二分查找返回x(可能有重复)最后一次出现的下标,
如无return -1
low,high 分别为待查元素的区间的 上下界(包含边界).
x为待查元素.注意 循环结束条件,low+1 == high */
int binS_last(int *a, int low, int high, int x)
{
    if(NULL == a || low > high || low < 0)
        return -1;
    int mid;
    while(low+1<high)//**
    {
        mid=low+((high-low)>>1);
        if(a[mid]<=x) // <=x
            low = mid;
        else // >x
            high=mid-1;
    }
    if(a[high] == x)//先判断high
        return high;
    else if(a[low] ==x)return low;
    return -1;
}
int main()
{
    int a[]= {-1,1,2,2,2,4,4,4,4,4,4,4}; //0-11
    printf("-1: %d\n", bin_search(a, 0, 11, -1));
    printf(" 4 fisrt: %d\n", binS_first(a, 0, 11, 4));
    printf(" 4 last: %d\n", binS_last(a, 0, 11, 4));
    printf("\n");
    int b[]= {-2,-2,0,5,5,7,7}; //0-6
    printf("-2 fisrt: %d\n", binS_first(b, 0, 6, -2));
    printf("-2 last: %d\n", binS_last(b, 0, 6, -2));
    printf(" 5 fisrt: %d\n", binS_first(b, 0, 6, 5));
    printf(" 5 last: %d\n", binS_last(b, 0, 6, 5));
    return 0;
}

运行结果截图:

                    

此外对于像,二分查找返回刚好小于x的元素下标,二分查找返回刚好大于x的元素下标, 返回有序数列某一个元素重复出现的次数等问题,可以根据上面

的寻找重复元素出现第一次 最后一次位置的方法 进行 问题求解。对于这类问题也可以参考 STL 中关于 lower_bound与upper_bound的实现。STL算法库

中已经有相关实现。

参考:

http://hedengcheng.com/?p=595

编程珠玑

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

二分查找(Binary Search) 常见问题解决方法总结 的相关文章

  • 拓数派加入 OpenCloudOS 操作系统开源社区,作为成员单位参与社区共建

    近日 拓数派签署 CLA Contributor License Agreement 贡献者许可协议 正式加入 OpenCloudOS 操作系统开源社区 拓数派 英文名称 OpenPie 是国内基础数据计算领域的高科技创新企业 作为国内云上
  • 【计算机开题报告】智能社区管理系统

    一 设计目的及意义 随着经济的发展 人们生活水平的提高 工作和日常事务繁忙 人们对服务就有了更深入 更精细的要求 而计算机技术的迅猛发展 使得这种需求变为可能 传统的社区服务业也与互联网技术结合更加密切 这是社会发展的必然趋势 为解决社区中
  • 【计算机开题报告】 医药信息管理系统

    一 选题依据 简述国内外研究现状 生产需求状况 说明选题目的 意义 列出主要参考文献 1 研究背景 随着医药事业的不断壮大 相关单位对于医药信息的管理变得越来越重要 传统的手工管理效率低 易出错 费时费力 不能及时精确的收集 传递 存储 加
  • Kali Linux 安全渗透核心总结,444页核心知识点

    就像IT人离不开Linux系统一样 网安人也离不开Kali Linux 作为攻击性防御和渗透测试的代名词 越来越多的人开始学习Kali 如果你也对kali感兴趣 又想深入了解这方面内容 不妨收藏一下这份Kali Linux安全渗透教程 共4
  • SQL 解析与执行流程

    一 前言 在先前的技术博客中 我们已经详细介绍过数据库的 parser 模块与执行流程 用户输入的 SQL 语句通过词法解析器生成 token 再通过语法分析器生成抽象语法树 AST 经过 AST 生成对应的 planNode 最后执行 p
  • 拼多多详情API开启运营比价新纪元

    随着互联网的快速发展 电商行业正在迅速崛起 拼多多作为一家新兴的电商平台 凭借其独特的营销策略和创新的商业模式 成为了电商行业的一匹黑马 在拼多多的成功背后 其详情API接口营销起到了至关重要的作用 本文将详细介绍拼多多详情API接口营销的
  • 【计算机毕业设计】病房管理系统

    当下 如果还依然使用纸质文档来记录并且管理相关信息 可能会出现很多问题 比如原始文件的丢失 因为采用纸质文档 很容易受潮或者怕火 不容易备份 需要花费大量的人员和资金来管理用纸质文档存储的信息 最重要的是数据出现问题寻找起来很麻烦 并且修改
  • 【计算机毕业设计】个人日常事务管理系统

    进入21世纪网络和计算机得到了飞速发展 并和生活进行了紧密的结合 目前 网络的运行速度以达到了千兆 覆盖范围更是深入到生活中的角角落落 这就促使 管理系统的发展 管理系统可以实现远程处理事务 远程工作信息和随时追踪工作的状态 网上管理系统给
  • 【计算机毕业设计】航空信息管理系统

    传统信息的管理大部分依赖于管理人员的手工登记与管理 然而 随着近些年信息技术的迅猛发展 让许多比较老套的信息管理模式进行了更新迭代 飞机票信息因为其管理内容繁杂 管理数量繁多导致手工进行处理不能满足广大用户的需求 因此就应运而生出相应的航空
  • 38条Web测试经验分享

    1 页面链接检查 每一个链接是否都有对应的页面 并且页面之间切换正确 可以使用一些工具 如LinkBotPro File AIDCS HTML Link Validater Xenu等工具 LinkBotPro不支持中文 中文字符显示为乱码
  • 图解python | 字符串及操作

    1 Python元组 Python的元组与列表类似 不同之处在于元组的元素不能修改 元组使用小括号 列表使用方括号 元组创建很简单 只需要在括号中添加元素 并使用逗号隔开即可 tup1 ByteDance ShowMeAI 1997 202
  • 软件测试|SQLAlchemy环境安装与基础使用

    简介 SQLAlchemy 是一个强大的 Python 库 用于与关系型数据库进行交互 它提供了高度抽象的对象关系映射 ORM 工具 允许使用 Python 对象来操作数据库 而不必编写原生SQL查询 本文将介绍如何安装 SQLAlchem
  • 电商数据api接口商品评论接口接入代码演示案例

    电商数据API接口商品评论 接口接入入口 提高用户体验 通过获取用户对商品的评论 商家可以了解用户对商品的满意度和需求 从而优化商品和服务 提高用户体验 提升销售业绩 用户在购买商品前通常会查看其他用户的评论 以了解商品的实际效果和质量 商
  • 【计算机毕业设计】微信小程序反诈科普平台

    相比于以前的传统手工管理方式 智能化的管理方式可以大幅降低反诈科普平台的运营人员成本 实现了反诈科普平台的标准化 制度化 程序化的管理 有效地防止了反诈科普平台的随意管理 提高了信息的处理速度和精确度 能够及时 准确地查询和修正反诈科普 一
  • 【计算机毕业设计】宝鸡文理学院学生成绩动态追踪系统

    研究开发宝鸡文理学院学生成绩动态追踪系统的目的是让使用者可以更方便的将人 设备和场景更立体的连接在一起 能让用户以更科幻的方式使用产品 体验高科技时代带给人们的方便 同时也能让用户体会到与以往常规产品不同的体验风格 与安卓 iOS相比较起来
  • 做测试不会 SQL?超详细的 SQL 查询语法教程来啦!

    前言 作为一名测试工程师 工作中在对测试结果进行数据比对的时候 或多或少要和数据库打交道的 要和数据库打交道 那么一些常用的sql查询语法必须要掌握 最近有部分做测试小伙伴表示sql查询不太会 问我有没有sql查询语法这一块的文档可以学习
  • 面试官问,如何在十亿级别用户中检查用户名是否存在?

    面试官问 如何在十亿级别用户中检查用户名是否存在 前言 不知道大家有没有留意过 在使用一些app注册的时候 提示你用户名已经被占用了 需要更换一个 这是如何实现的呢 你可能想这不是很简单吗 去数据库里查一下有没有不就行了吗 那么假如用户数量
  • Mysql中设置只允许指定ip能连接访问(可视化工具的方式)

    场景 Mysql中怎样设置指定ip远程访问连接 Mysql中怎样设置指定ip远程访问连接 navicat for mysql 设置只有某个ip可以远程链接 CSDN博客 前面设置root账户指定ip能连接访问是通过命令行的方式 如果通过可视
  • 温室气体排放更敏感的模型(即更高的平衡气候敏感性(ECS))在数年到数十年时间尺度上也具有更高的温度变化(Python代码实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Python代码 数据
  • 光波导结构

    摘要 增强现实和混合现实 AR MR 领域的新应用引起了人们对带有光栅区域的光波导系统的越来越多的关注 这些光波导系统用于输入和输出耦合以及扩瞳目的 VirtualLab Fusion为这类系统的仿真和设计提供了几个强大的工具 其中一个是具

随机推荐

  • Mybatis手动提交事务

    package com stylefeng guns modular system dao import java util List import java util Map import org apache ibatis annota
  • 找不到类型,或者不是编译时常数:RadioButtonGroup

    此类异常 都是由于我们要使用的组件包的路径 开发工具没给我们提供 一种做法是在组件面板中 ctrl F7 将需要使用的组件拖入到库中 或者拖到舞台后 删除便可以使用 另一种做法是在开发工具中 在 编辑 gt 首选参数 中 进行ActionS
  • Vue3封装函数式组件

    MyDialog vue
  • CSS中margin属性详解

    margin属性概述 margin是CSS层叠样式表中用来规定围绕在元素边框周围空白区域范围的属性 该接受任何长度单位 可以是像素 英寸 毫米或 em 相关属性 margin 可以单独改变元素的上 下 左 右边距 也可以一次改变所有的属性
  • qt.qpa.plugin: Could not load the Qt platform plugin “xcb“ in ““ even though it was found.(解决办法)

    一 报错信息 环境 ubuntu16 04 报错 在以安装pyqt5的情况下 qt qpa plugin Could not load the Qt platform plugin xcb in even though it was fou
  • 【译】用 Rust 实现 csv 解析-part4

    Rust and CSV parsing 译文 用 Rust 实现 csv 解析 part4 原文链接 https blog burntsushi net csv 原文作者 BurntSushi 译文来自 https github com
  • 计操理论课04 -- openEuler实验第三章进程管理

    文章目录 任务1 创建并运行内核线程 任务要求 任务代码 任务截图 任务2 打印输出当前系统 CPU 负载情况 任务要求 任务代码 任务截图 任务3 打印输出当前处于运行状态的进程的 PID 和名字 任务要求 任务代码 任务截图 任务4 使
  • 区块链基本概念学习笔记

    文章目录 区块链产生与发展历史 区块链的场景属性 区块链定义 区块链的特点 区块链加密货币的特点 区块链核心技术 区块链的核心概念 区块链分类 区块链架构特点 区块链产生与发展历史 区块链的场景属性 区块链定义 区块链是一种点对点传输协议
  • 交叉编译并移植Android工具adb与adbd过程

    Android tool 移植adb与adbd的记录 近期研发一个新功能 需要用到Android的adbd服务 如是尝试着交叉编译adbd 由于目前的使用场景是PC端通过usb连接到开发板上 利用adb push pull 进行文件的传输
  • 一个浮点数跨平台产生的问题

    感谢网友唐磊 微博 唐磊 name 投稿 本文原文在唐磊的博客上 原文地址 原文分析还不够好 而且可能对人有误导 所以 我对原文做了很多修改 并加了Linux下的内容 浮点数是一个很复杂的事情 希望这篇文章有助于大家了解浮点数与其相关的C
  • LeetCode【129】求根到叶子节点数字之和

    题目 给定一个二叉树 它的每个结点都存放一个 0 9 的数字 每条从根到叶子节点的路径都代表一个数字 例如 从根到叶子节点路径 1 gt 2 gt 3 代表数字 123 计算从根到叶子节点生成的所有数字之和 说明 叶子节点是指没有子节点的节
  • 电子游戏,一个价值千亿美元的机会

    如果元宇宙确实是互联网的继承者 那么说它的 前辈 来自电子游戏行业似乎很奇怪 毕竟 到目前为止 互联网与电子游戏行业的发展轨迹是完全不同的 互联网起源于政府的研究实验室和大学 后来 它逐渐扩展到企业 然后是中小企业 最后才是消费者 娱乐业可
  • 数据库事务(Database Transaction)

    数据库事务 Database Transaction 数据库事务 Database Transaction 是由一系列对系统中数据进行访问与更新的操作所组成的一个程序执行逻辑单元 一个数据库事务通常包含了一个序列的对数据库的读 写操作 它的
  • 12个Flex常用功能代码

    12个Flex常用功能代码 1 复制内容到系统剪贴板 1 System setClipboard strContent 2 复制一个ArrayCollection 1 dummy solution well it works 2 var b
  • ubuntu下配置vim

    1 安装vim sudo apt get install vim full2 配置文件的位置在目录 etc vim下面 有个名为vimrc的文件 这是系统中公共的vim配置文件 对所有用户都有效 3 设置语法高亮显示1 打开vimrc 添加
  • DLNA介绍(包括UPnP,2011/6/20 更新)

    这部分的内容大多来源于网络及官方文档 按照自己的翻译理解整理所成 东西比较多 从头慢慢看还是可以懂个大概的 目录 一 DNLA的建立 二 DLNA的成员 三 DLNA标准的制定 四 DLNA的设备 五 DLNA的架构 六 云时代的数字家庭
  • Python中列表、字典、元组、集合数据结构整理

    这篇文章主要介绍了Python中列表 字典 元组 集合数据结构整理 较为详细的分析了这几类数据结构的具体用法及相关技巧 需要的朋友可以参考下 本文详细归纳整理了Python中列表 字典 元组 集合数据结构 分享给大家供大家参考 具体分析如下
  • 518. 零钱兑换 II -- 完全背包

    518 零钱兑换 II 这道题其实就是一个完全背包问题 关于背包问题的相关文章见 01背包问题 动态规划 完全背包问题 class CoinChange 完全背包 518 零钱兑换 II https leetcode cn problems
  • Android Automotive-sensor服务详解

    本章将会详细介绍Android原生车辆服务的传感器处理流程 同时还会介绍Mananger lt lt gt gt Service之间数据传输协议 即Manager如何与Service进行交互 Car Sensor数据传递时序 车辆控制之Se
  • 二分查找(Binary Search) 常见问题解决方法总结

    缘由 今天浏览 何登成的技术博客 无意中发现了写的blog 二分查找 Binary Search 需要注意的问题 以及在数据库内核中的实现 随想总结下二分查找的常见问题 问题背景 今年的实习生招聘考试 我出了一道二分查找 Binary Se