当前位置: 首页 > news >正文

【C++】bsearch函数的使用及二分法查找介绍

写程序的时候,肯定避免不了需要从集合中找到符合条件的元素,一般情况下,最简单也最常用的就是循环遍历元素,这种方法虽然写的简单,但是小数据量还行,但是数据过大的话,这样效率就低了。循环的时候,比如你要的数据正好在集合的最后,那就需要把前面的每一个元素都要对比一次,如果你要查找的数据,正好在前几个,那就很快找到到了。但数据这东西毕竟不是可控的。

所以,要查找,我们就要采用一点技巧和方法,在C/C++中,搜索的时候,可以使用bsearch函数来实现元素的查找,这个函数定义在头文件stdlib.h中,使用方式如下:

本文所有代码在 cygwin,gcc 11.3,gdb 11.2 中编译调试

bsearch的基本使用方法

bsearch函数的使用方法简单,函数定义如下

void* bsearch( const void *key, const void *ptr, size_t count, size_t size,int (*comp)(const void*, const void*) );

具体参数如下

key - 指向要查找的元素的指针 
ptr - 指向要检验的数组的指针 
count - 数组的元素数目 
size - 数组每个元素的字节数 
comp - 比较函数。若首个参数小于第二个,则返回负整数值,若首个参数大于第二个,则返回正整数值,若两参数等价,则返回零。 将 key 传给首个参数,数组中的元素传给第二个。 

comp函数的定义如下

int cmp(const void *a, const void *b);

cmp参数说明:a表示要查找的那个元素,b表示查找数组里的元素

举例如下,从数组f中查找x,如果找到就打印“找到11”,没找到就打印"没找到11"

#include <stdio.h>
#include <stdlib.h>  
const int x = 11;
const int f[] = { 1,3,6,9,11 };
int cmp(const void *a, const void *b) {int x1 = (int)(*(int*)a);int x2 = (int)(*(int*)b);printf("x1:%d\n", x1);printf("x2:%d\n", x2);if (x1 < x2) return -1;else if (x1 > x2) return 1;else return 0;
}
int main(int, char**) {int* c = (int*)bsearch(&x, f, 5, sizeof (int), cmp);if( c != NULL ) {printf("找到%d\n",x);}else {printf("没找到%d\n",x);}return 1;
}

输出如下

x1:11
x2:6
x1:11
x2:11
找到 11

可以看到,通过bsearch函数,从f中找到了11

但是,观察cmp函数中的两行printf代码

printf("x1:%d\n", x1);
printf("x2:%d\n", x2);

它表示打印要查找的元素,也就是上例的x,x2表示被查找的f中的某个元素,通过观察输出的结果,可以发现,虽然f数组中有{ 1,3,6,9,11 }5个元素,但实际上,比较函数并没有比较全部元素,只比较的两次就找到了11.

这就是文章开头说到的,循环遍历会降低效率,而使用bsearch会让查找速度加快,应为bsearch的查找并非遍历元素,而是采用了二分法查找,bsearch的第一个字幕b代表的就是Bisection/baɪˈsekʃən/,中文意思为“二等分"

关于二分法查找

在查找上使用二分法,是通过多次切分被查找的数组,然后和要查找的元素组做对比,具体步骤如下

假设数组为arr,要查找的元素为key,数组长度为len,进行如下操作

1.把被查找的数组长度除以2,得到的中间值mid,作为索引,去数组中取值arr[mid]

2.使用比对函数进行对比,第一步取到的arr[mid]和要查找的值key如果,返回的值小于0,也就说明要查找的值key,在数组的索引为0到mid中间。然后,修改要查找的数组起始位置,重复第一步,在0到mid中间查找

如果返回的值大于0,就说明要查找的值key,在数组的索引为mid到数组的最后一个元素之间。然后修改要查找的数组起始位置,重复第一步,在arr[mid]到最后个元素中间查找

3.重复如上2,3步,然后最终找到你要找的值

4.如果返回返回0,说明找到了,可以返回了

例如

从[1,2,3,4,5]中,找到4

  1. 5/2 = 2,先找到索引是2的3
  2. 因为3比4小,说明4在索引2以后
  3. (2+1+5)/2 = 4, 索引4就是5 ,比4大所以,在所以在索引4以前,3以后
  4. (3+4)/2 = 3,索引3就是4,找到了

以上就是使用二分法的流程

自己实现一个bsearch

void * mybsearch(const void* base, int len, int size, const void* key, int (*cmp)(const void* a, const void* b))
{size_t low = 0, high = len;while (low < high){int mid = (low + high) / 2;void * p = (void *) (((const char *) base) + (mid * size));int comret  = (*cmp) (key,p);if (comret < 0)high = mid;else if (comret > 0)low = mid + 1;elsereturn p;}return NULL;
}

然后还是刚才的代码

#include <stdio.h>
#include <stdlib.h>  
const int x = 11;
const int f[] = { 1,3,6,9,11 };
int cmp(const void *a, const void *b) {int x1 = (int)(*(int*)a);int x2 = (int)(*(int*)b);printf("x1:%d\n", x1);printf("x2:%d\n", x2);if (x1 < x2) return -1;else if (x1 > x2) return 1;else return 0;
}
int main(int, char**) {int* c = (int*)mybsearch(f,5, sizeof (int), &x, cmp);if( c != NULL ) {printf("找到%d\n",x);}else {printf("没找到%d\n",x);}return 1;
}

执行一下,看结果

x1:11
x2:6
x1:11
x2:11
找到 11

运行结果正确

知道的二分法的原理,就知道bsearch该如何使用,什么情况下,bsearch就找不到你要的元素

例如,如果把要查找的数组和要查找的元素修改为

const int x = 2;
const int f[] = { 1,3,6,9,2 };

的话,bsearch就无法从f中找到2,因为你的数组并不是单调的,按照二分法的查找方式,就找不到
在这里插入图片描述

所以,使用bsearch时候,如果不能确认数组是排序好的,就要先调用qsort把数组排序好,然后在使用查找

在这里插入图片描述
如上例,经过qsort以后,数据会排序好,然后就能正常使用bsearch了
在这里插入图片描述

相关文章:

【C++】bsearch函数的使用及二分法查找介绍

写程序的时候&#xff0c;肯定避免不了需要从集合中找到符合条件的元素&#xff0c;一般情况下&#xff0c;最简单也最常用的就是循环遍历元素&#xff0c;这种方法虽然写的简单&#xff0c;但是小数据量还行&#xff0c;但是数据过大的话&#xff0c;这样效率就低了。循环的时…...

分布式系统中的补偿机制设计问题

我们知道&#xff0c;应用系统在分布式的情况下&#xff0c;在通信时会有着一个显著的问题&#xff0c;即一个业务流程往往需要组合一组服务&#xff0c;且单单一次通信可能会经过 DNS 服务&#xff0c;网卡、交换机、路由器、负载均衡等设备&#xff0c;而这些服务于设备都不一…...

类成员的方法

初识对象 生活中或是程序中&#xff0c;我们都可以使用设计表格、生产表格、填写表格的形式组织数据进行对比&#xff0c;在程序中&#xff1a; 设计表格&#xff0c;称之为&#xff1a;设计类&#xff08;class&#xff09; 打印表格&#xff0c;称之为&#xff1a;创建对象 …...

华为OD机试真题Python实现【端口合并】真题+解题思路+代码(20222023)

端口合并 题目 有M(1<=M<=10)个端口组, 每个端口组是长度为N(1<=N<=100)的整数数组, 如果端口组间存在 2 个及以上不同端口相同, 则认为这 2 个端口组互相关联,可以合并 第一行输入端口组个数 M,再输入 M 行,每行逗号分隔,代表端口组。 输出合并后的端口组…...

自考本科计算机网络原理(04741)历年大题真题【18年10月-22年10月】

文章目录一、简答题&#xff08;历年真题&#xff09;18年10月-22年10月历年简答题出题情况分析2018年10月2019年4月2019年10月2020年8月2020年10月2021年4月2021年10月2022年4月2022年10月二、综合题&#xff08;历年真题&#xff09;2018年10月2019年4月2019年10月2020年8月2…...

计算机SCI期刊投稿,除了投稿信,还要做什么准备? - 易智编译EaseEditing

投稿信的准备 期刊的编辑往往需要一些有关作者及其论文的信息。 而作者也希望给编辑提供一些有助于其全文送审及决策的信息。 这些信息都应该包括在投稿信中。 投稿信应包括以下几方面的内容&#xff1a; 文题和所有作者的姓名;稿件适宜的栏目; 为什么此论文适合于在该刊而…...

Allegro如何刷新封装和库里的封装同步操作指导

Allegro如何刷新封装和库里的封装同步操作指导 在做PCB设计的过程中,有时会因为库里的封装有更新,所以PCB上使用到了这个封装时候需要和库里的同步,如下图 如何刷新,具体操作如下 点击Place点击Update Symbols...

基于Vue3手写选课组件(含时区切换,拖拽选择)

环境说明 基于vue3vite 无关联别的ui框架&#xff0c;组件化 初次使用vue3&#xff0c;技术菜&#xff0c;大佬勿喷 main.ts "moment": "^2.29.4","moment-timezone": "^0.5.41",import moment from moment; import momentTz from &…...

准备好了吗?加入 GDE 成长计划,成为下一位谷歌开发者专家!

谷歌开发者专家 (Google Developer Experts&#xff0c;GDE)&#xff0c;又称谷歌开发者专家项目&#xff0c;是由一群经验丰富的技术专家、具有社交影响力的开发者和思想领袖组成的全球性社区。通过在各项活动演讲以及各个平台上发布优质内容来积极助力开发者、企业和技术社区…...

搭建帮助中心的 8 个最佳工具

网站帮助中心的作用通过向客户表明您了解他们所面临的问题以及如何提供帮助来建立信任&#xff1b;通过回答常见问题来改善客户服务&#xff0c;增强专业的品牌形象&#xff1b;通过减少重复发送给支持人员的电话和电子邮件&#xff0c;节省时间和金钱&#xff1b;增强您在搜索…...

LQB小板焊接V3版本的小板原理图,PCB图,注意事项和步骤

第一部分&#xff0c;这个部分&#xff0c;可以不焊接&#xff0c;直接用买的下载器进行下载代码&#xff0c;外接一个下载器&#xff0c;网上大概是10元左右&#xff0c;以后学习stm32的芯片的时候&#xff0c;这个下载器就是一个串口转换器&#xff0c;也可以使用。。 当然也…...

华为OD机试真题Python实现【翻转单词顺序】真题+解题思路+代码(20222023)

翻转单词顺序 题目 输入一个英文文章片段 翻转指定区间的单词顺序,标点符号和普通字母一样处理 例如输入字符串 I am a developer. 区间[0,3]则输出 developer. a am I 🔥🔥🔥🔥🔥👉👉👉👉👉👉 华为OD机试(Python)真题目录汇总 ## 输入 使用换行隔…...

微机原理和计算机组成原理复习

1&#xff1a;冯诺依曼机器的主要特点&#xff1f; 1&#xff09;计算机由运算器、存储器、控制器、输入设备和输出设备五大部分组成&#xff1b; 2&#xff09;指令和数据存储在存储器中&#xff0c;并可以按地址访问&#xff1b; 3&#xff09;指令和数据均以二进制表示&…...

mysql5.7.33安装配置教程【保姆级安装教程】

MySQL5.7.33安装教程 1、官方网站下载 点击这里跳转页面下载 1.1、看下你是什么系统&#xff0c;系统是64位还是32位 2、解压到D盘跟路径或者其下面纯英文路径 2.1、可见它没有data、log等文件夹&#xff0c;不需手动添加(下面执行命令自动初始化)&#xff01;&#xff01; …...

每天都和时间序列打交道,我总结了这篇文章!

Datawhale干货 作者&#xff1a;戳戳龍&#xff0c;上海交通大学&#xff0c;量化算法工程师前言&#x1f534; 平时工作中每天都在和时间序列打交道&#xff0c;对时间序列分析进行研究是有必要的&#x1f7e1; 分享和交流一些自己的在时序处理方面的心得&#xff0c;提供一…...

【Leetcode——重排链表】

文章目录一、重排链表思路1.思路2.总结一、重排链表 对于这道题&#xff0c;有两种思路&#xff1a; 思路1. 1.使用一个线性表&#xff0c;存储链表中的每个节点&#xff0c;然后按照题目的条件&#xff0c;来链接线性表的各个节点即可。 使用左下标和右下标来定位线性表中的…...

HCIP总结(一)

抽象语言---编码---二进制---电信号----处理电信号 &#xff08;电脑工作流程&#xff09; OSI参考模型 ----OSI/RM (核心思想&#xff1a;分层) 应用层----提供各种应用服务&#xff0c;将抽象语言转换成编码&#xff0c;提供人机交互的接口 表示层----将编码转换成二进制 …...

华为OD机试真题Python实现【黑板上色】真题+解题思路+代码(20222023)

题目 疫情过后希望小学终于又重新开学了,3 年 2 班开学第一天的任务是将后面的黑板报重新制作, 黑板上已经写上了N个正整数,同学们需要给这每个数分别上一种颜色, 为了让黑板报既美观又有学习意义,老师要求同种颜色的所有数都可以被这个颜色中最小的那个数整除, 现在帮小…...

C++中的利器——模板

前文本文主要是讲解一下C中的利器——模板&#xff0c;相信铁子们在学完这一节后&#xff0c;写代码会更加的得心应手&#xff0c;更加的顺畅。一&#xff0c;泛型编程想要学习模板&#xff0c;我们要先了解为什么需要模板&#xff0c;我们可以看看下面这个程序。int add(int&a…...

k8s控制器

目录 一、控制器简介 二、控制器类型 1、RC和RS 2、Deployment 3、DaemonSet 4、Job 5、CronJob 6、StateFulSet 7、HPA 一、控制器简介 在kubernetes中&#xff0c;按照Pod的创建方式可以将其分为两类&#xff1a; 自主式:kubernetes直接创建出来的Pod&#xff0c;…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

C#中的CLR属性、依赖属性与附加属性

CLR属性的主要特征 封装性&#xff1a; 隐藏字段的实现细节 提供对字段的受控访问 访问控制&#xff1a; 可单独设置get/set访问器的可见性 可创建只读或只写属性 计算属性&#xff1a; 可以在getter中执行计算逻辑 不需要直接对应一个字段 验证逻辑&#xff1a; 可以…...

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

Ubuntu Cursor升级成v1.0

0. 当前版本低 使用当前 Cursor v0.50时 GitHub Copilot Chat 打不开&#xff0c;快捷键也不好用&#xff0c;当看到 Cursor 升级后&#xff0c;还是蛮高兴的 1. 下载 Cursor 下载地址&#xff1a;https://www.cursor.com/cn/downloads 点击下载 Linux (x64) &#xff0c;…...