C++ set和multiset的使用小結(jié)
1. 序列式容器和關(guān)聯(lián)式容器(了解)
前面我們已經(jīng)接觸過(guò)STL中的部分容器如:string、vector、list、deque、array、forward_list等,這些容器統(tǒng)稱(chēng)為序列式容器,因?yàn)檫壿嫿Y(jié)構(gòu)為線性序列的數(shù)據(jù)結(jié)構(gòu),兩個(gè)位置存儲(chǔ)的值之間一般沒(méi)有緊密的關(guān)聯(lián)關(guān)系,比如交換一下,他依舊是序列式容器。順序容器中的元素是按他們?cè)谌萜髦械拇鎯?chǔ)位置來(lái)順序保存和訪問(wèn)的。
關(guān)聯(lián)式容器也是用來(lái)存儲(chǔ)數(shù)據(jù)的,與序列式容器不同的是,關(guān)聯(lián)式容器邏輯結(jié)構(gòu)通常是非線性結(jié)構(gòu),兩個(gè)位置有緊密的關(guān)聯(lián)關(guān)系,交換一下,他的存儲(chǔ)結(jié)構(gòu)就被破壞了。順序容器中的元素是按關(guān)鍵字來(lái)保存和訪問(wèn)的。關(guān)聯(lián)式容器有map/set系列和unordered_map/unordered_set系列。
map和set底層是紅黑樹(shù),紅黑樹(shù)是一顆平衡二叉搜索樹(shù)。set是key搜索場(chǎng)景的結(jié)構(gòu),map是key/value搜索場(chǎng)景的結(jié)構(gòu)。
說(shuō)人話 就是map set的值不能改 改了結(jié)構(gòu)會(huì)被破壞。
2. set系列的使用
2.1 set類(lèi)的介紹
set的聲明如下,T就是set底層關(guān)鍵字的類(lèi)型set默認(rèn)要求T支持小于比較,如果不支持或者想按自己的需求走可以自行實(shí)現(xiàn)仿函數(shù)傳給第二個(gè)模版參數(shù)。set底層存儲(chǔ)數(shù)據(jù)的內(nèi)存是從空間配置器申請(qǐng)的,如果需要可以自己實(shí)現(xiàn)內(nèi)存池,傳給第三個(gè)參數(shù)。- 一般情況下,我們都不需要傳后兩個(gè)模版參數(shù)。
set底層是用紅黑樹(shù)實(shí)現(xiàn),增刪查效率是 O ( l o g N ) O(logN) O(logN),迭代器遍歷是走的搜索樹(shù)的中序,所以是有序的。- 與
vector/list等容器的使用,STL容器接口設(shè)計(jì),高度相似,所以這里我們就不再一個(gè)接口一個(gè)接口的介紹,挑比較重要的接口進(jìn)行介紹。

2.2 set的構(gòu)造和迭代器
0.構(gòu)造:
set 的支持正向和反向迭代遍歷,遍歷默認(rèn)按升序順序,因?yàn)榈讓邮嵌嫠阉鳂?shù),迭代器遍歷走的中序;支持迭代器就意味著支持范圍 for,set 的 iterator 和 const_iterator 都不支持迭代器修改數(shù)據(jù),修改關(guān)鍵字?jǐn)?shù)據(jù),防止破壞底層搜索樹(shù)的結(jié)構(gòu)。

1. 空構(gòu)造(empty (1))
explicit set (const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());
- 作用:創(chuàng)建空的
set容器。 - 參數(shù):
comp:可選,自定義的鍵比較規(guī)則(默認(rèn)使用key_compare,即<比較);alloc:可選,內(nèi)存分配器(默認(rèn)使用allocator_type)。
2. 范圍構(gòu)造(range (2))
template <class InputIterator>
set (InputIterator first, InputIterator last,
const key_compare& comp = key_compare(),
const allocator_type& alloc = allocator_type());
- 作用:將迭代器
[first, last)范圍內(nèi)的元素插入set(自動(dòng)去重并按規(guī)則排序)。 - 參數(shù):
first/last:輸入迭代器,指定待插入元素的范圍;comp/alloc:同空構(gòu)造的可選參數(shù)。
3. 拷貝構(gòu)造(copy (3))
set (const set& x);
- 作用:創(chuàng)建一個(gè)與已有
set對(duì)象x內(nèi)容完全相同的新set。
4. 初始化列表(C++11)
void test_set1()
{
set<int> s = { 5,1,5,3,4,2,6,83,9,10,22 };
// 中序,排序+去重
set<int>::iterator it = s.begin();
while (it != s.end())
{
// 普通迭代器也不支持修改
// *it = 1;
cout << *it << " ";
++it;
}
cout << endl;
}

2.3 修改器(Modifiers)的成員函數(shù)

這是C++ std::set的修改器(Modifiers)成員函數(shù),負(fù)責(zé)對(duì)set的元素進(jìn)行增刪等操作。以下結(jié)合代碼示例逐一講解:
0. 迭代器

這個(gè)太基礎(chǔ)了 我個(gè)人感覺(jué)實(shí)在沒(méi)什么可以說(shuō)的 唯一要注意的就是 不能通過(guò)迭代器修改里面的值。
1. insert:插入元素
功能:向set中插入鍵值(自動(dòng)去重、按規(guī)則排序)。
代碼示例:
#include <set>
#include <iostream>
using namespace std;
int main() {
set<int> s;
// 插入單個(gè)元素
s.insert(3);
s.insert(1);
s.insert(2);
s.insert(2); // 重復(fù)元素,插入失?。╯et自動(dòng)去重)
// 遍歷輸出:1 2 3(默認(rèn)升序)
for (int val : s) cout << val << " ";
return 0;
}
2. erase:刪除元素
功能:刪除set中的元素(支持按鍵值、迭代器、范圍刪除)。
代碼示例:
int main() {
set<int> s = {1,2,3,4,5};
// 1. 按鍵值刪除
s.erase(3);
// 2. 按迭代器刪除
auto it = s.find(4);
if (it != s.end()) s.erase(it);
// 3. 按范圍刪除(刪除[begin, end))
s.erase(s.begin(), s.end()); //左閉右開(kāi)
cout << s.size(); // 輸出0
return 0;
}
3. swap:交換兩個(gè)set的內(nèi)容(與算法庫(kù)swap對(duì)比)
功能:交換當(dāng)前set與另一個(gè)set的所有元素(底層僅交換內(nèi)部指針,效率高,而算法庫(kù)中swap則涉及深層拷貝等)。
代碼示例:
int main() {
set<int> s1 = {1,2,3};
set<int> s2 = {4,5,6};
s1.swap(s2);
// s1變?yōu)閧4,5,6},s2變?yōu)閧1,2,3}
for (int val : s1) cout << val << " "; // 輸出4 5 6
return 0;
}
4. clear:清空所有元素
功能:刪除set中的所有元素,使其變?yōu)榭杖萜鳌?br />代碼示例:
int main() {
set<int> s = {1,2,3};
s.clear();
cout << s.empty(); // 輸出1(表示容器為空)
return 0;
}
5. emplace:構(gòu)造并插入元素(C++11+)
功能:直接在set中構(gòu)造元素(避免臨時(shí)對(duì)象拷貝,比insert更高效)。
代碼示例:
int main() {
set<pair<int, string>> s;
// emplace直接構(gòu)造pair(無(wú)需手動(dòng)創(chuàng)建臨時(shí)pair)
s.emplace(1, "apple");
// 等價(jià)于insert,但emplace更高效
s.insert(pair<int, string>(2, "banana"));
return 0;
}
6. emplace_hint:帶位置提示的構(gòu)造插入(C++11+)
功能:在指定迭代器位置附近構(gòu)造并插入元素(若位置合理,可提升插入效率)。
代碼示例:
int main() {
set<int> s = {1,3,5};
// 提示在3的位置附近插入2(實(shí)際插入到1和3之間)
auto it = s.find(3);
s.emplace_hint(it, 2);
for (int val : s) cout << val << " "; // 輸出1 2 3 5
return 0;
}
2.4 find(與算法庫(kù)find的對(duì)比)

這是C++標(biāo)準(zhǔn)庫(kù)中std::set::find成員函數(shù)的聲明(支持C++98及以上版本),其核心信息與使用說(shuō)明如下:
iterator find (const value_type& val) const;
- 返回值:
iterator(set的迭代器),指向找到的鍵值val;若val不存在,返回set::end()(尾后迭代器)。 - 參數(shù):
const value_type& val,待查找的鍵值(value_type即set的關(guān)鍵字類(lèi)型)。 - 特性:
const修飾表示該函數(shù)不會(huì)修改set本身。
find是set的查找接口,基于底層紅黑樹(shù)的特性,能以 O ( log ? N ) O(\log N) O(logN)的時(shí)間復(fù)雜度快速定位鍵值,常用于判斷元素是否存在、獲取元素迭代器。
- 效率:由于
set底層是有序的紅黑樹(shù),find通過(guò)二分查找邏輯實(shí)現(xiàn),效率遠(yuǎn)高于算法庫(kù)的find( O ( N ) O(N) O(N))。 - 迭代器特性:
set的迭代器是雙向迭代器,且不可修改(因?yàn)樾薷逆I值會(huì)破壞set的有序性)。
#include <set>
#include <iostream>
using namespace std;
int main() {
set<int> s = {1, 2, 3, 4, 5};
// 查找鍵值3
auto it = s.find(3);
if (it != s.end()) {
cout << "找到元素:" << *it << endl; // 輸出“找到元素:3”
}
// 查找不存在的鍵值6
it = s.find(6);
if (it == s.end()) {
cout << "未找到元素" << endl; // 輸出“未找到元素”
}
return 0;
}
2.5 key_comp && value_comp
| 函數(shù)名 | 功能描述 |
|---|---|
| key_comp | 返回set用于比較**鍵(key)**的函數(shù)對(duì)象,是set模板參數(shù)中指定的比較類(lèi)型(默認(rèn)是less<key_type>)。 |
| value_comp | 功能與key_comp一致(因?yàn)閟et的鍵和值是同一類(lèi)型),返回的比較對(duì)象邏輯等同于key_comp。 |
set的底層紅黑樹(shù)依賴(lài)比較規(guī)則維持有序性,這兩個(gè)函數(shù)可以獲取當(dāng)前set使用的比較邏輯,常用于:
- 自定義比較規(guī)則時(shí),驗(yàn)證或復(fù)用set的排序邏輯;
- 對(duì)set的元素進(jìn)行外部排序(保持與set內(nèi)部一致的規(guī)則)。
代碼示例
#include <set>
#include <iostream>
using namespace std;
int main() {
// 定義一個(gè)按降序排序的set
set<int, greater<int>> s = {3, 1, 2};
// 獲取key_comp比較對(duì)象
auto comp = s.key_comp();
// 使用comp判斷兩個(gè)鍵的大小關(guān)系(符合set的降序規(guī)則)
bool res = comp(1, 2); // 等價(jià)于greater<int>()(1,2),結(jié)果為false
cout << "1 > 2 ? " << boolalpha << res << endl; // 輸出“1 > 2 ? false”
return 0;
}
2.6 count(與find比較)
- 聲明:
size_type count (const value_type& val) const; - 功能:統(tǒng)計(jì)
set中值為val的元素個(gè)數(shù)(由于set不允許重復(fù)元素,返回值只能是0或1)。 - 參數(shù):
const value_type& val,待統(tǒng)計(jì)的目標(biāo)值; - 返回值:
size_type(無(wú)符號(hào)整數(shù)類(lèi)型),表示val在set中的出現(xiàn)次數(shù)。
由于set的“唯一性”特性,count的實(shí)際作用是判斷元素是否存在(返回1表示存在,0表示不存在),效果等價(jià)于find(val) != end(),但語(yǔ)義更偏向“計(jì)數(shù)”。
#include <set>
#include <iostream>
using namespace std;
int main() {
set<int> s = {1, 2, 3, 4};
// 統(tǒng)計(jì)存在的元素
size_t cnt1 = s.count(3);
cout << "元素3的出現(xiàn)次數(shù):" << cnt1 << endl; // 輸出1
// 統(tǒng)計(jì)不存在的元素
size_t cnt2 = s.count(5);
cout << "元素5的出現(xiàn)次數(shù):" << cnt2 << endl; // 輸出0
return 0;
}
與find的區(qū)別
| 函數(shù) | 功能 | 返回值類(lèi)型 | 適用場(chǎng)景 |
|---|---|---|---|
| find | 查找元素并返回迭代器 | 迭代器 | 需要獲取元素的位置時(shí) |
| count | 統(tǒng)計(jì)元素出現(xiàn)次數(shù) | 無(wú)符號(hào)整數(shù) | 僅需判斷元素是否存在時(shí) |
綜合對(duì)比 在判斷元素是否存在時(shí) count 更加方便!
2.7 lower_bound && upper_bound
| 函數(shù)名 | 功能描述 |
|---|---|
| lower_bound | 返回指向**第一個(gè)不小于(≥)目標(biāo)值val**的元素的迭代器;若所有元素都小于val,返回end()。 |
| upper_bound | 返回指向**第一個(gè)大于(>)目標(biāo)值val**的元素的迭代器;若所有元素都不大于val,返回end()。 |
由于set是有序容器(默認(rèn)升序),這兩個(gè)函數(shù)通過(guò)二分查找(時(shí)間復(fù)雜度 O ( log ? N ) O(\log N) O(logN))快速定位邊界,常用于獲取“等于val的元素區(qū)間”([lower_bound, upper_bound))。
#include <set>
#include <iostream>
using namespace std;
int main() {
set<int> s = {1, 3, 5, 7, 9};
int val = 5;
// 獲取lower_bound:第一個(gè)≥5的元素(即5)
auto lb = s.lower_bound(val);
cout << "lower_bound(" << val << "): " << *lb << endl; // 輸出5
// 獲取upper_bound:第一個(gè)>5的元素(即7)
auto ub = s.upper_bound(val);
cout << "upper_bound(" << val << "): " << *ub << endl; // 輸出7
// 若val不存在(如val=4)
val = 4;
lb = s.lower_bound(val); // 第一個(gè)≥4的元素是5
ub = s.upper_bound(val); // 第一個(gè)>4的元素是5
cout << "val=4時(shí),[lb, ub)區(qū)間長(zhǎng)度:" << distance(lb, ub) << endl; // 輸出0(無(wú)元素)
return 0;
}
用處: 刪除或者遍歷 某一區(qū)間的值:

3. multiset
multiset和set,核心差異集中在元素唯一性、接口行為、使用場(chǎng)景三個(gè)維度,但是multiset不用單獨(dú)包含頭文件,二者基本完全類(lèi)似。
3.1 核心特性差異
| 維度 | set | multiset |
|---|---|---|
| 元素唯一性 | 不允許重復(fù)元素(鍵唯一) | 允許重復(fù)元素(鍵可重復(fù)) |
| 底層實(shí)現(xiàn) | 紅黑樹(shù)(平衡二叉搜索樹(shù)) | 紅黑樹(shù)(平衡二叉搜索樹(shù)) |
| 有序性 | 元素按鍵有序排列 | 元素按鍵有序排列 |
3.2 接口行為差異
以常用成員函數(shù)為例,兩者行為因“唯一性”產(chǎn)生區(qū)別:
| 接口 | set的行為 | multiset的行為 |
|---|---|---|
| insert | 插入重復(fù)元素時(shí)返回失?。▋H插入一次) | 插入重復(fù)元素時(shí)成功(可插入多次) |
| find | 返回第一個(gè)匹配鍵的迭代器 | 返回第一個(gè)匹配鍵的迭代器(切記是中序遍歷的第一個(gè)) |
| count | 返回0或1(僅表示存在性) | 返回鍵的實(shí)際出現(xiàn)次數(shù) |
| lower_bound/upper_bound | 區(qū)間[lb, ub)長(zhǎng)度最多為1 | 區(qū)間[lb, ub)包含所有匹配鍵的元素 |
| erase | 按鍵刪除時(shí),刪除所有匹配的元素(僅1個(gè)) | 按鍵刪除時(shí),刪除所有匹配的元素(可能多個(gè)) |
3.3 使用場(chǎng)景差異
set:適用于需要唯一鍵的場(chǎng)景(如存儲(chǔ)不重復(fù)的ID、去重后的數(shù)據(jù)集);multiset:適用于需要統(tǒng)計(jì)鍵出現(xiàn)次數(shù)的場(chǎng)景(如統(tǒng)計(jì)單詞頻率、存儲(chǔ)可重復(fù)的有序數(shù)據(jù))。
#include <set>
#include <iostream>
using namespace std;
int main() {
// set:鍵唯一
set<int> s = {1, 2, 2, 3};
cout << "set大小:" << s.size() << endl; // 輸出3(自動(dòng)去重)
// multiset:鍵可重復(fù)
multiset<int> ms = {1, 2, 2, 3};
cout << "multiset大?。? << ms.size() << endl; // 輸出4(保留重復(fù))
// count接口差異
cout << "set中2的數(shù)量:" << s.count(2) << endl; // 輸出1
cout << "multiset中2的數(shù)量:" << ms.count(2) << endl; // 輸出2
return 0;
}
4. 例題部分
4.1 環(huán)形鏈表 II
題目鏈接: 點(diǎn)此跳轉(zhuǎn)


我們之前C語(yǔ)言階段是使用快慢指針完成的 其實(shí)我們可以用ste<Node*> s來(lái)做 遍歷鏈表,每個(gè)節(jié)點(diǎn)是否在s中,不在就插入,在的第一個(gè)點(diǎn)就是入口點(diǎn) , 要說(shuō)這道題唯一的缺陷 就是有 O ( N ) O(N) O(N)的空間復(fù)雜度。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode* head)
{
set<ListNode*> s;
ListNode* tmp=head;
while(tmp!=NULL)
{
if(s.count(tmp)==0)
{
s.insert(tmp);
tmp=tmp->next;
}
else
{
return tmp;
}
}
return NULL;
}
};
4.2 兩個(gè)數(shù)組的交集
題目鏈接: 點(diǎn)此轉(zhuǎn)跳

這題也挺簡(jiǎn)單的 其實(shí)就是拿set去重就可以了 然后用一個(gè)set的count去遍歷另一個(gè)set 把重復(fù)的插入vector即可。
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2)
{
//去重
set<int> s1(nums1.begin(),nums1.end());
set<int> s2(nums2.begin(),nums2.end());
vector<int> v;
for(auto e : s1)
{
if(s2.count(e))
{
v.push_back(e);
}
}
return v;
}
};
補(bǔ)充:
這么做 其實(shí)復(fù)雜度還是高的 因?yàn)?code>count的特性 時(shí)間復(fù)雜度可能要 O ( N ∗ l o g N ) O(N*logN) O(N∗logN)
但是利用雙指針特性就可以把復(fù)雜度壓縮到 O ( N ) O(N) O(N)。這個(gè)方法不光能找交集,也能找差集。

到此這篇關(guān)于C++ set和multiset的使用小結(jié)的文章就介紹到這了,更多相關(guān)C++ set和multiset內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解C++中的vector容器及用迭代器訪問(wèn)vector的方法
使用迭代器iterator可以更方便地解引用和訪問(wèn)成員,當(dāng)然也包括vector中的元素,本文就來(lái)詳解C++中的vector容器及用迭代器訪問(wèn)vector的方法,需要的朋友可以參考下2016-05-05
關(guān)于C++多重繼承下虛表結(jié)構(gòu)的問(wèn)題
這篇文章主要介紹了C++ 多重繼承下虛表結(jié)構(gòu)的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09
linux下實(shí)現(xiàn)的2048游戲示例分享
這篇文章主要介紹了linux下實(shí)現(xiàn)的2048游戲示例,需要的朋友可以參考下2014-04-04
C語(yǔ)言實(shí)現(xiàn)字符串替換的示例代碼
本文主要介紹了C語(yǔ)言實(shí)現(xiàn)字符串替換的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
C++空類(lèi)及沒(méi)有成員變量的類(lèi)的大小實(shí)例分析
這篇文章主要介紹了C++空類(lèi)及沒(méi)有成員變量的類(lèi)的大小,對(duì)于初學(xué)者更好的了解C++的指針及類(lèi)的存儲(chǔ)結(jié)構(gòu)很有幫助,需要的朋友可以參考下2014-07-07
Qt?Creator配置opencv環(huán)境的全過(guò)程記錄
最近在PC端QT下配置opencv,想著以后應(yīng)該會(huì)用到,索性記錄下,這篇文章主要給大家介紹了關(guān)于Qt?Creator配置opencv環(huán)境的相關(guān)資料,需要的朋友可以參考下2022-05-05
基于Matlab LBP實(shí)現(xiàn)植物葉片識(shí)別功能
局部二值模式(LBP)是由Ojala等人于2002年提出,它被用于特征提取,而且提取的特征是圖像的紋理特征。本文將利用Matlab和LBP實(shí)現(xiàn)植物葉片識(shí)別,需要的可以參考一下2022-02-02

