1. 程式人生 > 實用技巧 >C++知識點 STL容器2—set

C++知識點 STL容器2—set

~set~

set可能算是一種比較冷門的STL容器了,

喜歡用它的人覺得set真牛逼

不喜歡它的人覺得set真垃圾

很不幸,我屬於第一種


set作為一種封裝好的資料容器

最吸引人的地方是它的自動排序功能

這也就是說你可以擁有一個實時的排好序的序列

或者可以用一個序列同時實現大根堆和小根堆

時間複雜度和空間都是兩者和的1/2

善於運用set的自動排序特性可以為解題省去不少麻煩

啊就爽,就很爽。

繼承STL容器的傳統

set也有它自己專屬的標頭檔案 <set>

這個標頭檔案內包含兩種容器

set和multiset

set是自動排序還附帶去重的序列(有序集合),其中的元素不可重複

multiset是自動排序不去重的序列(有序多重集),其中可以有許多相同的元素

個人感覺在實際OI問題中比較常用的是multiset這種不去重的

兩者的實現原理都是一顆我不會打的紅黑樹,它們支援的函式基本相同


步入正題

首先我們來看一下怎麼宣告一個set和multiset並往其中插入元素

 #include <iostream>
 #include <set>
 using namespace std;
 struct Node {
       int l, r, val;
 }; 
 bool operator <(const Node &a, const
Node &b) { return a.val < b.val; } int main() { set<int> p;//宣告方式與其他容器並無太大差異 multiset<int> q; set<Node> fuc;//壓入set的東西型別必須定義"<" }

當然,你也可以這麼寫(友元版)

 #include <iostream>
 #include <set>
 using namespace std;
 struct Node {
     int l, r, val;
     
bool friend operator <(const Node &a, const Node &b) {return a.val < b.val;} set<Node> fuc; }; int main() { set<int> p; multiset<int> q; }

這樣就完成了以普通資料型別和結構體的sethemultiset的宣告

同時切記:set和multiset內的資料型別必須已定義小於號

學完了宣告就該學如何往容器內插入元素

(size函式,empty函式,clear函式在set中也有,與在vector中作用無異,故此處不提)

與vector不同,往set內插入元素不需要指定位置

元素進入set後自動根據“<”的定義找到自己的位置

set和multiset都使用insert函式插入元素

時間複雜度為O(logn)

程式碼如下

 #include <iostream>
 #include <set>
 using namespace std;
 int x;
 int main()
 {
     set<int> p; 
     multiset<int> q;
     int n;
     cin>>n;
     for(int i = 1; i <= n; i++)
     {
         cin>>x;
         q.insert(x);
         p.insert(x);
     }
 }

如此即可實現向set p和multiset q中插入n個元素

插入完元素後我們可以對這些元素進行操作

首先需要注意的是set不支援隨機訪問

也就是說不能通過下標的方式訪問set中的元素

想想也很好理解,即使這貨能用下標你也不知道排完序後哪個元素是哪個

所以set給了我們與之替代的兩種資料查詢方式

迭代器(可以理解為STL的指標)和find函式

set的迭代器宣告方式與在vector中的宣告方式無異

同樣是宣告一個名為it的迭代器

有set<int>::iterator it;

如果++it,

則it指向set中的下一個元素

因為set是有序集合,所以++it所表示的元素就是剛好比it所指向的元素大的第一個元素

同時set也有begin和end函式用來取隊首和隊尾(的迭代器)

begin在set中取的是最小元素的迭代器

end函式跟vector中一樣返回隊尾下個元素的迭代器

所以--end()表示的是隊尾元素的迭代器

我們看一段遍歷set的程式碼來幫助我們理解這些抽象的概念

 #include <iostream>
 #include <set>
 using namespace std;
 int main()
 {
     set<int> a;
     multiset<int> b;
     a.insert(1);
     a.insert(2);
     a.insert(3);
     a.insert(1);
     b.insert(1);
     b.insert(2);
     b.insert(3);
     b.insert(1);
     cout<<"set 中的元素依次是 ";
     for(set<int>::iterator it = a.begin(); it != a.end(); it++)
     cout<<*it<<" "; 
     cout<<endl;
     cout<<"multiset 中的元素依次是 ";
     for(multiset<int>::iterator it = b.begin(); it != b.end(); it++)
     cout<<*it<<" ";
     cout<<endl;
     return 0;
} 

程式輸出結果是

 set 中的元素依次是 1 2 3
 multiset 中的元素依次是 1 1 2 3

在這個例子中我們可以瞭解到set中迭代器的用法和set與multiset的本質區別

有助於我們搞明白並在程式上實現set的基本操作

瞭解了迭代器

我們再來看看我認為能與迭代器平起平坐幫了大忙的find函式

find函式能夠找到set中大小為x的元素並返回指向該元素的迭代器。

如果set中並不存在元素x則返回s.end()

x 多次出現則返回第一次出現時的迭代器

樣例如下

 #include <iostream>
 #include <set>
 using namespace std;
 int main()
 {
     set<int> p;
     multiset<int> q;
     p.insert(1);
     p.insert(2);
     p.insert(3);
     p.insert(1);
     q.insert(1);
     q.insert(2);
     q.insert(3);
     q.insert(1);
     int a = *p.find(1);
     int b = *q.find(3);
     cout<<a<<"    "<<b<<endl;
     return 0;  
 }

聰明的你已經看出來了

我這麼寫返回的一定是要查詢的那個數

其實就是沒屁用

啊就有的題可能會出現需要用到find函式的地方(裝不下去了)

在實際應用中,我們需要對不需要的元素進行刪除

set給了我們一個強大的資料刪除函式erase()

括號內既可以是一個迭代器

erase刪除這個迭代器所指的元素

括號內也可以是個值或者啥東西

erase在set或multiset中查詢並刪除和括號內這個東西一樣的全部元素

那問題來了

在用multiset解決實際問題時

很多情況下我們並不想把所有相同的元素都刪除

只想刪除一個兩個或幾個(erase說沒門兒

此時find函式突然出現

“勞資返回的是迭代器hahahahaha”

於是你就可以用find函式定位到元素x第一次出現的位置並返回此時的迭代器

再用erase通過迭代器刪除的功能刪除這個x

由於multiset恆為有序所以刪除哪個x無關緊要

反正修改後的multiset都一樣

所以我們就解決了這個問題(find函式大放異彩)