</>CodexBash
Learn
First stepsBeginner9 min

Maps and sets in everyday code

Finding things by value rather than position: sets, maps and counting in C++, Java and Python, and the array tricks that stand in for them in C.

Read these first

1 of 1 still to read
  1. Arrays and lists in everyday code
Hashing to a slotstep 1/7
0
1
2
3
4
5
A hash table is an array of slots. The key decides which slot, so there is no searching.

Arrays find things by position. A great many problems need to find things by value instead: have I seen this number before, how many times does this word appear, where was this character last. For that, every language has a map and a set.

  • A set holds values with no duplicates, and answers "is this in here?" in about one step.
  • A map pairs each key with a value, like a word with its count, and finds the value for a key in about one step.

How they manage one step, however large they get, is the subject of the lesson on hash tables. This lesson is how to use them.

Sets#

unordered_set<int> seen;
seen.insert(5);
bool has = seen.count(5) > 0;     // true
seen.erase(5);
int size = seen.size();

"Does this array contain a duplicate?" becomes one pass: for each value, if it is already in the set, the answer is yes; otherwise add it.

Maps#

unordered_map<string, int> count;
count["apple"]++;                 // a missing key starts at 0
count["apple"]++;
int apples = count["apple"];      // 2

if (count.count("pear")) { ... }  // check without creating the key

for (auto& [word, n] : count) {
    // every key and its value
}

Two traps, one in each direction:

  • C++: reading count[key] for a key that is not there quietly adds it with the value 0. Handy for counting, surprising when you only meant to look. Use count.count(key) or count.find(key) to check without changing anything.
  • Python: reading count[key] for a key that is not there raises an error. Use count.get(key, 0), or collections.defaultdict(int), which starts missing keys at 0.

And in C#

C has no map or set in its standard library. For these problems there are two good substitutes, and you will reach for them in the other languages too because they are faster still.

When the values are small, use an array as the map. The value is the index.

// Count each lowercase letter in a string.
int count[26] = {0};
for (int i = 0; s[i] != '\0'; i++) {
    count[s[i] - 'a']++;
}
// count[0] is how many 'a', count[25] how many 'z'.

That is how Valid anagram is usually solved: count the letters of both words, and compare the two arrays of 26.

When the values are large, sort first. After sorting, equal values sit next to each other, so a duplicate is simply two neighbours that match. That costs n log n rather than n, which is almost always fast enough.

Order is not kept#

unordered_map, HashMap and HashSet give you their contents in no particular order, and the order can change as they grow. Never rely on it. If you need keys in sorted order, use map in C++ or TreeMap in Java, which keep keys sorted at a small extra cost. Python's dict does keep the order in which keys were first added, but a set does not.

Try it#

Contains duplicate is a set. Valid anagram is two counts compared. Two sum is a map from each value to where it was seen, and the lesson on hash maps for lookups walks through exactly why that turns a slow answer into a fast one.

Practise it

The same idea, in the order it is worth meeting it.

  1. Not startedContains duplicateEasy
  2. Not startedValid anagramEasy
  3. Not startedTwo sumEasy
Create a free account to track your progress