C++如何使用STL实现高效查找和排序

STL中适合高效查找的容器有std::unordered_map、std::unordered_set、std::map、std::set和排序后的std::vector。其中std::unordered_map和std::unordered_set基于哈希表,平均查找时间复杂度为O(1),适用于对查找速度要求高且不关心顺序的场景;std::map和std::set基于红黑树,查找时间复杂度为O(log N),适用于需要有序数据或稳定性能的场景;排序后的std::vector结合二分查找可实现O(log N)查找,适合静态或低频更新的数据集。选择时需权衡数据规模、操作频率、是否有序及自定义类型的哈希或比较支持。

c++如何使用stl实现高效查找和排序

C++标准模板库(STL)通过提供一系列经过高度优化的容器(如

std::vector

std::map

std::unordered_map

)和算法(如

std::sort

std::find

std::binary_search

),使得在C++中实现高效的查找和排序变得相对直接且强大。关键在于理解不同容器和算法的底层机制及其时间复杂度,从而根据具体应用场景做出最合适的选择。

STL 提供了一整套工具来应对各种查找和排序需求。对于排序,

std::sort

是序列容器(如

std::vector

)的首选,它通常采用内省式排序(Introsort),性能非常出色,平均时间复杂度为O(N log N)。查找方面则更为多样,从简单的线性查找

std::find

到针对有序序列的二分查找

std::binary_search

std::lower_bound

等,再到基于树结构的

std::map

和基于哈希表的

std::unordered_map

,它们各自在不同场景下提供了从O(N)到O(log N)甚至平均O(1)的查找效率。选择哪个,往往是我在设计系统时最先考虑的问题之一,因为它直接关系到程序的响应速度。

STL中哪些容器最适合高效查找操作?

在STL中,针对高效查找,我们通常会在

std::vector

(配合排序)、

std::map

std::set

std::unordered_map

std::unordered_set

之间做选择。每种容器都有其适用场景和性能特点,这就像选择不同的工具箱来处理不同的任务。

std::vector

本身并不直接提供高效查找,但如果数据是静态的或者不经常变动,我们可以先用

std::sort

对其进行一次排序,之后再利用

std::binary_search

std::lower_bound

std::upper_bound

进行O(log N)的查找。这种方法在数据量大且查找频繁,但插入/删除操作较少时非常有效。我个人在处理一些只读数据集时,就喜欢这种“一次排序,多次查找”的模式。

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

std::map

std::set

是基于红黑树实现的,它们提供O(log N)的查找、插入和删除操作。它们的优点是数据始终保持有序,可以进行范围查找,并且性能非常稳定。如果你需要有序遍历,或者对查找性能有严格的对数级保证,

map

/

set

是很好的选择。

std::unordered_map

std::unordered_set

则是基于哈希表实现的。它们在平均情况下能提供O(1)的查找、插入和删除操作,理论上是查找最快的容器。但在最坏情况下(哈希冲突严重),性能可能退化到O(N)。它们不保证元素的顺序。我发现,对于那些对查找速度要求极致,且不关心元素顺序的场景,

unordered_map

几乎是我的首选。不过,这也要求我们对哈希函数的质量有所考量,特别是对于自定义类型作为键的情况。

如何利用

std::sort

和自定义比较器实现复杂数据类型的排序?

std::sort

是STL中最常用的排序算法之一,它接受一个迭代器范围和可选的比较函数。对于基本数据类型,

std::sort

默认会进行升序排序。但当我们处理自定义的复杂数据类型时,比如一个

Person

结构体,包含姓名、年龄等字段,就需要自定义比较器来告诉

std::sort

如何比较两个

Person

对象。

自定义比较器可以是:

一个函数对象(Functor):定义一个重载了

operator()

的类。一个Lambda表达式:C++11及更高版本中最灵活和简洁的方式。一个普通函数:作为比较器的参数传入。

我通常倾向于使用Lambda表达式,因为它简洁且可以直接在调用

std::sort

的地方定义,上下文清晰。

#include #include #include #include struct Person {    std::string name;    int age;    double height;};int main() {    std::vector people = {        {"Alice", 30, 1.65},        {"Bob", 25, 1.80},        {"Charlie", 30, 1.75},        {"David", 25, 1.70}    };    // 示例1: 按年龄升序排序    // 如果年龄相同,则按姓名升序排序    std::sort(people.begin(), people.end(), [](const Person& a, const Person& b) {        if (a.age != b.age) {            return a.age < b.age;        }        return a.name < b.name;    });    std::cout << "Sorted by age, then name:n";    for (const auto& p : people) {        std::cout << p.name << ", " << p.age << ", " << p.height < b.height; // 注意是 > 实现降序    });    std::cout << "nSorted by height (descending):n";    for (const auto& p : people) {        std::cout << p.name << ", " << p.age << ", " << p.height << "n";    }    return 0;}

通过这种方式,我们可以轻松地根据任何复杂的逻辑来对自定义数据类型进行排序。我记得刚开始学C++的时候,自定义排序函数让我觉得有点神奇,因为它可以把我的“比较规则”直接传给算法,非常灵活。

在STL中进行查找时,

std::find

std::binary_search

有何区别,何时选用?

std::find

std::binary_search

是STL中两种基本的查找算法,但它们的工作原理和适用场景截然不同。理解它们的区别,能帮助我们避免一些常见的性能陷阱。

std::find

执行的是线性查找。它从容器的起始位置开始,逐个元素地与目标值进行比较,直到找到匹配的元素或遍历完整个容器。它的时间复杂度是O(N),这意味着查找时间与容器中元素的数量成正比。

std::find

的优点是它不需要容器中的元素是排序的,适用于任何类型的序列容器。如果你只需要查找一次,且容器很小或者未排序,

std::find

是个不错的选择。

std::binary_search

执行的是二分查找。它的前提条件是容器中的元素必须已经排序。它通过不断将搜索范围减半来查找目标值,时间复杂度是O(log N)。这意味着即使容器中有数百万个元素,查找也能在非常短的时间内完成。除了

std::binary_search

,还有

std::lower_bound

std::upper_bound

,它们不仅能告诉你元素是否存在,还能返回其在有序序列中的插入位置或出现范围的迭代器。

何时选用:

选用

std::find

当容器未排序,且你不想或不能对其进行排序时。当容器非常小,O(N)的开销可以忽略不计时。当查找操作不频繁,或者只需要进行一次性查找时。选用

std::binary_search

(或

lower_bound

/

upper_bound

):当容器已经排序,或者你可以接受一次性排序的成本,并且之后会进行多次查找时。当容器非常大,O(log N)的性能优势会非常显著。当你需要查找元素的精确位置或范围时(使用

lower_bound

/

upper_bound

)。

我经常看到一些新手在处理一个大型数据集时,反复调用

std::find

,导致程序运行缓慢。其实很多时候,只要先对数据进行一次

std::sort

,然后切换到

std::binary_search

,性能就能得到质的飞跃。当然,如果数据经常变动,每次插入或删除后都需要重新排序,那么

std::map

std::unordered_map

可能更合适,因为它们内部维护了有序性或哈希结构。

std::unordered_map

在性能上真的比

std::map

更有优势吗?有哪些潜在的陷阱?

std::unordered_map

std::map

都是键值对容器,但它们的底层实现和性能特性差异巨大,因此不能简单地说哪个“更优”,而应该说哪个“更适合”特定场景。我个人在项目中,对这两种容器的选择,往往是性能调优的关键点之一。

性能优势:

std::unordered_map

基于哈希表实现,其平均时间复杂度在查找、插入和删除操作上都是O(1)。这意味着理论上,无论容器中存储了多少元素,这些操作的耗时都是常数级别的。相比之下,

std::map

基于红黑树实现,其所有操作的时间复杂度都是O(log N)。对于大数据集,O(1)的平均性能无疑具有巨大的吸引力。

潜在陷阱:尽管

unordered_map

在平均性能上表现出色,但它并非没有缺点,甚至有一些“陷阱”需要注意:

最坏情况性能退化: 当哈希函数设计不当,或者遇到恶意数据导致大量哈希冲突时,

unordered_map

的性能可能退化到O(N)。这时,它甚至可能比

map

更慢。我曾经遇到过因为自定义类型哈希函数写得不好,导致

unordered_map

性能急剧下降的情况,排查起来还挺费劲的。哈希函数要求: 对于自定义类型作为键,你必须提供一个有效的哈希函数(通过特化

std::hash

模板或提供一个自定义的哈希函数对象)。如果忘记提供,或者提供的哈希函数质量不高,就会导致编译错误或性能问题。内存开销:

unordered_map

通常比

map

有更高的内存开销,因为它需要维护一个哈希表(通常是一个

std::vector

或数组),即使其中很多桶是空的。此外,每个节点通常也比

map

的节点更大。无序性:

unordered_map

不保证元素的任何特定顺序。如果你需要按键的顺序遍历元素,或者需要进行范围查询,

unordered_map

就无法满足需求。而

map

则天然地保持了键的有序性。哈希表重哈希(rehash)开销: 当哈希表的负载因子(load factor)超过阈值时,

unordered_map

会进行一次重哈希操作,这涉及到重新分配更大的内存并重新计算所有元素的哈希值和位置。这个操作的开销是O(N),虽然不频繁,但在某些实时性要求高的场景下需要考虑。

何时选用:

选用

unordered_map

当你需要最快的平均查找、插入和删除速度,且不关心元素的顺序,并且可以确保哈希函数质量较高时。选用

map

当你需要保持元素的有序性,需要进行范围查询,或者对性能的稳定性有严格要求(避免最坏情况),或者自定义类型作为键难以提供高质量哈希函数时。

例如,如果你要存储一个人的ID到其详细信息的映射,并且ID是

int

string

这种有良好内置哈希支持的类型,且你只关心快速通过ID查找,那么

unordered_map

会是很好的选择。但如果你需要按ID范围查找,或者ID是自定义的复杂对象,且你不想花精力去写一个好的哈希函数,那么

map

可能更稳妥。

#include #include #include #include // 自定义类型作为键struct Point {    int x, y;    // 必须提供相等运算符    bool operator==(const Point& other) const {        return x == other.x && y == other.y;    }};// 为自定义类型提供哈希函数// 方式1: 特化std::hashnamespace std {    template     struct hash {        size_t operator()(const Point& p) const {            // 一个简单的哈希组合,实际应用中可能需要更复杂的哈希函数            return hash()(p.x) ^ (hash()(p.y) << 1);        }    };}int main() {    std::unordered_map umap;    umap[{1, 2}] = "Point A";    umap[{3, 4}] = "Point B";    if (umap.count({1, 2})) {        std::cout << "Found in unordered_map: " << umap[{1, 2}] << std::endl;    }    // std::map 也可以使用 Point 作为键,但 Point 必须定义 operator<    std::map m;    // Point 必须有 operator<    // bool operator<(const Point& other) const {    //     if (x != other.x) return x < other.x;    //     return y < other.y;    // }    // 如果没有,这里会编译错误    return 0;}

这段代码展示了

unordered_map

使用自定义类型作为键时,需要提供

operator==

std::hash

特化。如果没有这些,

unordered_map

就无法工作。而

map

则需要

operator<

。这些细节,在实际开发中,往往是决定使用哪种容器的关键因素。

以上就是C++如何使用STL实现高效查找和排序的详细内容,更多请关注创想鸟其它相关文章!

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

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月18日 23:54:23
下一篇 2025年12月18日 23:54:35

相关推荐

  • Uniapp 中如何不拉伸不裁剪地展示图片?

    灵活展示图片:如何不拉伸不裁剪 在界面设计中,常常需要以原尺寸展示用户上传的图片。本文将介绍一种在 uniapp 框架中实现该功能的简单方法。 对于不同尺寸的图片,可以采用以下处理方式: 极端宽高比:撑满屏幕宽度或高度,再等比缩放居中。非极端宽高比:居中显示,若能撑满则撑满。 然而,如果需要不拉伸不…

    2025年12月24日
    400
  • 如何让小说网站控制台显示乱码,同时网页内容正常显示?

    如何在不影响用户界面的情况下实现控制台乱码? 当在小说网站上下载小说时,大家可能会遇到一个问题:网站上的文本在网页内正常显示,但是在控制台中却是乱码。如何实现此类操作,从而在不影响用户界面(UI)的情况下保持控制台乱码呢? 答案在于使用自定义字体。网站可以通过在服务器端配置自定义字体,并通过在客户端…

    2025年12月24日
    800
  • 如何在地图上轻松创建气泡信息框?

    地图上气泡信息框的巧妙生成 地图上气泡信息框是一种常用的交互功能,它简便易用,能够为用户提供额外信息。本文将探讨如何借助地图库的功能轻松创建这一功能。 利用地图库的原生功能 大多数地图库,如高德地图,都提供了现成的信息窗体和右键菜单功能。这些功能可以通过以下途径实现: 高德地图 JS API 参考文…

    2025年12月24日
    400
  • 如何使用 scroll-behavior 属性实现元素scrollLeft变化时的平滑动画?

    如何实现元素scrollleft变化时的平滑动画效果? 在许多网页应用中,滚动容器的水平滚动条(scrollleft)需要频繁使用。为了让滚动动作更加自然,你希望给scrollleft的变化添加动画效果。 解决方案:scroll-behavior 属性 要实现scrollleft变化时的平滑动画效果…

    2025年12月24日
    000
  • 如何为滚动元素添加平滑过渡,使滚动条滑动时更自然流畅?

    给滚动元素平滑过渡 如何在滚动条属性(scrollleft)发生改变时为元素添加平滑的过渡效果? 解决方案:scroll-behavior 属性 为滚动容器设置 scroll-behavior 属性可以实现平滑滚动。 html 代码: click the button to slide right!…

    2025年12月24日
    500
  • 如何选择元素个数不固定的指定类名子元素?

    灵活选择元素个数不固定的指定类名子元素 在网页布局中,有时需要选择特定类名的子元素,但这些元素的数量并不固定。例如,下面这段 html 代码中,activebar 和 item 元素的数量均不固定: *n *n 如果需要选择第一个 item元素,可以使用 css 选择器 :nth-child()。该…

    2025年12月24日
    200
  • 使用 SVG 如何实现自定义宽度、间距和半径的虚线边框?

    使用 svg 实现自定义虚线边框 如何实现一个具有自定义宽度、间距和半径的虚线边框是一个常见的前端开发问题。传统的解决方案通常涉及使用 border-image 引入切片图片,但是这种方法存在引入外部资源、性能低下的缺点。 为了避免上述问题,可以使用 svg(可缩放矢量图形)来创建纯代码实现。一种方…

    2025年12月24日
    100
  • 如何让“元素跟随文本高度,而不是撑高父容器?

    如何让 元素跟随文本高度,而不是撑高父容器 在页面布局中,经常遇到父容器高度被子元素撑开的问题。在图例所示的案例中,父容器被较高的图片撑开,而文本的高度没有被考虑。本问答将提供纯css解决方案,让图片跟随文本高度,确保父容器的高度不会被图片影响。 解决方法 为了解决这个问题,需要将图片从文档流中脱离…

    2025年12月24日
    000
  • 为什么 CSS mask 属性未请求指定图片?

    解决 css mask 属性未请求图片的问题 在使用 css mask 属性时,指定了图片地址,但网络面板显示未请求获取该图片,这可能是由于浏览器兼容性问题造成的。 问题 如下代码所示: 立即学习“前端免费学习笔记(深入)”; icon [data-icon=”cloud”] { –icon-cl…

    2025年12月24日
    200
  • 如何利用 CSS 选中激活标签并影响相邻元素的样式?

    如何利用 css 选中激活标签并影响相邻元素? 为了实现激活标签影响相邻元素的样式需求,可以通过 :has 选择器来实现。以下是如何具体操作: 对于激活标签相邻后的元素,可以在 css 中使用以下代码进行设置: li:has(+li.active) { border-radius: 0 0 10px…

    2025年12月24日
    100
  • 如何模拟Windows 10 设置界面中的鼠标悬浮放大效果?

    win10设置界面的鼠标移动显示周边的样式(探照灯效果)的实现方式 在windows设置界面的鼠标悬浮效果中,光标周围会显示一个放大区域。在前端开发中,可以通过多种方式实现类似的效果。 使用css 使用css的transform和box-shadow属性。通过将transform: scale(1.…

    2025年12月24日
    200
  • 为什么我的 Safari 自定义样式表在百度页面上失效了?

    为什么在 Safari 中自定义样式表未能正常工作? 在 Safari 的偏好设置中设置自定义样式表后,您对其进行测试却发现效果不同。在您自己的网页中,样式有效,而在百度页面中却失效。 造成这种情况的原因是,第一个访问的项目使用了文件协议,可以访问本地目录中的图片文件。而第二个访问的百度使用了 ht…

    2025年12月24日
    000
  • 如何用前端实现 Windows 10 设置界面的鼠标移动探照灯效果?

    如何在前端实现 Windows 10 设置界面中的鼠标移动探照灯效果 想要在前端开发中实现 Windows 10 设置界面中类似的鼠标移动探照灯效果,可以通过以下途径: CSS 解决方案 DEMO 1: Windows 10 网格悬停效果:https://codepen.io/tr4553r7/pe…

    2025年12月24日
    000
  • 使用CSS mask属性指定图片URL时,为什么浏览器无法加载图片?

    css mask属性未能加载图片的解决方法 使用css mask属性指定图片url时,如示例中所示: mask: url(“https://api.iconify.design/mdi:apple-icloud.svg”) center / contain no-repeat; 但是,在网络面板中却…

    2025年12月24日
    000
  • 如何用CSS Paint API为网页元素添加时尚的斑马线边框?

    为元素添加时尚的斑马线边框 在网页设计中,有时我们需要添加时尚的边框来提升元素的视觉效果。其中,斑马线边框是一种既醒目又别致的设计元素。 实现斜向斑马线边框 要实现斜向斑马线间隔圆环,我们可以使用css paint api。该api提供了强大的功能,可以让我们在元素上绘制复杂的图形。 立即学习“前端…

    2025年12月24日
    000
  • 图片如何不撑高父容器?

    如何让图片不撑高父容器? 当父容器包含不同高度的子元素时,父容器的高度通常会被最高元素撑开。如果你希望父容器的高度由文本内容撑开,避免图片对其产生影响,可以通过以下 css 解决方法: 绝对定位元素: .child-image { position: absolute; top: 0; left: …

    2025年12月24日
    000
  • 为什么自定义样式表在 Safari 中访问百度页面时无法生效?

    自定义样式表在 safari 中失效的原因 用户尝试在 safari 偏好设置中添加自定义样式表,代码如下: body { background-image: url(“/users/luxury/desktop/wallhaven-o5762l.png”) !important;} 测试后发现,在…

    2025年12月24日
    000
  • CSS 帮助

    我正在尝试将文本附加到棕色框的左侧。我不能。我不知道代码有什么问题。请帮助我。 css .hero { position: relative; bottom: 80px; display: flex; justify-content: left; align-items: start; color:…

    2025年12月24日 好文分享
    200
  • 前端代码辅助工具:如何选择最可靠的AI工具?

    前端代码辅助工具:可靠性探讨 对于前端工程师来说,在HTML、CSS和JavaScript开发中借助AI工具是司空见惯的事情。然而,并非所有工具都能提供同等的可靠性。 个性化需求 关于哪个AI工具最可靠,这个问题没有一刀切的答案。每个人的使用习惯和项目需求各不相同。以下是一些影响选择的重要因素: 立…

    2025年12月24日
    000
  • 如何用 CSS Paint API 实现倾斜的斑马线间隔圆环?

    实现斑马线边框样式:探究 css paint api 本文将探究如何使用 css paint api 实现倾斜的斑马线间隔圆环。 问题: 给定一个有多个圆圈组成的斑马线图案,如何使用 css 实现倾斜的斑马线间隔圆环? 答案: 立即学习“前端免费学习笔记(深入)”; 使用 css paint api…

    2025年12月24日
    000

发表回复

登录后才能评论
关注微信