不同的資料結構「擅長」做不同的操作。陣列(或是 vector
)可以在 $O(1)$ 的時間內找到第 $i$ 項的數字,卻要花很久的時間插入或刪除元素。Linked List 可以在 $O(1)$ 時間插入跟刪除數字,但是要花很久才能找到某一個數字是否存在。有沒有一種方法可以折衷,讓「修改」跟「查詢」兩類的操作時間都不差?這就是 set
出場的時刻。
set
是一個 STL 提供的二元搜尋樹,內部實作的是紅黑樹(Red-Black Tree),是一個實作複雜,但是可以自動平衡避免退化的資料結構。因為 STL 已經幫你實作出來了,就不需要特別擔心實作細節,直接使用!
set
儲存的資料有以下特點:
int
來說,就是每個點數字不一樣。set
在插入、查詢、刪除的複雜度都是 $O(\log n)$。看以下程式片段:
set<int> se; // 宣告一個元素型別為 int 的集合
se.insert(1); // 插入元素 1
se.insert(2);
se.insert(5);
cout << se.count(1) << endl; // 查找元素,true
cout << se.count(3) << endl; // false
se.erase(1); // 刪除元素,若集合內不含此元素則回傳 false
cout << *se.find(3) << endl;
// find() 回傳 iterator,若找不到則回傳 se.end()
cout << *se.lower_bound(3) << endl;
cout << *se.upper_bound(3) << endl;
// 因為 set 有序,所以也支援二分搜操作
STL 也支援可以重複元素的集合,用法與 set
差不多:
multiset<int> se; // 宣告一個元素型別為 int 的 multiset
se.insert(1); // 插入元素 1
se.insert(1); // 可以重複插入
se.insert(5);
cout << se.count(1) << endl; // 計算元素個數,2
cout << se.count(3) << endl; // 0
se.erase(1);
// 一次刪除所有元素 1,若集合內不含此元素則回傳 false
se.erase(find(5)) // 一次刪除一個元素 5
當然,set
與 multiset
也支援 size()
與 empty()
,在此不做贅述了。
set
是一棵二元搜尋樹,當然可以進行遍歷的操作。STL 的二元搜尋樹沒辦法存取一個子樹的左右節點,不過 STL 提供了迭代器(iterator),可以用不同的方式進行遍歷。如果對這個語法感到困惑的話,可以參考基礎資料結構 / Iterator
for (set<int>::iterator it = se.begin(); it != se.end(); it++)
cout << *it << ' ';
或者用 C++11 後提供的 auto
關鍵字進行尋訪:
for (auto &val: se)
cout << val << ' ';
不要被梗!
奕辰找到了一個神奇的機器,可以做三件事:
INSERT
$x$,也就是把一個數字輸入進去這個機器
QUERY MIN
,也就是查詢目前的最小值
QUERY MAX
,同上,查詢目前的最大值
而且, 只要一個數字被查詢過了,機器就會把他吐出來,代表不在機器中了!
int
存下。map
的概念基本上跟 set
的一樣,可是不是存數字而是存兩個值,key
和 value
。使用的方法大致上是用 key
這個值當成類似陣列的索引值,裡面存的東西是 value
。
map
的元素依照 key
排列,所以同理是依照 key
搜尋。跟 set
一樣,map
的 key
值不能有重複。
map<string,int> mapp;
// 宣告一個由 string 映射到 int 的 map
mapp.insert({"apple", 1});
// 插入一個 key 值為 "apple" 的節點
mapp["book"] = 2; // 也可以這樣插入
mapp["apple"] = 3; // 改值
cout << mapp.count("apple") << endl; // true
cout << mapp.count("my girlfriend") << endl; // false
for (map<string,int>::iterator it = mapp.begin(); it != mapp.end(); it++)
cout << it->first << ' ' << it->second << endl;
for (auto &val: mapp)
cout << val.first << ' ' << val.second << endl;
// apple 3
// book 2
如果要看一個 map
裡面的東西,但是不確定它存不存在(像是以下程式)
if(mapp["orange"] == 2) //...
那 mapp
就會先開一個 key 為 orange
的點,佔空間!所以要先判斷存不存在:
if(mapp.find("orange") != mapp.end() && mapp["orange"] == 2) //...
才不會浪費許多不必要的記憶體。這個方法也可以用在 value
可能是預設值(像是 $0$)的情況。
同樣的STL也有 multimap
這種東西,實際上不太常使用,想知道詳細的可以看 CPP Reference。
map
是個好用的東西喔!
恭喜成為森林管理者!現在你的第一個工作就是計算森林中樹的比例。有 $N$ 棵樹,給你他們的英文名稱(由大小寫英文字母表示),請輸出他們所佔的比例為何,輸出至小數點後四位。(比例定義為:$\frac{\text{樹出現的次數}}{N}$)
廣播電台上播了 $n$ 首歌的歌單,第 $i$ 首歌編號為 $k_i$。請求出最長一段,所有歌都相異的連續區間。