首页 > 后端开发 > C++ > 正文

C++如何在STL中使用自定义排序规则

P粉602998670
发布: 2025-09-18 09:46:01
原创
405人浏览过
<blockquote>自定义排序规则通过提供满足严格弱序的比较器实现,可应用于std::sort、std::set、std::map、std::priority_queue等STL容器和算法,支持按多条件、对象属性或非标准逻辑排序,提升数据处理灵活性。</blockquote> <p><img src="https://img.php.cn/upload/article/000/969/633/175815996264799.png" alt="c++如何在stl中使用自定义排序规则"></p> <p>在C++的STL中,如果你想让数据按照非默认的、你自己的逻辑来<a style="color:#f60; text-decoration:underline;" title="排列" href="https://www.php.cn/zt/56129.html" target="_blank">排列</a>,核心思路就是提供一个自定义的比较规则。这通常通过一个函数对象(functor)、一个lambda表达式,或者一个普通函数指针来实现,将其作为参数传递给需要排序的算法或容器。这样,STL算法就不会用它内置的<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;"><</pre>
登录后复制
</div>操作符来比较元素,而是用你给出的规则。</p> <h3>解决方案</h3> <p>在C++<a style="color:#f60; text-decoration:underline;" title="标准库" href="https://www.php.cn/zt/74427.html" target="_blank">标准库</a>中,自定义排序规则主要围绕着“比较器”(Comparator)的概念展开。无论你处理的是<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::sort</pre>
登录后复制
</div>这样的算法,还是<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::set</pre>
登录后复制
</div>、<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::map</pre>
登录后复制
</div>、<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::priority_queue</pre>
登录后复制
</div>这类有序容器,其原理都是一致的:提供一个可调用的实体,它接受两个元素作为参数,并返回一个<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">bool</pre>
登录后复制
</div>值,指示第一个元素是否“小于”第二个元素(按照你的自定义规则)。这个“小于”必须满足严格弱序(Strict Weak Ordering)的要求。</p> <p><strong>1. 使用Lambda表达式(推荐方式)</strong></p> <p>这是现代C++中最灵活、最简洁的方式,特别适合于比较规则不复杂,或者只在特定位置使用一次的情况。</p> <p><span>立即学习</span>“<a href="https://pan.quark.cn/s/6e7abc4abb9f" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">C++免费学习笔记(深入)</a>”;</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:cpp;toolbar:false;'>#include <iostream> #include <vector> #include <algorithm> #include <string> struct Person { std::string name; int age; }; int main() { std::vector<Person> people = { {"Alice", 30}, {"Bob", 25}, {"Charlie", 35}, {"David", 25} }; // 按年龄升序排序 std::sort(people.begin(), people.end(), [](const Person& a, const Person& b) { return a.age < b.age; }); std::cout << "Sorted by age (ascending):" << std::endl; for (const auto& p : people) { std::cout << p.name << " (" << p.age << ")" << std::endl; } // 如果年龄相同,按姓名降序排序 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 << "\nSorted by age (asc), then name (desc):" << std::endl; for (const auto& p : people) { std::cout << p.name << " (" << p.age << ")" << std::endl; } return 0; }</pre>
登录后复制
</div><p><strong>2. 使用函数对象(Functor)</strong></p> <p>当比较规则比较复杂,或者需要在多个地方复用,甚至需要比较器本身维护一些状态时,函数对象是一个非常好的选择。它是一个重载了<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">operator()</pre>
登录后复制
</div>的类。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:cpp;toolbar:false;'>#include <iostream> #include <vector> #include <algorithm> #include <string> struct Person { std::string name; int age; }; // 按年龄降序排序的函数对象 struct ComparePersonByAgeDesc { bool operator()(const Person& a, const Person& b) const { return a.age > b.age; // 注意这里是 > } }; // 另一个例子:按姓名长度升序 struct ComparePersonByNameLength { bool operator()(const Person& a, const Person& b) const { return a.name.length() < b.name.length(); } }; int main() { std::vector<Person> people = { {"Alice", 30}, {"Bob", 25}, {"Charlie", 35}, {"David", 25} }; std::sort(people.begin(), people.end(), ComparePersonByAgeDesc()); std::cout << "Sorted by age (desc) using functor:" << std::endl; for (const auto& p : people) { std::cout << p.name << " (" << p.age << ")" << std::endl; } std::sort(people.begin(), people.end(), ComparePersonByNameLength()); std::cout << "\nSorted by name length (asc) using functor:" << std::endl; for (const auto& p : people) { std::cout << p.name << " (" << p.age << ")" << std::endl; } return 0; }</pre>
登录后复制
</div><p><strong>3. 使用普通函数指针</strong></p> <p>虽然不如lambda和函数对象灵活,但对于简单的、无状态的比较逻辑,也可以使用普通函数。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:cpp;toolbar:false;'>#include <iostream> #include <vector> #include <algorithm> #include <string> struct Person { std::string name; int age; }; // 普通函数作为比较器 bool comparePeopleByNameAsc(const Person& a, const Person& b) { return a.name < b.name; } int main() { std::vector<Person> people = { {"Alice", 30}, {"Bob", 25}, {"Charlie", 35}, {"David", 25} }; std::sort(people.begin(), people.end(), comparePeopleByNameAsc); std::cout << "Sorted by name (asc) using function pointer:" << std::endl; for (const auto& p : people) { std::cout << p.name << " (" << p.age << ")" << std::endl; } return 0; }</pre>
登录后复制
</div><p>这三种方式都殊途同归,都是为了提供一个满足严格弱序的二元谓词(binary p<a style="color:#f60; text-decoration:underline;" title="red" href="https://www.php.cn/zt/122037.html" target="_blank">red</a>icate),让STL算法或容器知道如何比较两个元素。</p> <h3>C++自定义排序规则的实际应用场景是什么?</h3> <p>自定义排序规则在实际开发中几乎无处不在,远不止是把数字从小到大排那么简单。我个人觉得,它真正解放了我们处理复杂数据结构的能力,让数据组织变得更加灵活和富有表现力。</p> <p>想象一下,你有一个包含用户信息的列表,每个用户有ID、姓名、注册日期、活跃度等多个字段。你可能需要:</p> <ul> <li> <strong>按注册日期排序</strong>:最新的用户排在前面,方便查看新用户增长。</li> <li> <strong>按活跃度排序</strong>:最活跃的用户排在前面,用于奖励或特殊推荐。</li> <li> <strong>多条件复合排序</strong>:比如,先按<a style="color:#f60; text-decoration:underline;" title="会员" href="https://www.php.cn/zt/27337.html" target="_blank">会员</a>等级降序,如果等级相同,再按消费金额降序,如果金额也相同,最后按注册时间升序。这种多级排序是自定义比较器最常见的应用之一,用默认的<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;"><</pre>
登录后复制
</div>操作符根本无法实现。</li> <li> <strong>自定义对象排序</strong>:你的<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">struct</pre>
登录后复制
</div>或<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">class</pre>
登录后复制
</div>对象通常没有默认的<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;"><</pre>
登录后复制
</div>操作符,或者默认的比较逻辑不符合你的需求。比如一个<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">Point</pre>
登录后复制
</div>类,你可能想按距离原点的远近排序,而不是按X坐标或Y坐标。</li> <li> <strong>非标准顺序排序</strong>:比如,对字符串进行不区分大小写的排序,或者按照特定的字母表顺序(例如,某些语言的特殊字符排序规则)。</li> <li> <strong>优先级队列的自定义</strong>:<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::priority_queue</pre>
登录后复制
</div>默认是最大堆,如果你想要一个最小堆,或者基于某个复杂逻辑的优先级队列,就必须提供自定义比较器。</li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::set</pre>
登录后复制
</div>和<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::map</pre>
登录后复制
</div>的键值排序</strong>:如果你想用自定义对象作为<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::set</pre>
登录后复制
</div>的元素或<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::map</pre>
登录后复制
</div>的键,并且希望它们按照特定规则排序,那么自定义比较器是必不可少的。</li> <li> <strong>性能优化或特定算法需求</strong>:在某些情况下,你可能需要一个更高效的比较函数,或者某个算法(如<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::nth_element</pre>
登录后复制
</div>)需要一个非标准的比较逻辑来找到第k个元素。</li> </ul> <p>从我的经验来看,一旦你的数据结构稍微复杂一点,或者排序需求超出了简单的升序/降序,自定义比较器就会成为你的得力助手。它让代码更清晰,意图更明确,避免了为了排序而修改原始数据结构或创建临时数据结构的麻烦。</p> <h3>C++自定义比较函数中的常见错误有哪些?</h3> <p>自定义比较函数虽然强大,但它有一个非常严格的要求:必须满足“严格弱序”(Strict Weak Ordering, SWO)。这是STL算法和容器能够正确工作的基础。违反SWO是自定义比较器最常见的错误来源,而且往往会导致程序行为异常,从错误结果到崩溃,甚至无限循环都有可能。</p> <p><strong>1. 严格弱序(SWO)的核心原则</strong></p> <p>一个比较函数<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">comp(a, b)</pre>
登录后复制
</div>返回<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">true</pre>
登录后复制
</div>表示<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">a</pre>
登录后复制
</div>在排序上“小于”<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">b</pre>
登录后复制
</div>,它必须满足:</p> <div class="aritcle_card"> <a class="aritcle_card_img" href="/xiazai/code/9844"> <img src="https://img.php.cn/upload/webcode/000/000/007/176009940655088.png" alt="X-Node企业快速建站1.0.6.0801"> </a> <div class="aritcle_card_info"> <a href="/xiazai/code/9844">X-Node企业快速建站1.0.6.0801</a> <p>特色介绍: 1、ASP+XML+XSLT开发,代码、界面、样式全分离,可快速开发 2、支持语言包,支持多模板,ASP文件中无任何HTML or 中文 3、无限级分类,无限级菜单,自由排序 4、自定义版头(用于不规则页面) 5、自动查找无用的上传文件与空目录,并有回收站,可删除、还原、永久删除 6、增强的Cache管理,可单独管理单个Cache 7、以内存和XML做为Cache,兼顾性能与消耗 8、</p> <div class=""> <img src="/static/images/card_xiazai.png" alt="X-Node企业快速建站1.0.6.0801"> <span>0</span> </div> </div> <a href="/xiazai/code/9844" class="aritcle_card_btn"> <span>查看详情</span> <img src="/static/images/cardxiayige-3.png" alt="X-Node企业快速建站1.0.6.0801"> </a> </div> <ul> <li> <strong>非自反性(Irreflexivity)</strong>:<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">comp(a, a)</pre>
登录后复制
</div> 必须始终为 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">false</pre>
登录后复制
</div>。一个元素不能“小于”它自己。</li> <li> <strong>非对称性(Asymmetry)</strong>:如果 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">comp(a, b)</pre>
登录后复制
</div> 为 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">true</pre>
登录后复制
</div>,那么 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">comp(b, a)</pre>
登录后复制
</div> 必须为 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">false</pre>
登录后复制
</div>。</li> <li> <strong>传递性(Transitivity)</strong>:如果 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">comp(a, b)</pre>
登录后复制
</div> 为 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">true</pre>
登录后复制
</div> 且 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">comp(b, c)</pre>
登录后复制
</div> 为 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">true</pre>
登录后复制
</div>,那么 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">comp(a, c)</pre>
登录后复制
</div> 也必须为 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">true</pre>
登录后复制
</div>。</li> <li> <strong>等价性(Equivalence)</strong>:如果 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">comp(a, b)</pre>
登录后复制
</div> 为 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">false</pre>
登录后复制
</div> 且 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">comp(b, a)</pre>
登录后复制
</div> 也为 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">false</pre>
登录后复制
</div>,那么 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">a</pre>
登录后复制
</div> 和 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">b</pre>
登录后复制
</div> 被认为是等价的。这种等价关系也必须是传递的:如果 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">a</pre>
登录后复制
</div> 等价于 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">b</pre>
登录后复制
</div> 且 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">b</pre>
登录后复制
</div> 等价于 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">c</pre>
登录后复制
</div>,那么 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">a</pre>
登录后复制
</div> 也必须等价于 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">c</pre>
登录后复制
</div>。</li> </ul> <p><strong>常见的SWO违反错误:</strong></p> <ul> <li> <strong>使用<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;"><=</pre>
登录后复制
</div>而不是<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;"><</pre>
登录后复制
</div>(或<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">>=</pre>
登录后复制
</div>而不是<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">></pre>
登录后复制
</div>)</strong>: 这是最典型的错误。例如,如果你写 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">return a.value <= b.value;</pre>
登录后复制
</div>。<ul> <li><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">comp(a, a)</pre>
登录后复制
</div> 会返回 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">true</pre>
登录后复制
</div>(因为 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">a.value <= a.value</pre>
登录后复制
</div>),违反了非自反性。</li> <li>结果:程序可能崩溃,或者进入无限循环,或者排序结果不正确。</li> </ul> </li> <li> <strong>不一致的比较逻辑</strong>: 当比较逻辑涉及多个字段时,很容易出错。 例如,你可能想先按年龄排序,年龄相同再按姓名排序。<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:cpp;toolbar:false;'>// 错误示例:可能违反SWO [](const Person& a, const Person& b) { if (a.age < b.age) return true; if (a.name < b.name) return true; // 这里可能导致问题 return false; }</pre>
登录后复制
</div><p>这个例子的问题在于,如果<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">a.age == b.age</pre>
登录后复制
</div>但<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">a.name > b.name</pre>
登录后复制
</div>,它会返回<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">false</pre>
登录后复制
</div>。如果<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">b.age == c.age</pre>
登录后复制
</div>但<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">b.name > c.name</pre>
登录后复制
</div>,它也会返回<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">false</pre>
登录后复制
</div>。但如果<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">a.age < c.age</pre>
登录后复制
</div>,那么<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">a</pre>
登录后复制
</div>应该小于<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">c</pre>
登录后复制
</div>。但如果<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">a.age == b.age</pre>
登录后复制
</div>且<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">a.name > b.name</pre>
登录后复制
</div>,同时<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">b.age == c.age</pre>
登录后复制
</div>且<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">b.name > c.name</pre>
登录后复制
</div>,那么<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">a</pre>
登录后复制
</div>和<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">b</pre>
登录后复制
</div>、<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">b</pre>
登录后复制
</div>和<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">c</pre>
登录后复制
</div>都被认为是等价的。但<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">a</pre>
登录后复制
</div>和<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">c</pre>
登录后复制
</div>可能并非等价。 正确的写法是:</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:cpp;toolbar:false;'>[](const Person& a, const Person& b) { if (a.age != b.age) { return a.age < b.age; } return a.name < b.name; // 只有当年龄完全相等时才比较姓名 }</pre>
登录后复制
</div><p>这种分层比较的逻辑,确保了只有在更高优先级字段相等时,才进入下一级比较。</p> </li> <li> <strong>比较器捕获了不稳定的状态(针对Lambda或Functor)</strong>: 如果你的Lambda通过引用捕获了外部变量,而这个变量在排序过程中被修改了,或者在Lambda被调用时已经失效,那么比较结果就会变得不稳定和不可预测。<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:cpp;toolbar:false;'>int sort_direction = 1; // 1 for asc, -1 for desc std::sort(vec.begin(), vec.end(), [&](int a, int b) { return (a * sort_direction) < (b * sort_direction); // 错误:sort_direction可能被修改 });</pre>
登录后复制
</div><p>应该使用按值捕获<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">[=]</pre>
登录后复制
</div>或者将<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">sort_direction</pre>
登录后复制
</div>作为<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">const</pre>
登录后复制
</div>引用捕获。</p> </li> <li> <strong>性能问题</strong>: 比较函数如果执行了复杂的操作(如字符串拷贝、大量计算、IO操作),会显著拖慢排序速度,因为比较操作在排序算法中会被频繁调用。尽量保持比较函数简洁高效。</li> <li> <strong>对<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">const</pre>
登录后复制
</div>的误解</strong>: 函数对象中的<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">operator()</pre>
登录后复制
</div>通常应该声明为<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">const</pre>
登录后复制
</div>,因为它不应该修改对象的状态。如果它修改了,并且这个函数对象被拷贝(例如在<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::set</pre>
登录后复制
</div>或<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::map</pre>
登录后复制
</div>中),那么每个拷贝可能都有不同的状态,导致行为异常。</li> </ul> <p>我个人在调试这类问题时,通常会先检查比较函数是否满足<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">comp(a, a)</pre>
登录后复制
</div>为<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">false</pre>
登录后复制
</div>。如果不是,那几乎可以肯定就是<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;"><=</pre>
登录后复制
</div>之类的符号用错了。然后,对于复杂的多条件排序,我会仔细检查每一层逻辑,确保它们是互斥且递进的。一旦SWO被破坏,STL的行为是未定义的,任何奇怪的事情都可能发生。</p> <h3>除了std::sort,哪些C++ STL容器和算法支持自定义排序?</h3> <p>STL的强大之处在于其一致性和通用性。一旦你理解了自定义比较器的概念,你会发现它在许多地方都能派上用场,远不止<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::sort</pre>
登录后复制
</div>。它允许我们对STL的各种组件进行精细控制,使其适应我们独特的数据组织需求。</p> <p><strong>1. 有序关联容器:<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::set</pre>
登录后复制
</div> 和 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::map</pre>
登录后复制
</div></strong></p> <p>这些容器内部使用红黑树实现,它们的元素或键是自动排序的。默认情况下,它们使用<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::less<Key></pre>
登录后复制
</div>(也就是<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;"><</pre>
登录后复制
</div>操作符)来比较键。如果你想改变这个排序规则,就需要在模板参数中提供你的自定义比较器。</p> <ul> <li> <p><strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::set</pre>
登录后复制
</div></strong>:存储唯一且已排序的元素。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:cpp;toolbar:false;'>#include <set> #include <string> #include <iostream> struct CustomStringCompare { bool operator()(const std::string& a, const std::string& b) const { // 按字符串长度降序,长度相同则按字典序升序 if (a.length() != b.length()) { return a.length() > b.length(); // 注意这里是 > } return a < b; } }; int main() { std::set<std::string, CustomStringCompare> mySet; mySet.insert("apple"); mySet.insert("banana"); mySet.insert("cat"); mySet.insert("dog"); mySet.insert("elephant"); for (const auto& s : mySet) { std::cout << s << std::endl; } // 输出可能为:elephant, banana, apple, dog, cat (长度降序,同长度字典序) return 0; }</pre>
登录后复制
</div></li> <li> <p><strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::map</pre>
登录后复制
</div></strong>:存储<a style="color:#f60; text-decoration:underline;" title="键值对" href="https://www.php.cn/zt/49710.html" target="_blank">键值对</a>,键是唯一的且已排序的。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:cpp;toolbar:false;'>#include <map> #include <string> #include <iostream> // 使用上面定义的 CustomStringCompare int main() { std::map<std::string, int, CustomStringCompare> myMap; myMap["apple"] = 1; myMap["banana"] = 2; myMap["cat"] = 3; myMap["dog"] = 4; myMap["elephant"] = 5; for (const auto& pair : myMap) { std::cout << pair.first << ": " << pair.second << std::endl; } return 0; }</pre>
登录后复制
</div><p>需要注意的是,对于<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::set</pre>
登录后复制
</div>和<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::map</pre>
登录后复制
</div>,比较器是作为模板参数传递的,这意味着它在编译时就确定了,并且通常是无状态的(或者状态在构造时确定)。</p> </li> </ul> <p><strong>2. 优先级队列:<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::priority_queue</pre>
登录后复制
</div></strong></p> <p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::priority_queue</pre>
登录后复制
</div>默认是一个最大堆,也就是说,队首元素是最大的。如果你想要一个最小堆,或者基于自定义优先级的队列,同样需要提供比较器。它的比较器参数是<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">Compare</pre>
登录后复制
</div>,表示“less than”的关系,但实际上它用于构建一个堆,使得“最大”的元素在顶部。所以,如果你想让“最小”的元素在顶部(最小堆),你的比较器应该定义“大于”的关系。</p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:cpp;toolbar:false;'>#include <queue> #include <vector> #include <iostream> struct Task { std::string name; int priority; // 越小优先级越高 }; // 自定义比较器:让优先级小的元素排在前面(即“更大”),实现最小堆 struct CompareTasks { bool operator()(const Task& a, const Task& b) const { return a.priority > b.priority; // 如果 a 的优先级数字更大,那么 a 优先级更低,排在后面 } }; int main() { std::priority_queue<Task, std::vector<Task>, CompareTasks> taskQueue; taskQueue.push({"High importance", 1}); taskQueue.push({"Medium importance", 2}); taskQueue.push({"Low importance", 3}); taskQueue.push({"Urgent", 0}); while (!taskQueue.empty()) { std::cout << "Processing task: " << taskQueue.top().name << " (Priority: " << taskQueue.top().priority << ")" << std::endl; taskQueue.pop(); } // 输出顺序:Urgent, High importance, Medium importance, Low importance return 0; }</pre>
登录后复制
</div><p><strong>3. 其他算法</strong></p> <p>除了<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::sort</pre>
登录后复制
</div>,许多其他STL算法也接受自定义比较器,例如:</p> <ul> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::min_element</pre>
登录后复制
</div>, <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::max_element</pre>
登录后复制
</div></strong>:查找序列中的最小/最大元素。<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:cpp;toolbar:false;'>// 查找年龄最大的Person auto oldest = std::max_element(people.begin(), people.end(), [](const Person& a, const Person& b) { return a.age < b.age; // 返回年龄较小的那个 }); std::cout << "Oldest person: " << oldest->name << std::endl;</pre>
登录后复制
</div></li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::nth_element</pre>
登录后复制
</div></strong>:将序列重新排列,使得第n个元素是如果整个序列被排序后,它将位于的位置。它也会把所有“小于”它的元素放在它前面,所有“大于”它的元素放在它后面。<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class='brush:cpp;toolbar:false;'>// 找到年龄第三大的Person(即按年龄降序排列后的第3个) // 注意:这里的比较器和std::sort一样,是定义“小于”的关系 // 所以要找第三大,相当于找按升序排列后的倒数第三个,或者写一个降序比较器 std::nth_element(people.begin(), people.begin() + 2, people.end(), [](const Person& a, const Person& b) { return a.age > b.age; // 降序比较器,找第3大的 }); std::cout << "Third oldest person: " << people[2].name << std::endl;</pre>
登录后复制
</div></li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::stable_sort</pre>
登录后复制
</div></strong>:与<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::sort</pre>
登录后复制
</div>类似,但保证相等元素的相对顺序不变。同样接受自定义比较器。</li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::partial_sort</pre>
登录后复制
</div></strong>:对序列的一部分进行排序。</li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::is_sorted</pre>
登录后复制
</div></strong>:检查序列是否已排序。</li> <li> <strong><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false;">std::merge</pre>
登录后复制
</div></strong>:合并两个已排序的序列。</li> </ul> <p>可以说,任何涉及“比较”操作的STL算法或容器,都有可能提供自定义比较器的接口。掌握了这一核心模式,你就能更灵活、更高效地利用C++标准库来解决各种实际问题。它确实是C++泛型编程思想的一个绝佳体现。</p>

以上就是C++如何在STL中使用自定义排序规则的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号