承德住房和城乡建设局网站关闭了惠州网站策划建设
2026/6/20 8:05:13 网站建设 项目流程
承德住房和城乡建设局网站关闭了,惠州网站策划建设,广州网站推广找哪家,互联网培训C multiset 全面解析与实战指南 在C标准模板库#xff08;STL#xff09;的关联容器中#xff0c;multiset是一种支持元素重复存储的有序集合。它与基础的set容器核心逻辑一致#xff0c;均基于红黑树#xff08;自平衡二叉搜索树#xff09;实现#xff0c;保证了元素的…C multiset 全面解析与实战指南在C标准模板库STL的关联容器中multiset是一种支持元素重复存储的有序集合。它与基础的set容器核心逻辑一致均基于红黑树自平衡二叉搜索树实现保证了元素的有序性和高效的增删查操作但区别于set的“元素唯一性”限制multiset允许相同值的元素共存这使其在处理需要存储重复数据且需有序排列的场景时极具优势。本文将从底层原理出发详细拆解multiset的核心特性、常用接口结合实战案例演示具体用法并对比set明确适用边界帮助大家彻底掌握这一实用容器。一、multiset 核心原理与特性要理解multiset的行为逻辑首先需要明确其底层实现和核心特性这是正确使用它的基础。1.1 底层实现红黑树的支撑multiset与set、map、multimap同属STL关联容器底层均依赖红黑树一种自平衡的二叉搜索树实现。红黑树通过节点颜色红/黑的约束和动态旋转操作确保树的高度始终维持在O(log n)级别从而保证了插入、删除、查找等核心操作的时间复杂度稳定为O(log n)。对于multiset而言红黑树的“二叉搜索树特性”左子树节点值 ≤ 父节点值 ≤ 右子树节点值支持重复元素是其“有序性”和“重复存储”的核心保障。当插入新元素时红黑树会根据元素值找到合适的插入位置维持整体有序当删除元素时会通过旋转调整保持树的平衡确保后续操作效率。1.2 核心特性总结有序性元素默认按升序排列可通过自定义比较函数修改排序规则允许重复支持存储多个值相同的元素无“唯一性”限制高效操作插入、删除、查找的时间复杂度均为O(log n)效率稳定不可直接修改元素元素是排序的依据直接修改会破坏红黑树的有序性需先删除旧元素再插入新元素迭代器特性支持双向迭代器可/–遍历不支持随机访问插入/删除元素时除指向被删除元素的迭代器外其他迭代器均不失效无下标访问因元素有序但无索引概念不支持operator[]和at()接口。1.3 与set的核心区别multiset与set的底层实现完全一致核心差异仅在于对“元素唯一性”的要求这也导致了两者接口和用法的细微区别。通过下表可快速区分特性/接口setmultiset元素唯一性不允许重复元素插入重复元素会失败允许重复元素插入重复元素始终成功insert()返回值返回pairiterator, boolbool标记插入是否成功仅返回插入位置的迭代器插入必成功查找与删除find()返回唯一匹配元素的迭代器erase(val)删除唯一匹配元素find()返回第一个匹配元素的迭代器erase(val)删除所有匹配元素核心适用场景存储不重复的有序数据如去重、判重存储可重复的有序数据如统计频率、保留重复序列二、C multiset 常用接口详解使用multiset前需包含头文件set与set共用头文件并使用std命名空间或显式指定std::multiset。multiset的接口与set大部分一致以下重点讲解核心接口及多元素场景下的特殊用法。2.1 构造与析构接口原型功能说明示例multiset();默认构造创建空multiset默认升序std::multiset ms;multiset(const Compare comp);自定义比较函数构造空multiset如降序std::multisetint, std::greater ms_desc;multiset(InputIterator first, InputIterator last);迭代器构造拷贝[first, last)区间的元素std::vector vec{1,2,2,3}; std::multiset ms(vec.begin(), vec.end());multiset(const multiset other);拷贝构造函数std::multiset ms2(ms);~multiset();析构函数释放红黑树所有节点资源-2.2 迭代器相关multiset的迭代器用于遍历有序元素核心接口与其他关联容器一致接口功能说明begin() / end()返回指向第一个元素/最后一个元素下一个位置的迭代器非constrbegin() / rend()返回指向最后一个元素/第一个元素前一个位置的反向迭代器逆序遍历cbegin() / cend()返回const迭代器不可修改元素避免破坏有序性迭代器遍历示例#includeset#includeiostreamusingnamespacestd;intmain(){// 构造multiset包含重复元素默认升序multisetintms{2,1,3,2,4,1};// 正向遍历升序cout正向遍历升序;for(autoitms.begin();it!ms.end();it){cout*it ;// 输出1 1 2 2 3 4}coutendl;// 反向遍历降序cout反向遍历降序;for(autoitms.rbegin();it!ms.rend();it){cout*it ;// 输出4 3 2 2 1 1}coutendl;// 自定义降序的multisetmultisetint,greaterintms_desc{2,1,3,2,4,1};cout自定义降序遍历;for(autox:ms_desc){coutx ;// 输出4 3 2 2 1 1}coutendl;return0;}2.3 容量相关接口功能说明size()返回当前元素的个数包含重复元素empty()判断容器是否为空空返回true否则返回falsemax_size()返回容器可容纳的最大元素个数受系统内存限制2.4 插入与删除核心操作multiset的插入操作因支持重复元素接口返回值与set不同删除操作可按元素值、迭代器删除灵活性较高。接口功能说明时间复杂度insert(const value_type val)插入元素val返回插入位置的迭代器插入必成功O(log n)insert(InputIterator first, InputIterator last)插入[first, last)区间的所有元素O(k log(nk))k为区间元素个数emplace(Args… args)直接构造元素避免拷贝效率高于insertO(log n)erase(iterator pos)删除迭代器pos指向的元素返回下一个元素的迭代器O(log n)erase(const value_type val)删除所有值为val的元素返回删除的元素个数O(log n k)k为匹配元素个数erase(iterator first, iterator last)删除[first, last)区间的元素返回下一个元素的迭代器O(log n k)k为区间元素个数clear()清空容器删除所有元素size变为0O(n)插入与删除示例#includeset#includeiostreamusingnamespacestd;intmain(){multisetintms;// 1. 插入单个元素ms.insert(2);ms.insert(2);// 插入重复元素成功ms.emplace(1);// emplace直接构造效率更高cout插入后sizems.size()endl;// 输出3cout插入后元素;for(autox:ms)coutx ;// 输出1 2 2coutendl;// 2. 插入区间元素intarr[]{3,2,4};ms.insert(arr,arr3);cout插入区间后元素;for(autox:ms)coutx ;// 输出1 2 2 2 3 4coutendl;// 3. 按元素值删除删除所有2size_t delCountms.erase(2);cout删除元素2的个数delCountendl;// 输出3cout删除后元素;for(autox:ms)coutx ;// 输出1 3 4coutendl;// 4. 按迭代器删除删除第一个元素autoitms.begin();ms.erase(it);cout按迭代器删除后元素;for(autox:ms)coutx ;// 输出3 4coutendl;// 5. 清空容器ms.clear();coutclear后emptyms.empty()endl;// 输出1truereturn0;}2.5 查找与统计核心操作因multiset支持重复元素查找操作除了常规的find()还提供了专门用于定位“重复元素区间”的接口lower_bound()、upper_bound()、equal_range()这是处理重复元素的核心关键。接口功能说明返回值find(const value_type val)查找值为val的第一个元素匹配元素的迭代器若无匹配返回end()count(const value_type val)统计值为val的元素个数匹配元素的个数无匹配返回0lower_bound(const value_type val)查找第一个值 ≥ val的元素对应迭代器若无则返回end()upper_bound(const value_type val)查找第一个值 val的元素对应迭代器若无则返回end()equal_range(const value_type val)获取所有值 val的元素区间左闭右开pairiterator, iteratorfirstlower_bound(val)secondupper_bound(val)查找与统计示例核心重点#includeset#includeiostreamusingnamespacestd;intmain(){multisetintms{1,2,2,3,2,4,1};// 1. 查找第一个值为2的元素autofindItms.find(2);if(findIt!ms.end()){cout第一个值为2的元素*findItendl;// 输出2}// 2. 统计值为2的元素个数size_t countms.count(2);cout值为2的元素个数countendl;// 输出3// 3. 用lower_bound和upper_bound遍历所有值为2的元素autolowItms.lower_bound(2);autoupItms.upper_bound(2);coutlower_bound upper_bound遍历值为2的元素;for(autoitlowIt;it!upIt;it){cout*it ;// 输出2 2 2}coutendl;// 4. 用equal_range遍历所有值为2的元素更简洁autorangems.equal_range(2);coutequal_range遍历值为2的元素;for(autoitrange.first;it!range.second;it){cout*it ;// 输出2 2 2}coutendl;// 5. 查找不存在的元素autonoItms.find(5);if(noItms.end()){cout值为5的元素不存在endl;}return0;}三、multiset 实战案例multiset的核心优势是“有序存储支持重复”以下通过两个经典场景演示其实际应用3.1 场景1统计数组元素出现频率并排序需求给定一个整数数组统计每个元素的出现次数并按元素值升序输出结果。#includeset#includeiostream#includevectorusingnamespacestd;voidcountAndSortElements(constvectorintarr){// 1. 插入数组元素到multiset自动排序保留重复multisetintms(arr.begin(),arr.end());// 2. 遍历统计每个元素的出现次数autoitms.begin();while(it!ms.end()){intval*it;// 统计当前元素的个数size_t countms.count(val);// 输出结果cout元素 val 出现次数countendl;// 跳过当前元素的所有重复项itms.upper_bound(val);}}intmain(){vectorintarr{3,1,4,1,5,9,2,6,5,3,5};cout数组元素频率统计按元素升序endl;countAndSortElements(arr);return0;}输出结果数组元素频率统计按元素升序 元素 1 出现次数2 元素 2 出现次数1 元素 3 出现次数2 元素 4 出现次数1 元素 5 出现次数3 元素 6 出现次数1 元素 9 出现次数13.2 场景2维护一个有序的数据流支持快速查询中位数需求设计一个数据结构支持向数据流中插入整数并能快速查询当前数据流的中位数。中位数是有序列表中间的数若列表长度为偶数取中间两个数的平均值。思路利用两个multiset构建“双堆”结构——左半部分大根堆存储较小的元素右半部分小根堆存储较大的元素确保左半部分元素个数≥右半部分最多相差1。此时若元素总数为奇数左半部分的最大值大根堆顶即为中位数若元素总数为偶数中位数为左半部分最大值与右半部分最小值的平均值。#includeset#includeiostream#includealgorithmusingnamespacestd;classMedianFinder{private:// 左半部分大根堆用greater排序最大元素在末尾multisetint,greaterintleft;// 右半部分小根堆默认升序最小元素在开头multisetintright;public:// 插入元素voidaddNum(intnum){// 先插入左半部分再调整平衡left.insert(num);// 确保左半部分的最大元素 ≤ 右半部分的最小元素if(!left.empty()!right.empty()*left.begin()*right.begin()){intval*left.begin();left.erase(left.begin());right.insert(val);}// 确保左半部分元素个数最多比右半部分多1if(left.size()right.size()1){intval*left.begin();left.erase(left.begin());right.insert(val);}// 确保右半部分元素个数不超过左半部分if(right.size()left.size()){intval*right.begin();right.erase(right.begin());left.insert(val);}}// 查询中位数doublefindMedian(){if(left.size()right.size()){// 奇数个元素左半部分最大值为中位数return*left.begin();}else{// 偶数个元素取两部分顶元素的平均值return(*left.begin()*right.begin())/2.0;}}};intmain(){MedianFinder mf;mf.addNum(1);mf.addNum(2);cout当前中位数mf.findMedian()endl;// 输出1.5mf.addNum(3);cout当前中位数mf.findMedian()endl;// 输出2.0mf.addNum(4);cout当前中位数mf.findMedian()endl;// 输出2.5return0;}四、multiset 使用注意事项禁止直接修改元素multiset的元素是红黑树排序的依据直接修改元素值会破坏树的有序性导致容器行为异常。若需修改元素必须先删除旧元素erase()再插入新元素insert()。自定义比较函数的规则自定义排序时比较函数必须满足“严格弱序”即不可传递的等价关系。例如自定义降序使用std::greater若自定义函数需确保对于任意a、b!comp(a,b) !comp(b,a) 表示a和b等价可共存。迭代器失效问题插入元素时红黑树的旋转调整不会导致迭代器失效删除元素时只有指向被删除元素的迭代器失效其他迭代器包括指向其他元素的迭代器、begin()、end()等均有效。效率对比与选择若需存储不重复的有序数据优先使用setset的查找、插入效率略高于multiset因无需处理重复元素的冗余逻辑若需存储重复的有序数据优先使用multiset若无需有序可使用unordered_multiset哈希表实现平均插入/查找效率O(1)但无序。处理重复元素的最佳实践遍历某一重复元素的所有实例时优先使用equal_range()接口其效率高于“find() 循环计数”且代码更简洁。线程安全性与所有STL容器一致multiset不保证线程安全。多线程环境下并发读写时需手动加锁如使用std::mutex保护容器操作。五、总结multiset是C STL中用于存储“有序重复元素”的关联容器底层基于红黑树实现兼具有序性和高效操作的特性。其核心优势在于支持重复元素的有序存储通过equal_range()等接口可便捷地处理重复元素的遍历与统计特别适合频率统计、有序数据流维护等场景。使用时需注意禁止直接修改元素、不支持下标访问若无需重复元素应优先选择set。掌握multiset的核心接口尤其是equal_range()和适用场景能帮助我们在处理有序重复数据时写出更高效、简洁的代码。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询