2.6.2. Map and Set

In C++ STL, map and set, including multimap and multiset, are basically binary search trees. The time complexities of search, insert and delete are O(LogN). The difference is set is used to store only keys while map is used to store key value pairs.

../_images/map_.png
../_images/set.png

To use any object as a key in map/set, we have to tell the map/set how to compare the two objects using a comparison function in the third parameter of the template. We can:

  1. Define less-than operator< inside the object class

  2. Use a comparison object

  3. Specialize std::less function

One example is:

 1template<typename T1, typename T2>
 2struct Node
 3{
 4    T1 x;
 5    T2 y;
 6    // constructor
 7    Node(T1 x, T2 y): x(x), y(y) {}
 8    // overload `<` operator to use a `Node` object as a key in a `std::map`
 9    // It returns true if the current object appears before the specified object
10    bool operator<(const Node &ob) const {
11        return x < ob.x || (x == ob.x && y < ob.y);
12    }
13};

Or we can use the built-in transparent functor greater<>:

1set<int, greater<>> s = {1, 2, 3, 4};
2for (int i: s) cout << i; // output: 4321
3map<int, char, greater<>> m = {{1, 'a'},
4                               {2, 'b'}};
5for (auto &[x, y]: m) cout << x; // output: 21

Functions to know: erase, count, find, equal_range, lower_bound, upper_bound, insert/emplace.

  • 🐾 How to delete one item from multimap?

Erasing by value may cause unexpected result because multimap could have duplicate values. The correct way is to erase by iterator.

nx = a_map.erase(a_map.find(x))

If you want to delete the first item in case there are duplicate values, you can use:

nx = a_map.erase(a_map.lower_bound(x))

The same applied to multiset. Also note the erase method returns the next iterator of the deleted iterator.

2.6.2.1. Entity Spotter

input: string -> "Apple Park is the corporate headquarters of Apple Inc"
        idx   ->  0     1    2  3   4         5            6  7     8
        dict: {"Apple", "Apple Park", "corporate"}
output: {"Apple":{0,1},{7,8}, "Apple Park":{0,2}, "corporate":{4,5}}
Code Block 2.6.8 Entity Spotter
 1map<string, vector<pair<int, int>>> get_range(string s, const unordered_set<string>& dict){
 2  map<string, vector<pair<int, int>>> res;
 3  auto vi = split(s);
 4  for (int i = 0; i < vi.size(); i++) {
 5    string n = vi[i];
 6    if (dict.count(n)) res[n].emplace_back(i, i + 1);
 7    for (int j = i + 1; j < vi.size(); j++) {
 8      n += " " + vi[j];
 9      if (dict.find(n) != dict.end())
10        res[n].emplace_back(i, j + 1);
11    }
12  }
13  return res;
14}

2.6.2.2. Dynamic Bounding Box

Implement a class, on the x-y plane, there are no points at the beginning, constantly add and remove points, the points are represented by coordinates (x, y), each step returns a minimum box to frame all the points, the box is parallel to x and y axis.

^                                 ^                                 ^
|                                 |       +z(3,5)                   |       +z(3,5)
|                                 |                                 |
|           +y(4,2)        -->    |           +y(4,2)        -->    |
|                                 |                                 |
|     +x(2,1)                     |     +x(2,1)                     |     +x(2,1)
|---------------------->          |---------------------->          |---------------------->
Bounding Box: {{2,1},{4,2}}        {{2,1},{4,5}}                     {{2,1},{3,5}}
Code Block 2.6.9 Dynamic Bounding Box
 1using PII = pair<int, int>;
 2
 3struct comp_x{
 4  bool operator()(const PII& a, const PII &b) const {
 5    return a.first < b.first;
 6  }
 7};
 8
 9struct comp_y{
10  bool operator()(const PII& a, const PII &b) const {
11    return a.second < b.second;
12  }
13};
14
15class dynamic_bbox {
16  multiset<PII, comp_x> points_by_x;
17  multiset<PII, comp_y> points_by_y;
18public:
19  void add_point(int x, int y) {
20    points_by_x.emplace(x, y);
21    points_by_y.emplace(x, y);
22  }
23  void remove_point(int x, int y){
24    if(points_by_x.count({x,y})){
25      points_by_x.erase(points_by_x.find({x,y}));
26      points_by_y.erase(points_by_y.find({x,y}));
27    }
28  }
29  pair<PII, PII> get_bbox() {
30    int min_x = points_by_x.begin()->first;
31    int max_x = points_by_x.rbegin()->first;
32    int min_y = points_by_y.begin()->second;
33    int max_y = points_by_y.rbegin()->second;
34    return {{min_x, min_y},{max_x, max_y}};
35  }
36};