先讀語法與例子,再操作互動圖解並完成四選一自測。忘記寫法時回看速查表,最後用中英詞彙卡複習。
vector 用編號查:v[3] 是「第 4 格」。可是現實裡你想查的通常是「"pear" 賣多少錢」「"A" 這個頂點連到誰」。名字不是編號,沒辦法當索引用,於是你只剩一條路:從頭一筆一筆比對過去。
那條路有多慢,跑一次就知道:
N=100000,查詢 Q=1000,預期命中 0 vector:命中 0,耗時 [依環境而異] us unordered_set:命中 0,耗時 [依環境而異] us 本次耗時比:[依環境而異] 倍
兩段都查詢 1000 個不存在的奇數鍵,所以命中數應同為 0;把計數印出來也讓查詢結果成為可觀察行為,避免最佳化器直接刪除無人使用的工作。vector 每次最壞掃過 N 筆,unordered_set 則提供平均常數時間查詢;實際耗時與比值受編譯選項、機器及雜湊實作影響。
map 與 set 提供依鍵查詢的介面。先看資料要保存鍵值還是只有鍵,再看是否需要順序,才能挑選合適的容器。
不要依賴其他標頭碰巧間接引入宣告。使用 map、set、unordered_map、unordered_set 或迭代器工具時,應分別明確引入對應標頭。
所以本頁每一段可執行的範例都寫成完整程式(#include + main()),你可以整段複製貼上。另外注意:#include 不能被 kernel 的自動包裝塞進 main() 裡面,一格裡有 #include 就把整支程式(含 main)寫完整。詳細規矩看 00B · 環境設定 的 FAQ。
[編譯失敗]
cell.cpp: In function ‘int main()’:
cell.cpp:5:5: error: ‘map’ was not declared in this scope
5 | map<string, int> price; // 忘了 #include <map><iostream> 不會順便把 map 帶進來。看到 ‘map’ was not declared in this scope,漏寫 #include <map> 是常見原因之一;補上標頭後若仍失敗,再依診斷位置檢查語法。
map<K, V>:一張鍵對值的表宣告的時候要講清楚兩件事:鍵長什麼樣、值長什麼樣。map<string, int> 就是「用字串查整數」。Python 的 dict 不必先講,因為它同一個容器裡可以塞各種型別;C++ 的容器是同質的,型別在編譯期就釘死。
pair<K, V> 把一個鍵與一個值包成一組,成員名稱是 first 與 second。make_pair(k, v) 會依引數建立這一組資料;獨立使用時應明確引入 <utility>。下面先用它把 "kiwi" 與 60 一起交給 insert。
size = 3 apple 賣 45 有沒有 kiwi? 1 有沒有 mango? 0 改價後 apple 賣 50,size 還是 3 刪掉 pear 後 size = 2
四件事值得注意:① map<string, int> 的兩個型別是鍵的型別與值的型別,順序不能反。② insert 要包成一組 pair,所以有 make_pair。③ count 只回傳 0 或 1(map 的鍵不會重複),拿來當「在不在」的判斷剛好。④ 同一個鍵再指派一次是覆蓋,size() 不會變。
| 你想做的事 | 寫法 | 回傳什麼 |
|---|---|---|
| 放進去/改掉 | m[k] = v; | 將值指定給 m[k];整個指定運算式也可產生所指定的值,通常直接當一行敘述使用 |
| 放進去(已存在就不動) | m.insert(make_pair(k, v)); | 一組 pair:迭代器 + 有沒有成功放進去 |
| 問「在不在」 | m.count(k) | 0 或 1 |
| 找到並拿出來 | m.find(k) | 迭代器,找不到時等於 m.end() |
| 刪掉一個鍵 | m.erase(k); | 刪掉幾筆(0 或 1) |
| 問有幾筆 | m.size() | 目前的鍵數 |
| 清空 | m.clear(); | — |
find 回傳的不是值,而是一個表示容器位置的迭代器。找到時它指向一組 pair,其中 first 是鍵、second 是值;找不到時則等於尾後哨兵 end(),不可解參考。PART 05 會再系統整理迭代器的走訪語法。
count 說 pear 在 find 找到 pear = 30 find 找不到 mango,回傳的是 end()
count 回答「在不在」,find 回傳一個迭代器:找到就指著那一組資料,找不到就等於 end()。想拿值就用 find,只想判斷就用 count,免得同一個鍵查兩次。it->first 是鍵、it->second 是值,這兩個名字後面會一直出現。
map 內部靠「比大小」把鍵排好,所以鍵的型別必須支援 <。int、double、string 都內建有,直接可以用。等你到 P9 想把自己寫的類別當鍵,就得自己多載 operator<,否則編譯器會產生冗長的樣板診斷訊息。
m[k] 查不到,會自己建一個 與 Python 差最多的一點Python 的 d["mango"] 查不到會立即丟出 KeyError。C++ 的 m["mango"] 則會靜默插入一筆,再回傳那筆的值;若原意只是查詢,這項改動可能到較後面的結果才被發現。
一開始 size = 1 查 mango:0 查完 size = 2 又查三個之後 size = 4 count 查 durian:0 用 count 查完 size = 4
從 1 變 2、再變 4。m[k] 查不到就會當場建一個,值是預設初始化的結果:int 是 0、double 是 0.0、string 是空字串、vector 是空的。所以印出來的 0 不是「查不到」,是「剛剛替你建了一個 0」。最後兩行是對照組:count 查完 size 一動也不動。
| 查一個不存在的鍵 | Python dict | C++ map |
|---|---|---|
| 會發生什麼 | 丟出 KeyError,程式中斷 | 建一個新鍵,值是預設初始化的結果 |
| 容器大小 | 不變 | 加一 |
| 不想動到容器的查法 | d.get(k) 或 k in d | m.count(k)|m.find(k) |
| 唯讀情境 | 照樣可以用中括號 | const map 不能用 [],編譯器直接擋下來 |
確定要寫入(指派、++、push_back)就用 m[k],它順便建鍵反而幫了你的忙。
只是要讀或判斷就用 count 或 find。
一個空的 map<string, int> m;,接著執行:m["a"] = 1;、if (m["b"] > 0) cout << "yes";、m.count("c");。最後 m.size() 是?
若要求鍵必須存在,可用 m.at(k),找不到會丟 out_of_range。C++17 可用 count 或 find 做存在性判斷;contains 是較新版本的介面,不在本頁 C++17 範例使用。
set<T>:一個「這個我見過沒有」的容器三個特性,一次到手:不重複、自動排序、查得快。你只要問它「這個在不在」,它不存值。
丟了 5 次,size = 3 走訪:apple kiwi pear apple 在嗎? 1 erase 之後還在嗎? 0,size = 2
set 就是只有鍵、沒有值的 map:同樣不重複、同樣自動排序、同樣用 count 查、用 erase 刪。差別只在 insert 直接丟元素進去,以及走訪時 *it 就是元素本身,不必再拆 first/second。注意走訪印出來是 apple kiwi pear —— 字典序,不是丟進去的順序。
| 操作 | set<T> | map<K, V> |
|---|---|---|
| 放進去 | s.insert(x); | m[k] = v; |
| 查在不在 | s.count(x) | m.count(k) |
| 刪掉 | s.erase(x); | m.erase(k); |
走訪時 *it 是 | 元素本身 | 一組 pair(first/second) |
| 重複丟同一個 | 只留一份,size 不變 | 值被覆蓋,size 不變 |
把資料加入 set,再讀 size 就能得到不同值的數目。也能用 set 記錄已處理的識別碼,避免同一項重複處理;加入與查詢是兩種不同操作。
set 與 map 依比較器判斷兩個鍵是否屬於同一位置:若 !(a < b) && !(b < a),兩者便視為等價鍵。這和直接計算 a == b 是不同機制;自訂比較器必須維持一致且符合嚴格弱序。
map 與 unordered_map 的常用介面相近,但契約不同:map 依比較器維持鍵的次序,unordered_map 依雜湊與相等判定查找鍵。這裡比較介面、順序與查詢成本;使用這些類別不需要先實作內部資料結構。
插入順序 : kiwi apple durian pear mango guava lychee fig map 走訪 : apple durian fig guava kiwi lychee mango pear unordered_map : [順序未指定] 桶數 bucket_count = [由實作與目前狀態決定]
map 依鍵的大小順序走訪;unordered_map 的走訪順序未指定。bucket_count() 回傳目前的桶數,由標準函式庫實作與容器的容量狀態決定。
map 的一般查詢具有 O(log n) 複雜度;unordered_map 平均為 O(1),最壞可能為 O(n)。這是成長速度的比較,不表示每次實測後者都較快,仍受資料量、鍵與實作影響。
右欄那個順序只是這台機器、這個版本跑出來的結果。你在自己的 notebook 跑,很可能得到另一個順序,那是正常的。
走訪: 58:Bob 64:Dee 77:Cid 91:Ann 最低分 58 最高分 91 第一個不低於 70 的: 77
插入順序是 91、58、77、64,走訪卻是 58、64、77、91,因為預設 map 依鍵排序。非空時 begin() 指向最小鍵、rbegin() 指向最大鍵;lower_bound(70) 找第一個不小於 70 的鍵,找不到則回傳 end(),不可直接解參考。unordered_map 沒有這些排序保證。
| 你的需求 | 選誰 | 為什麼 |
|---|---|---|
| 只是查得到就好,量很大 | unordered_map | 平均常數時間查詢;實際速度仍依資料與環境而異 |
| 要照鍵的順序輸出 | map | 走訪本身就是排好的,省一次排序 |
| 要「最小的鍵」「第一個不小於 x 的鍵」 | map | begin() 取得最小鍵需非空;lower_bound 查下界。無序版的 begin() 不代表最小鍵,也沒有 lower_bound。 |
| 鍵是自己寫的類別 | map | 多載一個 operator< 就好;雜湊版要自己寫雜湊函式,麻煩得多 |
| 要求最壞情況也穩定 | map | 標準保證對數時間;雜湊表碰撞嚴重時最壞可退化成線性時間 |
| 作業沒特別要求 | map | 輸出順序固定,比對答案時不會因為走訪順序不同而看起來像錯的 |
begin()/end():所有 STL 容器的共同語言迭代器是一個「指著容器裡某個位置」的東西,用起來像指標:*it 取值、++it 往前一格、it-> 存取成員。學會這四個符號,vector、set、map、list 就全部會走訪了。
| 寫法 | 意思 | 要注意 |
|---|---|---|
| c.begin() | 指向第一個元素 | 容器是空的時候,它等於 end() |
| c.end() | 指向最後一個的再下一格 | 不能解參考,它只是個哨兵 |
| ++it | 往後移一格 | 慣例寫前置 ++it,不寫 it++:後置版會多複製一份 |
| *it | 取出它指著的東西 | map 拿到的是 pair,set/vector 拿到的是元素 |
| it->first | 等同 (*it).first | 跟指標的 -> 完全同一個符號 |
| it != c.end() | 還沒走完 | 迴圈條件用 != 不用 <:樹和雜湊表的迭代器沒有大小可比 |
[begin, end):左閉右開,end() 站在門外end() 不是最後一個元素,是最後一個元素的再下一格。整個範圍是「含頭、不含尾」,
這種區間叫左閉右開 —— C++ 所有吃一對迭代器的東西(走訪、P5 的範圍建構式、sort)都是這個慣例:
▲ v = {10, 20, 30}:begin() 指第一格,end() 指「索引 3」那個不存在的位置。所以 vector<int> sub(v.begin()+1, v.begin()+3) 抄到的是索引 1、2 —— 20 30,尾端那格不含。
對 vector 來說,迭代器的行為特別像指標:連 begin() + i 這種「往後跳 i 格」都支援
(P4 的指標算術),所以 *(v.begin() + 2) 拿到第三個元素。
但別的容器就不行了 —— 差別整理成一句:
指標是記憶體位址;迭代器是「用來走訪容器的位置物件」。元素連續排列的
vector/string,迭代器可以 + i 隨機跳;list、map、set
的元素散在各處,迭代器只能 ++it 一步一步走,寫 L.begin() + 1 直接編譯錯誤
(no match for 'operator+')。這也是上表說迴圈條件用 != 不用 < 的原因。
*it = 10 *(begin()+2) = 30 sub = 20 30 list ++lit = 20
第 12 行對照上面的圖:[begin()+1, begin()+3) 抄索引 1、2,尾端不含。把第 17 行改成 auto x = L.begin() + 1; 會得到 no match for 'operator+' —— 迭代器像指標到什麼程度,是各容器自己決定的。
list<int> L = {10, 20, 30}; 之後寫 cout << *(L.begin() + 1); 會發生什麼事?
vector: 31 17 93 set : 17 31 93 map : apple=45 pear=30
vector 印出來是插入順序、set 是排好的、map 是照鍵排好的,但三個迴圈長得一模一樣。這就是迭代器的價值:容器內部是陣列、樹還是雜湊表,走訪的人不必知道。map 唯一的不同是 *it 拿到的是一組 pair,所以改用 it->first 與 it->second。
erase 掉一個元素之後,指著它的迭代器就失效了,跟 P4 的懸空指標是同一種病。正確寫法是 it = m.erase(it);(erase 會回傳下一格):刪的時候用 erase 的回傳值前進,沒刪才 ++it。標準句型:for (auto it = m.begin(); it != m.end(); ) { if (cond) it = m.erase(it); else ++it; }
<algorithm>:把一對迭代器交給現成的演算法sort、find 這些不是容器的成員函式,你不會寫 v.sort()。它們是 <algorithm> 這個標頭裡的自由函式,
你要做的是把「從哪到哪」用一對迭代器告訴它:sort(v.begin(), v.end()) 的意思是「把 [begin, end) 這一段排好」。
這正是左閉右開慣例的回報 —— 演算法只認得迭代器,不認得容器,所以同一套 find、count 通吃 vector、string、陣列,
一個都不用重寫。
| 寫法 | 做什麼 | 獨立使用方式 |
|---|---|---|
| sort(b, e) | 把這一段由小到大排好 | 拿來對照自己寫的排序:sort(aList.begin(), aList.end()); // the STL built-in sort |
| find(b, e, x) | 找 x,回傳迭代器 | 找不到就回傳 e(也就是 v.end())——不是 -1 |
| reverse(b, e) | 把這一段前後反轉 | 進位轉換:餘數是從低位算出來的,最後要反過來印 |
| count(b, e, x) | 數 x 出現幾次 | 統計字元/元素出現次數 |
| min(a, b)/max(a, b) swap(a, b) | 兩個值取小、取大、交換 | 動態規劃 minCoins = min(numCoins, minCoins);、 heap swap(heap[i], heap[parentIdx]);這三個吃的是值,不是迭代器( swap 正式的家在 <utility>,但常跟這群一起用) |
find 沒有辦法回傳「找不到」這種特別的數字,因為容器裡什麼值都可能是合法元素。它的答案是位置,所以找不到時它回傳門外那一格 ——
就是上面圖裡那個哨兵 end()。於是判斷成員的標準寫法長這樣:
if (find(v.begin(), v.end(), 15) != v.end()) { /* 有 15 */ }
查一般序列可使用 find(values.begin(), values.end(), target),並與 end 比較。std::find 會逐項掃描;map/set 的成員 find 則利用容器本身的查詢結構。相同名字不等於相同成本。
sorted : 17 17 31 55 93 find 55 : *it = 55,索引 3 find 99 : 找不到,回傳的就是 end() reversed: 93 55 31 17 17 count 17: 2 次 min/max : 3 / 8
第 16 行的 hit - v.begin() 就是「這是第幾格」:兩個迭代器相減得到距離,這也是 vector 迭代器像指標才有的特權。
最後補一句限制:sort 需要迭代器能隨機跳,所以 sort(L.begin(), L.end()) 對 list 編譯不過(list 有自己的 L.sort()),
而 set/map 本來就排好了,根本不需要排。
vector<int> v = {10, 20, 30}; 執行 find(v.begin(), v.end(), 99);,這個 99 不在裡面。它回傳什麼?
it->first 到 auto& [k, v]map 裡每一筆資料是一組 pair<const K, V>:first 是鍵、second 是值。走訪就是把這些 pair 一組一組拿出來。
語法糖(syntax sugar)讓常見操作有更簡潔的寫法。這裡分成兩件事:auto 讓編譯器從初值推導型別,省下型別名稱;範圍 for 則把 begin()、end()、前進與取元素的步驟包起來,省下走訪的迴圈控制。兩者可以分開使用。
Des Moines 4 Sacramento is the capital of California Des Moines is the capital of Iowa Salt Lake City is the capital of Utah Madison is the capital of Wisconsin
這份 map 以州名作為鍵,依鍵的順序走訪。it 指著目前的位置,*it 是該位置的鍵值對;it->second 就是 (*it).second。
把上面程式的 for 迴圈換成以下寫法。capitals.begin() 回傳 map<string, string>::iterator,因此 auto it 的型別在編譯時就已確定。
再改成範圍 for,直接為每個元素取名 entry。現在存取的是元素本身,使用 entry.first 與 entry.second。
這三種走訪都會印出相同的州名與首府。auto 與範圍 for 從 C++11 就能使用;後面的 auto& [key, value] 則再加上 C++17 的結構化繫結。
map<string, string> 的元素型別是 pair<const string, string>。決定「拿副本還是改原元素」的是宣告中的 &:
| 範圍 for 裡的宣告 | entry 的完整型別 | 修改 second 的結果 |
|---|---|---|
auto entry | pair<const string, string> | 複製一筆資料,只改到副本 |
pair<const string, string> entry | pair<const string, string> | 同樣是副本;明寫型別不會省掉複製 |
auto& entry | pair<const string, string>& | 參考原元素,修改會寫回 map |
const auto& entry | const pair<const string, string>& | 唯讀參考,不複製,也不能透過 entry 修改 |
所以 for (pair<const string, string> entry : capitals) 能正常印出資料,但 entry.second = "Unknown"; 只會改到副本。要改原本的首府,寫 for (auto& entry : capitals);完整寫法則是 for (pair<const string, string>& entry : capitals)。只要印出資料時,可用 const auto& 避免複製。
對這個 map,上面的範圍 for 可對照成以下 iterator 寫法。關鍵是每一輪的 auto& entry = *it;;拿掉 &,就會複製目前的元素。
元素中的 first 是 const string,即使使用 auto&,也不能改鍵;second 才是可修改的值。程式裡把迴圈變數命名為 entry,可以清楚區分變數名稱與型別名稱 pair。
apple 45 / kiwi 60 / pear 30 / apple 45 / kiwi 60 / pear 30 / apple 45 / kiwi 60 / pear 30 / 全部漲五元後:apple=50 kiwi=65 pear=35
三行輸出一模一樣,選最好讀的那個就好。結構化繫結(C++17)把 pair 當場拆成兩個有名字的變數,比 p.first/p.second 好懂太多。最後一段是關鍵:auto& 的 & 不能省,少了它你改的是複製品,原本的 map 不會變。順帶一提,鍵是唯讀的 —— 改了鍵,樹的排序就壞了,所以編譯器直接禁止。
| 寫法 | 長什麼樣 | 什麼時候用 |
|---|---|---|
| 迭代器 | for (map<string,int>::iterator it = m.begin(); it != m.end(); ++it) | 要在迴圈裡刪東西,或需要拿到位置 |
| range-for + pair | for (auto& p : m) | C++11 起可用,最通用 |
| 結構化繫結 | for (auto& [k, v] : m) | C++17 起可用,最好讀,優先選這個 |
for (auto p : m):每一輪複製一組 pair,改了不算數,而且鍵是 string 的話每輪都在複製字串。
for (auto& p : m):參考本尊,改得動,也不複製。
for (const auto& p : m):參考但唯讀,只是要印出來時最安全。這三個差別在 P3 講過,這裡是它最常出現的場合。
計數、去重與分組都可以用已學過的容器操作組合起來,不需要自己實作容器內部。先看一段完整程式,再比較每個核心敘述會不會新增鍵、會不會改變元素數量。
計數: applex2 figx1 kiwix2 pearx3 去重: 4 種 → apple fig kiwi pear 依長度分組: 3 個字母: fig 4 個字母: pear pear kiwi pear kiwi 5 個字母: apple apple
計數:freq[w]++ 這一行同時處理了兩種情況:沒看過的鍵先被建出來、值預設是 0,再 ++ 變 1;看過的鍵就直接加一。PART 02 說 m[k] 會自己建鍵是個陷阱,在這裡它反而是整個寫法成立的理由。
去重:全丟進 set,size() 就是種類數。
分組:byLength[len].push_back(w):[] 先生出一個空的 vector,再往裡面塞。
| 要做的事 | 容器 | 核心那一行 |
|---|---|---|
| 算每個東西出現幾次 | map<T, int> | freq[x]++; |
| 問「有幾種不同的」 | set<T> | uniq.insert(x); |
| 問「這個處理過了沒」 | set<T> | if (visited.count(x)) continue; |
| 把同類的收在一起 | map<K, vector<V>> | group[key].push_back(v); |
Design: Ada Ben (2 people) Support: Cora (1 people) Training: (0 people) departments = 3
members["Design"] 在缺少鍵時建立空 vector,再由 push_back 加入名字。外層 members.size() 是部門數,每份 names.size() 是該部門人數,兩者不可混為一談。Training 雖然沒有成員,仍是已建立的鍵;若只想查部門是否存在,可用 find,避免意外建立空名單。
你要統計一份成績單裡每個分數出現幾次,最後從低分到高分印出來。哪一個選擇最省事?
一個空的 map<string, int> freq;,第一次執行 freq["pear"]++;。freq["pear"] 明明還不存在,為什麼可以直接 ++?
| map | unordered_map | set | unordered_set | |
|---|---|---|---|---|
| 存什麼 | 鍵 + 值 | 鍵 + 值 | 只有鍵 | 只有鍵 |
| 組織方式 | 依比較器排序 | 雜湊分桶 | 依比較器排序 | 雜湊分桶 |
| 查詢/插入/刪除 | $O(\log n)$ | 平均 $O(1)$ 最壞 $O(n)$ | $O(\log n)$ | 平均 $O(1)$ 最壞 $O(n)$ |
| 走訪順序 | 照鍵排好 | 未指定的走訪順序 | 照鍵排好 | 未指定的走訪順序 |
| 鍵的型別要有 | operator< | hash + operator== | operator< | hash + operator== |
| 要 include | <map> | <unordered_map> | <set> | <unordered_set> |
有 lower_bound/rbegin | 有 | 沒有 | 有 | 沒有 |
| 典型用途 | 要排序輸出、要範圍查詢 | 純查表、量大 | 去重、走過沒 | 去重、量大 |
| 你在 Python 寫 | C++ 寫法 | 差在哪 |
|---|---|---|
| d = {} | map<string, int> d; | C++ 要先講清楚鍵與值的型別 |
| d[k] = v | d[k] = v; | 一樣 |
| d[k](k 不存在) | d[k] | Python 丟 KeyError;C++ 建一個新鍵 |
| k in d | d.count(k) | C++ 回傳 0 或 1,可以直接當條件 |
| d.get(k, 0) | d.count(k) ? d[k] : 0 | C++ 沒有現成的 get,自己判斷 |
| del d[k] | d.erase(k); | C++ 刪不存在的鍵不會出錯,回傳 0 |
| len(d) | d.size() | 一樣 |
| for k, v in d.items() | for (auto& [k, v] : d) | C++17 起可用 |
| s = set() | set<string> s; | C++ 的 set 自動排序,Python 的不排 |
| s.add(x) | s.insert(x); | 一樣 |
| 走訪 dict 的順序 | C++ map 是排序的 | Python 3.7 起是插入順序;C++ 沒有插入順序的版本 |
① 要寫入就用 m[k],只是要查就用 m.count(k)。
② <map>、<set>、<unordered_map> 一定要自己 include。
③ 需要排序輸出、需要 lower_bound 就選 map;純查表選 unordered_map。
④ 走訪 map 優先寫 for (auto& [k, v] : m),那個 & 不要漏。
| 資源 | 看什麼 |
|---|---|
| cppreference · std::map | 完整的成員函式清單。查 lower_bound、at、emplace 的正確用法。 |
| cppreference · std::unordered_map | bucket_count、load_factor、rehash,會用到這些名詞。 |
| C++ Tutor | 把 PART 02 那段貼進去,一步一步看 m["mango"] 執行完 map 裡多了什麼。 |
| 本站 · 搜尋與排序 | 雜湊表的內部:雜湊函式、碰撞處理、負載因子。 |
| C++ Gossip · 容器與樣板 | 用 map<string, vector<string>> 表示部門名單,比較外層鍵數與內層人數。 |
| C++ Gossip · 容器與樣板 | 比較器與相等判定:確認「排列順序」和「鍵是否相同」的關係。 |
每個選項都有解說:選錯也點開看看為什麼錯。全對之後再往下翻詞彙卡。
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
map<string, int> m;
m["pear"] = 30;
cout << m["mango"] << " ";
cout << m.count("kiwi") << " ";
cout << m.size() << endl;
return 0;
}
#include <iostream>
#include <set>
using namespace std;
int main() {
set<int> s;
s.insert(93); s.insert(17); s.insert(93);
s.insert(31); s.insert(17);
cout << s.size() << ": ";
for (set<int>::iterator it = s.begin(); it != s.end(); ++it)
cout << *it << " ";
cout << endl;
return 0;
}
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
string w[5] = {"b", "a", "b", "c", "b"};
map<string, int> freq;
for (int i = 0; i < 5; i++) freq[w[i]]++;
for (auto& [k, n] : freq) cout << k << n << " ";
cout << endl;
return 0;
}
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
map<string, int> m;
m["pear"] = 30;
map<string, int>::iterator it = m.find("kiwi");
if (it == m.end()) cout << "miss ";
else cout << it->second << " ";
cout << m.size() << endl;
return 0;
}
#include <iostream>
#include <map>
#include <vector>
#include <string>
using namespace std;
int main() {
map<string, vector<string>> adj;
adj["A"].push_back("B");
adj["A"].push_back("C");
adj["B"].push_back("C");
cout << adj.size() << " ";
cout << adj["C"].size() << " ";
cout << adj.size() << endl;
return 0;
}
先看正面術語,心中默想定義再翻面對答案;洗牌後再過一輪,直到每張都能不假思索說出來。