C++STL查找算法find和binary_search使用

std::find适用于无序数据的线性查找,返回元素位置,时间复杂度O(N);std::binary_search要求数据有序,仅判断存在性,时间复杂度O(log N),效率更高。

c++stl查找算法find和binary_search使用

在C++ STL中,

std::find

std::binary_search

是两种核心的查找算法,它们各自适用于不同的场景和数据特性。简单来说,

find

是一种线性查找,不要求数据有序,它会遍历序列直到找到目标或遍历结束;而

binary_search

则是一种二分查找,它严格要求数据必须已经有序,效率远高于

find

,但它只告诉你元素是否存在,不会返回具体位置。选择哪一个,核心在于你的数据是否已排序,以及你需要的是元素的存在性判断还是其精确位置。

解决方案

std::find

std::binary_search

是C++标准库(STL)中两种非常实用的查找算法,它们都定义在


头文件中。理解它们的适用场景和工作原理,对于编写高效且正确的C++代码至关重要。

std::find

:线性查找的通用解法

std::find

是最基础的查找算法之一。它接受一个迭代器范围

[first, last)

和一个待查找的值

val

。它的工作方式很简单:从

first

指向的元素开始,逐个比较,直到找到第一个与

val

相等的元素,或者遍历到

last

立即学习“C++免费学习笔记(深入)”;

特点:不要求数据有序: 这是它最大的优点,你可以对任何序列类型(如

std::vector

std::list

std::array

等)使用它,无论它们是否排序。返回迭代器: 如果找到目标元素,它会返回一个指向该元素的迭代器;如果没有找到,则返回

last

迭代器。这使得你可以进一步操作找到的元素。时间复杂度: 平均和最坏情况下都是O(N),N是序列中的元素数量。这意味着,数据量越大,查找时间越长。何时使用:当你的数据是无序的。当你需要找到元素的具体位置(迭代器),而不仅仅是判断它是否存在。当数据量相对较小,O(N)的性能开销可以接受时。

#include #include #include  // for std::findint main() {    std::vector numbers = {10, 20, 30, 40, 20, 50};    // 使用 std::find 查找 30    auto it_found = std::find(numbers.begin(), numbers.end(), 30);    if (it_found != numbers.end()) {        std::cout << "找到 30,位于索引: " << std::distance(numbers.begin(), it_found) << std::endl;    } else {        std::cout << "未找到 30" << std::endl;    }    // 使用 std::find 查找 99 (不存在的元素)    auto it_not_found = std::find(numbers.begin(), numbers.end(), 99);    if (it_not_found != numbers.end()) {        std::cout << "找到 99" << std::endl;    } else {        std::cout << "未找到 99" << std::endl;    }    return 0;}

std::binary_search

:高效查找的有序利器

std::binary_search

是一个基于二分查找的算法,它的效率非常高,但前提是它操作的范围

[first, last)

必须是已排序的(默认是升序,也可以提供自定义比较器)。它不会返回元素的具体位置,只返回一个布尔值,指示元素是否存在。

特点:要求数据有序: 这是它使用的前提条件。如果数据无序,结果将是未定义的或错误的。只返回布尔值:

true

表示找到,

false

表示未找到。时间复杂度: O(log N)。对于大型数据集,这比O(N)的线性查找快得多。例如,在一百万个元素中查找,线性查找可能需要一百万次比较,而二分查找只需要大约20次。何时使用:当你的数据已经有序,或者你愿意为了一系列查找而先对数据进行排序。当你只需要判断某个元素是否存在,而不需要知道它的具体位置。

#include #include #include  // for std::binary_search, std::sortint main() {    std::vector sorted_numbers = {10, 20, 30, 40, 50}; // 假设数据已排序    // 如果数据未排序,需要先排序    // std::vector unsorted_numbers = {50, 10, 30, 20, 40};    // std::sort(unsorted_numbers.begin(), unsorted_numbers.end());    // std::cout << "排序后的数据: ";    // for (int n : unsorted_numbers) {    //     std::cout << n << " ";    // }    // std::cout << std::endl;    // 使用 std::binary_search 查找 30    if (std::binary_search(sorted_numbers.begin(), sorted_numbers.end(), 30)) {        std::cout << "找到 30" << std::endl;    } else {        std::cout << "未找到 30" << std::endl;    }    // 使用 std::binary_search 查找 99 (不存在的元素)    if (std::binary_search(sorted_numbers.begin(), sorted_numbers.end(), 99)) {        std::cout << "找到 99" << std::endl;    } else {        std::cout << "未找到 99" << std::endl;    }    return 0;}

为什么在有序数据中,

binary_search

find

更受青睐?

这是一个非常实际的问题,在我个人的开发经验中,如果数据量稍大且确定有序,我几乎总是倾向于

binary_search

或者其他基于二分查找的变体。原因很简单,但其背后的性能差异是巨大的。

std::find

的工作方式是线性的,它从头到尾一个一个地检查元素。想象一下你在图书馆找一本书,如果书架上的书是随机摆放的,你只能一本一本翻过去看书名,直到找到为止。这就是O(N)的复杂度,最坏情况下你需要检查所有书。

std::binary_search

则利用了数据有序的特性。它就像你在查字典:你知道字母顺序,所以你可以直接翻到中间,如果目标字母在你翻到的这一页之前,你就只在前半部分找;如果之后,就只在后半部分找。每次查找都将搜索范围缩小一半。这种“分而治之”的策略,使得查找次数呈对数增长,也就是O(log N)。

举个例子,假设有一个包含100万个元素的

std::vector

std::find

平均需要进行50万次比较(最坏情况100万次)。

std::binary_search

最多只需要大约20次比较(log₂1,000,000 ≈ 19.9)。

这种效率上的天壤之别,在处理大数据集时尤为明显。如果你的应用程序对性能有要求,并且数据可以保持有序,那么选择

binary_search

或其同类算法是毫无疑问的优化方向。当然,如果数据量特别小,比如只有几十个元素,

find

的O(N)和

binary_search

的O(log N)在实际执行时间上可能差别不大,甚至

find

由于其更简单的内部逻辑,在某些极端情况下可能略快一点(因为

binary_search

的逻辑分支更多)。但从扩展性考虑,

binary_search

无疑是更优的选择。

除了

find

binary_search

,STL还有哪些查找利器?

STL的查找算法远不止这两个,它提供了一系列工具来应对各种复杂的查找需求。在我看来,掌握这些工具能让你更灵活地处理数据。

std::lower_bound

std::upper_bound

这两个算法是

binary_search

的“升级版”,它们也要求数据有序。

std::lower_bound(first, last, val)

返回一个迭代器,指向序列中第一个不小于

val

的元素。

std::upper_bound(first, last, val)

返回一个迭代器,指向序列中第一个大于

val

的元素。它们常用于查找一个元素可能插入的位置,或者查找一个区间内所有与

val

相等的元素。示例:

std::vector data = {10, 20, 30, 30, 30, 40, 50};auto low = std::lower_bound(data.begin(), data.end(), 30); // 指向第一个30auto up = std::upper_bound(data.begin(), data.end(), 30);   // 指向第一个40std::cout << "30出现的次数: " << std::distance(low, up) << std::endl; // 输出3

std::equal_range

结合了

lower_bound

upper_bound

的功能,同样要求数据有序。它返回一个

std::pair

,其中

first

lower_bound

的结果,

second

upper_bound

的结果。对于查找某个值在有序序列中的所有出现位置,这个函数非常方便。

std::find_if

std::find_if_not

这两个是

find

的“条件查找”版本。它们不查找具体的值,而是查找满足特定条件的第一个元素。

std::find_if(first, last, predicate)

查找第一个使

predicate

返回

true

的元素。

std::find_if_not(first, last, predicate)

查找第一个使

predicate

返回

false

的元素。示例:

std::vector nums = {1, 2, 3, 4, 5, 6};auto it_even = std::find_if(nums.begin(), nums.end(), [](int n){ return n % 2 == 0; });if (it_even != nums.end()) {    std::cout << "第一个偶数是: " << *it_even << std::endl; // 输出2}

std::count

std::count_if

用于计算某个值或满足某个条件的元素在序列中出现的次数。

std::count(first, last, val)

计算

val

出现的次数。

std::count_if(first, last, predicate)

计算满足

predicate

条件的元素次数。

std::search

std::find_end

用于在一个序列中查找另一个子序列的出现。

std::search

查找第一个出现的子序列。

std::find_end

查找最后一个出现的子序列。

除了这些通用算法,别忘了STL容器自身也提供了高效的查找方法,比如

std::set

std::map

std::unordered_set

std::unordered_map

。这些容器的

find

成员函数通常比全局算法更高效,因为它们利用了容器内部的数据结构(红黑树或哈希表)来实现O(log N)或平均O(1)的查找速度。当你需要频繁查找时,选择合适的容器本身就是一种强大的查找策略。

在使用STL查找算法时,常见的性能陷阱和优化建议有哪些?

尽管STL算法强大,但在实际使用中,如果不注意一些细节,可能会掉入性能陷阱。我个人在项目中就遇到过不少因为选择不当或理解偏差导致的性能瓶颈

对未排序数据使用

binary_search

这是最常见的错误,也是最致命的。

std::binary_search

要求数据有序,如果你在一个无序的

vector

上调用它,结果是未定义的,很可能返回错误的结果,而编译器不会报错。如果你需要对无序数据进行高效查找,要么先排序(如果查找次数多于一次),要么考虑使用哈希表。

频繁地对大型数据集进行排序后单次查找: 如果你只有一个查找操作,并且数据是无序的,那么先用

std::sort

进行O(N log N)的排序,然后用

std::binary_search

进行O(log N)的查找,总成本是O(N log N)。这通常比直接用

std::find

进行O(N)的线性查找要慢。只有当你需要进行多次查找时,预排序的成本才能被摊销。

选择错误的容器类型:

std::vector

对于

std::find

来说,由于其内存连续性,缓存局部性好,表现不错。但对于大量插入/删除操作,它效率不高。

std::list

std::find

list

上的表现通常比

vector

差,因为

list

的元素不连续,缓存命中率低。

binary_search

无法直接应用于

list

(需要随机访问迭代器)。

std::set

/

std::map

这些基于红黑树的容器,其

find

成员函数提供O(log N)的查找效率,且数据始终保持有序。如果你的核心需求是高效查找和自动排序,它们是很好的选择。

std::unordered_set

/

std::unordered_map

基于哈希表的容器,提供平均O(1)的查找效率。这是在需要极致查找速度且不关心元素顺序时的首选。但在最坏情况下(哈希冲突严重),性能可能退化到O(N)。

自定义比较器的效率: 如果你为

std::sort

std::binary_search

提供了自定义比较器(lambda表达式或函数对象),请确保这个比较器本身是高效的。一个复杂的比较器可能会抵消二分查找带来的性能优势。

不必要的拷贝: 当查找对象是复杂类型时,确保比较操作是按引用进行的,避免不必要的对象拷贝。例如,如果你的

vector

存储的是大对象,

std::find

在比较时会使用

operator==

,如果这个操作涉及大量拷贝,会影响性能。

迭代器失效: 虽然不直接是查找算法本身的性能陷阱,但在使用查找算法得到迭代器后,如果对容器进行了修改(如插入、删除),可能会导致迭代器失效,后续使用失效迭代器会引发未定义行为。这是STL编程中一个非常基础但又容易被忽视的问题。

优化建议:

了解你的数据: 数据是否需要有序?查找频率如何?数据量大小?这些是选择算法和容器的关键。选择合适的容器: 如果你需要频繁查找且数据有序,考虑

std::set

std::map

。如果查找是主要的且不需要有序,

std::unordered_set

std::unordered_map

通常是最快的。预排序: 如果数据大部分时间是静态的,但需要多次高效查找,那么一次性排序的成本是值得的。使用

std::lower_bound

等: 当你需要获取有序序列中元素的精确位置或范围时,

lower_bound

upper_bound

equal_range

binary_search

更有用,因为它们提供了迭代器。剖析(Profiling): 如果对性能有疑问,不要猜测,使用性能分析工具(如Valgrind, gprof, Visual Studio Profiler等)来找出真正的瓶颈。有时候,你认为的瓶颈可能并不是实际的。

总之,STL提供了丰富的查找工具,关键在于理解它们的工作原理和适用场景,并结合实际需求做出明智的选择。

以上就是C++STL查找算法find和binary_search使用的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1476055.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Yii2框架Gii工具怎么用_Yii2框架Gii代码生成器教程
上一篇 2026年5月10日 10:47:08
阻止搜索引擎爬虫触发网站非预期操作的指南
下一篇 2026年5月10日 10:47:12

相关推荐

  • 股票对比特币的投资价值是真的吗?股票与比特币之争原因分析

    股票与比特币投资价值之争源于属性差异:股票依托企业盈利和现金流,具备稳定分红与监管保障,适合长期投资;比特币则依赖去中心化、稀缺性及市场共识,价格波动剧烈,缺乏内在价值支撑,监管风险高,更多被视作投机性资产或数字黄金。两者在风险特征、功能定位和市场成熟度上存在根本区别。 Binance币安 欧易OK…

    2026年5月10日
    000
  • php数据库游标使用教程_php数据库逐行处理数据方法

    使用PDO和MySQLi的游标功能可实现数据库大数据量下的低内存逐行处理。首先通过PDO设置PDO::MYSQL_ATTR_USE_BUFFERED_QUERY为false,结合fetch()方法逐行读取;或使用MySQLi的query()配合MYSQLI_USE_RESULT模式执行未缓冲查询,再…

    2026年5月10日
    000
  • Go 语言中的泛型:概念、影响与演进

    泛型是一种允许在编译时使用类型参数编写代码的编程范式,它使得函数或数据结构能够处理多种数据类型,从而实现代码复用和类型安全。在静态类型语言中,泛型的缺失曾导致大量重复代码,开发者不得不为不同类型的数据集合编写功能相同的函数。go 1.18版本引入泛型后,有效解决了这一痛点,显著提升了代码的灵活性和可…

    2026年5月10日
    000
  • 在移动运行时中集成Next.js API路由的策略

    在移动运行时(如Capacitor或Expo)中直接运行包含Next.js API路由的完整应用是不可行的,因为API路由属于服务器端逻辑,而Capacitor/Expo仅打包客户端代码。本文旨在探讨几种将现有Next.js应用及其API路由适配到移动环境的策略,包括外部化API服务、迁移API逻辑…

    2026年5月10日
    000
  • c++怎么解决undefined reference to链接错误_c++链接错误undefined reference排查方法

    出现 undefined reference 错误是由于链接器找不到函数或变量的实现,常见原因包括:1. 函数声明但未定义;2. 源文件未参与链接;3. 类成员函数或静态成员变量未定义;4. 第三方库未正确链接;5. 命名空间或拼写错误;6. 模板函数定义不在头文件中;7. extern 变量未在任…

    2026年5月10日
    100
  • c++怎么使用std::span_c++ std::span使用方法

    c++kquote>std::span是C++20引入的轻量级非拥有式容器,用于安全引用连续内存。它无需复制数据,支持数组、vector等连续存储结构,通过#include 使用。可从原生数组、容器、指针+长度或迭代器构造,提供size()、data()、subspan()等类似容器的操作接口…

    2026年5月10日
    100
  • HTML地理位置怎么优化_本地SEO代码优化技巧

    HTML地理位置优化需使用Schema.org标记并确保信息一致,结合关键词、地图嵌入和本地内容提升本地搜索排名。 HTML地理位置优化,简单来说,就是让你的网站在本地搜索结果中更容易被找到。核心在于告诉搜索引擎你的网站与特定地理位置相关,并提升用户体验。 解决方案 使用Schema.org标记: …

    2026年5月10日
    200
  • Go 语言性能基准测试:利用 testing 包进行代码性能分析

    本文详细介绍了在 Go 语言中进行代码性能基准测试的现代方法。针对开发者在寻找类似秒表功能的计时器时可能遇到的困惑,我们重点阐述了如何利用 Go 内置的 testing 包来编写和执行基准测试函数,以准确测量代码段的运行效率,并提供了实用的示例和执行指南,帮助开发者优化程序性能。 在软件开发中,尤其…

    2026年5月10日
    000
  • Golang系统调用阻塞怎么排查?Golang非阻塞IO方案

    Golang系统调用阻塞怎么排查?Golang非阻塞IO方案Golang系统调用阻塞怎么排查?Golang非阻塞IO方案Golang系统调用阻塞怎么排查?Golang非阻塞IO方案Golang系统调用阻塞怎么排查?Golang非阻塞IO方案

    golang系统调用阻塞问题可通过以下方法排查与解决:1. 使用profiling工具如go tool pprof分析cpu和内存使用,识别耗时最长的函数及系统调用阻塞点;2. 利用strace跟踪系统调用,查看耗时操作;3. 增加日志记录关键操作耗时;4. 检查资源限制如文件描述符数量;5. 进行…

    2026年5月10日 用户投稿
    000
  • 阻止搜索引擎爬虫触发网站非预期操作的指南

    本教程旨在解决搜索引擎爬虫(如bingbot)因访问网站特定页面而意外触发邮件发送等非预期操作的问题。核心解决方案是遵循http协议规范,将执行状态变更操作的请求从get方法改为post方法,并辅以必要的认证机制,以确保网站功能的正确性和安全性,有效防止爬虫对网站造成干扰。 理解搜索引擎爬虫与HTT…

    2026年5月10日
    000
  • 怎样使用 JavaScript 的 Typed Arrays 处理二进制数据?

    Typed Arrays通过ArrayBuffer实现对二进制数据的高效操作,需用视图如Int32Array或DataView访问,支持多种数据类型和字节序控制,适用于处理图像、音频等原始数据。 JavaScript 的 Typed Arrays 提供了一种高效处理二进制数据的方式,特别适用于操作原…

    2026年5月10日
    100
  • Bootstrap Accordion:防止所有手风琴同时展开及初始状态修复

    Bootstrap Accordion:防止所有手风琴同时展开及初始状态修复 本文旨在解决 Bootstrap 手风琴组件中多个手风琴同时展开的问题,并提供修复页面加载时手风琴箭头方向错误的方案。通过修改 HTML 结构中的 aria-labelledby 和 id 属性,确保每个手风琴项具有唯一的…

    2026年5月10日
    100
  • Debian RabbitMQ如何进行版本升级

    要在Debian系统上升级RabbitMQ,您可以按照以下步骤操作: 添加RabbitMQ官方仓库 首先,您需要添加RabbitMQ的官方仓库。这可以通过以下命令完成: sudo apt-get install -y apt-transport-httpscurl -fsSL https://git…

    2026年5月10日
    000
  • JavaScript中模拟点击事件触发DOM元素的onclick功能

    本教程详细阐述了如何在JavaScript中通过编程方式触发HTML元素的点击事件,以激活其关联的`onclick`功能或其他事件监听器。我们将介绍使用`element.click()`方法的最佳实践,并探讨其与直接调用`onclick`函数之间的区别,同时提供示例代码和注意事项,帮助开发者实现页面…

    2026年5月10日
    000
  • html5如何实现弹窗_HTML5模态框弹窗实现步骤与代码【弹窗】

    可使用HTML5 dialog元素、div+CSS+JS手动实现、:target伪类无JS方案或SweetAlert2等第三方库创建强制交互弹窗;其中dialog语义清晰且原生支持模态行为,其余方案侧重兼容性、轻量性或功能丰富性。 如果您希望在网页中创建一个用户无法绕过、必须交互的弹窗界面,则可以使…

    2026年5月10日
    000
  • setTimeout与异步执行的关系

    setTimeout与异步执行的关系setTimeout与异步执行的关系setTimeout与异步执行的关系setTimeout与异步执行的关系

    settimeout是理解javascript异步编程的关键,因为它揭示了单线程环境下任务调度的机制。1. settimeout将任务放入宏任务队列,等待调用栈清空后执行,避免阻塞当前代码;2. settimeout(…, 0)用于延迟到下一个事件循环执行,而promise.resolv…

    2026年5月10日 用户投稿
    000
  • 通过 XPath 在指定标签中查找元素

    通过 XPath 在指定标签中查找元素通过 XPath 在指定标签中查找元素通过 XPath 在指定标签中查找元素通过 XPath 在指定标签中查找元素

    本文旨在介绍如何使用 XPath 表达式在 HTML 或 XML 文档中查找特定标签内的元素。我们将探讨如何限制搜索范围,使其仅限于 `span`、`h1`、`h2` 等指定的标签,并提供有效的 XPath 表达式示例,以帮助您更精确地定位目标元素。 在使用 XPath 进行元素查找时,有时我们需要…

    2026年5月10日 用户投稿
    000
  • C++ int转string的方法汇总_C++11 to_string函数的使用详解

    C++中int转string最推荐使用std::to_string,它自C++11起成为标准,语法简单、类型安全,只需包含头文件,适用于整型和浮点型转换。 在C++中,将int类型转换为string类型是常见的操作。随着C++11标准的引入,std::to_string 成为了最简单直接的方法。本文…

    2026年5月10日
    000
  • 如何用TypeScript类型声明完善JavaScript单例Promise函数?

    TypeScript类型声明完善JavaScript单例Promise函数 本文介绍如何为javascript工具库编写精准的typescript类型声明文件(index.d.ts),解决类型定义难题。重点在于singlepromise函数的完善类型定义,该函数创建一个单例promise函数,并支持…

    2026年5月10日
    000
  • Go语言中按Unicode字符(Rune)遍历字符串的最佳实践

    在go语言中,字符串是utf-8编码的字节序列。直接通过索引访问字符串会得到字节而非unicode字符(rune),这在处理多字节字符时可能导致错误。本文将详细介绍如何使用go语言的for…range循环,以正确且高效的方式遍历字符串中的每一个unicode字符,并提供示例代码,帮助开发…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信