C++ STL
set / multiset / unordered_set
Complete lesson for set / multiset / unordered_set using LEARNCODEGURU’s 6-level learning model — from basics to advanced practice, with C++ OOP and STL where relevant.
Basics — set / multiset / unordered_set
Section: C++ STL. C++ set family — set, multiset, unordered_set with real-life examples and comparisons.
What is a set family?
- **Set-like** containers store **keys only** (no mapped value like
map). set— **unique** keys, always **sorted** (balanced tree).multiset— **duplicates allowed**, still **sorted**.unordered_set— **unique** keys via **hashing** (no sorted order).- Headers:
#includeand#include. - **Real-life:** guest list without repeats (
set); score sheet with tied marks (multiset); “have I seen this ID?” badge scanner (unordered_set).
| Idea | Meaning |
|---|---|
| Key-only | Stores elements, not key→value pairs |
| Unique vs multi | set/unordered_set reject duplicates; multiset keeps them |
| Order | Tree sets sorted; hash set unordered |
| Lookup | Tree O(log n); hash avg O(1) |
| Iterate | Range-for walks all keys |
Why use set / multiset / unordered_set?
- Fast membership tests and automatic uniqueness (or sorted multis).
- Tree sets give ordered traversal “for free”.
- Hash sets shine when order does not matter and speed does.
| Benefit | What you gain |
|---|---|
| Uniqueness | No manual “already present?” for set / unordered_set |
| Sorted walk | set/multiset iterate ascending |
| Fast lookup | unordered_set average O(1) membership |
| Duplicate counts | multiset::count for frequency |
| Clear intent | Better than a hand-rolled sorted vector for keys |
Which set container?
- Pick by **duplicates** and whether you need **sorted order**.
| Container | Duplicates? | Order | Typical cost | Header | Real-life pick |
|---|---|---|---|---|---|
set | No (unique) | Sorted | O(log n) | | Unique roll numbers, sorted tags |
multiset | Yes | Sorted | O(log n) | | Exam scores with ties |
unordered_set | No (unique) | Hash order | Avg O(1) | | Visited URLs, seen IDs |
Differences between set, multiset, unordered_set
- Same “collection of keys” idea — different uniqueness and ordering contracts.
| Feature | set | multiset | unordered_set |
|---|---|---|---|
| Duplicates | No | Yes | No |
| Order | Sorted | Sorted | Unspecified |
| Structure | Tree | Tree | Hash table |
| insert/find | O(log n) | O(log n) | Avg O(1) |
count(k) | 0 or 1 | Frequency | 0 or 1 |
erase(k) | That key | **All** equals | That key |
| Real-life | Unique sorted IDs | Sorted bag of scores | Fast “seen?” set |
| Need sorted walk? | Yes | Yes | No |
set — unique sorted keys
- Stores **unique** keys in **sorted** order (balanced tree).
- insert / find / erase are **O(log n)**.
- Inserting an existing key has no effect (set stays unique).
- Iterators walk keys in ascending order.
- **Real-life:** classroom roll numbers (no duplicates, naturally sorted); sorted unique product SKUs on a shelf list.
#include <iostream>
#include <set>
using namespace std;
int main() {
set<int> s = {3, 1, 2, 2}; // {1, 2, 3}
s.insert(4);
cout << s.count(2) << "\n"; // 1
for (int x : s) cout << x << " ";
cout << "\n";
return 0;
} multiset — sorted with duplicates
- Like
set, but **duplicate keys are allowed** and kept sorted. count(key)returns how many times the key appears.equal_range(key)gives the iterator range of all equal keys.erase(key)removes **all** matches;erase(iterator)removes one.- **Real-life:** exam marks where several students score 90; waiting times logged and sorted with repeats.
#include <iostream>
#include <set>
using namespace std;
int main() {
multiset<int> ms = {1, 2, 2, 3};
ms.insert(2); // now three 2's
cout << ms.count(2) << "\n"; // 3
auto [lo, hi] = ms.equal_range(2);
for (auto it = lo; it != hi; ++it) cout << *it << " ";
cout << "\n";
return 0;
} unordered_set — unique hashed keys
- Unique keys; **not sorted** — hash buckets.
- Average **O(1)** insert/find/erase; worst case can degrade.
- Great for membership tests (“have I seen this before?”).
- Custom types need a **hash** and **equality**.
- **Real-life:** airport boarding pass scan (already boarded?); unique words in a document when order does not matter.
#include <iostream>
#include <string>
#include <unordered_set>
using namespace std;
int main() {
unordered_set<string> us = {"a", "b"};
us.insert("c");
us.insert("a"); // ignored — unique
cout << (us.find("a") != us.end()) << "\n"; // 1
cout << us.size() << "\n"; // 3
return 0;
} Common operations
- Shared ideas:
insert,erase,find,count,size,empty, iterate with range-for. findreturns an iterator; compare withend()for missing keys.- Prefer
count/findover assuming sorted order onunordered_set.
| Operation | set / multiset | unordered_set |
|---|---|---|
| Insert | insert(x) | insert(x) |
| Lookup | find / count O(log n) | find / count avg O(1) |
| Iterate | Ascending keys | Bucket order (unspecified) |
| Duplicates | set no / multiset yes | No |
| Lower/upper bound | Yes (tree) | No |
Real-life examples by container
- Map everyday problems to the right set type.
| Real-life situation | Best fit | Why |
|---|---|---|
| Unique student roll numbers, sorted printout | set | Unique + ascending order |
| Tags on a blog post (no duplicate tags) | set | Uniqueness + optional sorted display |
| Dictionary of banned words (lookup often) | unordered_set | Fast membership, order unused |
| “Visited page” tracker in a crawler | unordered_set | Fast “seen URL?” checks |
| Exam scores with many 85s and 90s | multiset | Duplicates + sorted |
| Sensor readings bag, keep all repeats sorted | multiset | Multi + order |
| Lottery ticket numbers (unique draws) | set | Unique sorted set |
| Login session tokens already used | unordered_set | O(1) replay check |
| Sorted unique product IDs for inventory | set | Ordered unique catalog |
| Word frequency prep (count later in map) | multiset or map | Multi if only bag; map if count |
Set family applications
- Sets appear whenever you track membership, uniqueness, or sorted bags of keys.
| Application | Container | How it helps |
|---|---|---|
| Remove duplicates from a list | set / unordered_set | Insert all; size is unique count |
| Membership / “seen before?” | unordered_set | Average O(1) find/insert |
| Sorted unique report | set | Iterate ascending unique keys |
| Frequency of equal keys | multiset | count / equal_range |
| Intersection of two ID lists | set | Walk both sorted ranges |
| Graph visited nodes | unordered_set | Mark nodes as explored |
| Spell-check dictionary | unordered_set | Fast word present? |
| Leaderboard ties (same score many times) | multiset | Keep all equal scores sorted |
| Unique event IDs today | set | Dedup + chronological-ish sort if numeric |
| Anagram helper (char multiset) | multiset | Same letters with counts |
Application — unique sorted IDs (set)
- **Real-life:** collect registration numbers; print once each in order.
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main() {
set<string> rolls;
for (auto& id : {"A12", "B07", "A12", "C01"})
rolls.insert(id);
for (const auto& id : rolls) cout << id << " ";
cout << "\n"; // A12 B07 C01
return 0;
} Application — exam scores bag (multiset)
- **Real-life:** store every student’s mark; count how many scored 90.
#include <iostream>
#include <set>
using namespace std;
int main() {
multiset<int> scores = {70, 90, 85, 90, 90, 60};
cout << "How many 90s: " << scores.count(90) << "\n"; // 3
cout << "Lowest: " << *scores.begin() << "\n";
cout << "Highest: " << *scores.rbegin() << "\n";
return 0;
} Application — seen-before check (unordered_set)
- **Real-life:** badge scanner — reject a second swipe of the same ID.
#include <iostream>
#include <string>
#include <unordered_set>
using namespace std;
int main() {
unordered_set<string> seen;
for (auto& id : {"U1", "U2", "U1", "U3"}) {
if (!seen.insert(id).second)
cout << id << " already entered\n";
else
cout << id << " welcome\n";
}
return 0;
} When to use which
- Need sorted unique keys or ordered traversal → **
set**. - Need sorted keys **with duplicates** → **
multiset**. - Need fast unique membership, order irrelevant → **
unordered_set**. - **Real-life rule of thumb:** printer needs alphabetical unique list →
set; hospital lab keeps all temperature readings →multiset; security gate only asks “seen?” →unordered_set.
Key points to remember
set= unique + sorted;multiset= duplicates + sorted;unordered_set= unique + hash.multiset.erase(key)removes **all** equals — erase one iterator to remove a single copy.unordered_setiteration order is not sorted and not stable across runs.- Use
find/countfor membership; do not assume hash order. - Real-life: unique IDs → set/unordered_set; score bags → multiset; fast seen? → unordered_set.
Advanced — set / multiset / unordered_set
erase details on multiset
ms.erase(2)removes every2.- To remove one duplicate:
auto it = ms.find(2); if (it != ms.end()) ms.erase(it);. - **Real-life:** cancel one of three identical print jobs — erase by iterator, not by value.
Application — set intersection idea
- Two sorted unique lists → walk with two iterators or use
std::set_intersection. - **Real-life:** students in both “Math club” and “Robotics club”.
#include <iostream>
#include <set>
#include <algorithm>
#include <vector>
using namespace std;
int main() {
set<int> a = {1, 2, 3, 5};
set<int> b = {2, 3, 4, 6};
vector<int> both;
set_intersection(a.begin(), a.end(), b.begin(), b.end(),
back_inserter(both));
for (int x : both) cout << x << " ";
cout << "\n"; // 2 3
return 0;
} Application — remove duplicates from stream
- Insert into
unordered_set(orsetif you need sorted unique output). - **Real-life:** dedupe phone numbers from a CSV import.
#include <iostream>
#include <vector>
#include <unordered_set>
using namespace std;
int main() {
vector<int> raw = {5, 1, 5, 2, 1, 3};
unordered_set<int> uniq;
vector<int> out;
for (int x : raw)
if (uniq.insert(x).second) out.push_back(x);
for (int x : out) cout << x << " ";
cout << "\n"; // 5 1 2 3 (first-seen order)
return 0;
} Custom order / hash notes
set/multisetaccept a comparator type (defaultless).unordered_setmay needstd::hashspecialization for user types.- Do not store mutable keys that change their hash/order while inside the container.
- **Real-life:** descending leaderboard keys →
setwithgreater<>; customEmployeeIdtype needs hash forunordered_set.
Complexity reminder
- Tree sets: predictable O(log n).
- Hash sets: average O(1), watch pathological collisions and rehash cost.
- Prefer
unordered_setfor pure membership; prefersetwhen you need order or ordered algorithms.
Design tips
- Need order → tree (
set/multiset); only membership →unordered_set. - Duplicates matter →
multiset(ormapkey→count). - For “key → value”, use
map/unordered_map, not a set. - Guard
*find/*begin— empty containers have no elements. - Document real-life meaning of each key (ID, score, token).
Tricky Interview Points
Q1) Difference between set and multiset?
Answer set stores unique sorted keys; multiset allows duplicate keys, still sorted.
Q2) set vs unordered_set?
Answer set is ordered (tree, O(log n)); unordered_set is hashed (average O(1)), no sorted order.
Q3) How do you remove one duplicate from a multiset?
Answer Erase a single iterator from find(), not erase(key) which removes all equals.
Q4) Can set store duplicates?
Answer No. Inserting an existing key does not add another element.
Q5) When prefer unordered_set?
Answer When you only need fast unique membership and do not need sorted iteration.
Q6) Give a real-life example for set.
Answer Unique student roll numbers printed in sorted order; unique product SKUs in a catalog.
Q7) Give a real-life example for multiset.
Answer Exam scores where many students share the same mark; a bag of sorted sensor readings.
Q8) Give a real-life example for unordered_set.
Answer Visited URLs in a crawler; badge IDs already scanned at a gate.
Q9) What does count return on set vs multiset?
Answer On set: 0 or 1. On multiset: how many times the key appears.
Q10) Default order of set?
Answer Ascending (less<T>). Use greater<T> for descending.
Q11) Can you lower_bound on unordered_set?
Answer No — ordered bounds are for tree containers (set/multiset/map).
Q12) set vs map?
Answer set stores keys only; map stores key→value pairs.
Simple Programs
set — unique sorted
#include <iostream>
#include <set>
using namespace std;
int main() {
set<int> s = {5, 1, 3, 3};
for (int x : s) cout << x << " ";
cout << "\n";
return 0;
} multiset — count duplicates
#include <iostream>
#include <set>
using namespace std;
int main() {
multiset<int> ms = {1, 2, 2, 2, 4};
cout << ms.count(2) << "\n";
return 0;
} unordered_set — membership
#include <iostream>
#include <unordered_set>
using namespace std;
int main() {
unordered_set<int> seen;
for (int x : {1, 2, 1, 3}) {
if (!seen.insert(x).second) cout << x << " duplicate\n";
}
return 0;
} Real-life — unique tags (set)
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main() {
set<string> tags;
for (auto& t : {"cpp", "stl", "cpp", "set"})
tags.insert(t);
for (const auto& t : tags) cout << "#" << t << " ";
cout << "\n";
return 0;
} Real-life — score frequencies (multiset)
#include <iostream>
#include <set>
using namespace std;
int main() {
multiset<int> marks = {88, 90, 88, 70, 90};
for (int m : {70, 88, 90})
cout << m << " appears " << marks.count(m) << "\n";
return 0;
} Real-life — gate scanner (unordered_set)
#include <iostream>
#include <unordered_set>
#include <string>
using namespace std;
int main() {
unordered_set<string> inside;
auto swipe = [&](const string& id) {
if (inside.count(id)) cout << id << " already inside\n";
else { inside.insert(id); cout << id << " entered\n"; }
};
swipe("A1"); swipe("B2"); swipe("A1");
return 0;
} Problem Solving Tricks
- set = unique + sorted; multiset = duplicates + sorted; unordered_set = unique + hash.
- multiset.erase(key) removes ALL matches.
- unordered_set has no meaningful sorted order.
- Use count/find for membership tests.
- Pick unordered_set for fast “seen before?” checks.
- Pick set when you need ordered unique keys.
- Real-life: rolls/tags → set; scores bag → multiset; visited IDs → unordered_set.
- set ≠ map — no mapped value on set.
- insert().second tells you if a new unique key was added.
- rbegin() on set/multiset gives largest key quickly.
- Prefer unordered_set for pure membership speed.
- Document what each key means in the real problem.
Practice Programs List
- Build a set from an array with duplicates; print sorted unique values.
- Use multiset to store scores and print how many times 90 appears.
- Use unordered_set to detect the first duplicate in a stream of ints.
- Compare sizes of set vs multiset filled with the same input.
- Write a short table: when you would choose each of the three.
- Model blog tags with a set of strings (no duplicate tags).
- Model a badge gate with unordered_set; reject second swipe.
- Erase only one copy of a score from a multiset of marks.
- Compute intersection of two club membership sets.
- List 8 real-life examples and pick the best container for each.
- Explain why erase(key) on multiset is dangerous for “cancel one job”.
- Deduplicate a CSV of phone numbers preserving first-seen order.