Programming Lesson Plans
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

185 lines
5.0 KiB

3 years ago
  1. # On Sets (and Maps)
  2. Sets are a powerful data structure used to reduce the complexities of many
  3. problems that involve some method of efficient search.
  4. This will cover the basic C++ API for both ordered and unordered sets.
  5. ## gp_hash_table
  6. Generally, the hash function of the c++ `unordered_set` does not work very well
  7. (the specifics can be found at
  8. [this CF Article](https://codeforces.com/blog/entry/60737))
  9. Instead, as the article shows us, we should probably use the `gp_hash_table`
  10. class, which is actually a map. (However, note that the map, in general, has all
  11. of the same functionality that a set has).
  12. In order to use this class, use the following lines at the top of your code:
  13. ```cpp
  14. #include <ext/pb_ds/assoc_container.hpp>
  15. using namespace __gnu_pbds;
  16. ```
  17. ## Unordered Map
  18. An unordered map is a data structure that can query for a value given a key in
  19. O(1) time. It can also set a value to a specific key in O(1) time.
  20. ### Inserting elements into a hash table
  21. ```cpp
  22. gp_hash_table<string, t2> mp;
  23. mp["key"] = value;
  24. ```
  25. The two type arguments given to the constructor are for the types of the keys
  26. and the values respectively.
  27. **Merely** using the key of "key" in an equality operator like this will add the
  28. key "key" to the set of keys that `mp` already has.
  29. ### Finding if an element exists
  30. When a value for a specific key is initialized in a map, it will always be
  31. initialized to a **0-like** value, unless you give it an explicit initialization
  32. as shown above. You might then be tempted to do the following when checking if a
  33. certain key is in the set of keys that the map has:
  34. ```cpp
  35. if (!mp["key"]) {
  36. //code
  37. }
  38. ```
  39. You probably **never want to do this**, as this statement will implicitly add
  40. "key" to the set of keys that the map already has, which is undesirable if
  41. working under tight memory constraints. The correct method is as follows:
  42. ```cpp
  43. if (mp.find("key") != mp.end()) {
  44. // code
  45. }
  46. ```
  47. `mp.end()` points to the element **past** the last key in a map (or hash table),
  48. so if the `find` function yields us this value, it means that the value is not
  49. in the hash table.
  50. ## Regular Map
  51. Although a Hash Table can be quite useful, it is oftentimes limited by the fact
  52. that the objects we need as keys are simply not hashable. If the complexity
  53. allows for it, we can then use a map. A c++ map is a data structure that has
  54. key-value pairs, but locates keys and values by the process of searching for the
  55. keys, which has logarithmic complexity relative to the number of key-value pairs
  56. that the map contains.
  57. In order to declare a map,
  58. ```cpp
  59. map<type1,type2> mp;
  60. ```
  61. The setting and querying methods are the same as before.
  62. ## Applications of a map
  63. A map is used to store **key-value pairs**, and one can exploit this concept in
  64. several different ways.
  65. For instance, let's say that you have a vector of a generic type and you want to
  66. construct a frequency table. (Where I can efficiently query how many of a
  67. certain object exist on a vector.) I can construct a map to be able to
  68. efficiently query this, as shown below:
  69. ```cpp
  70. vector<T> v;
  71. /*
  72. Initialize the vector somehow.
  73. */
  74. map<T, int> freqtable;
  75. for (T x : v) {
  76. freqtable[x]++;
  77. }
  78. ```
  79. One can take advantage, in the case of number types, that a map value will
  80. default to 0. However, the following is less concise, but more conceptually
  81. clear:
  82. ```cpp
  83. map<T, int> freqtable;
  84. for (int i = 0; i < v.size(); ++i) {
  85. T x = v[i];
  86. if (freqtable.find(x) != freqtable.end()) {
  87. freqtable[x]++;
  88. } else {
  89. freqtable = 1;
  90. }
  91. }
  92. ```
  93. For both examples, the type `T` is merely a placeholder, and you would put
  94. whatever type you need for the task at hand in place of it.
  95. ## Set
  96. C++ offers a `set` class which can be useful in several applications.
  97. The advantage of storing data like this is that it provides a way to get the
  98. minimum/maximum of all of the values in a set in logarithmic time.
  99. To declare a set, use the following:
  100. ```cpp
  101. set<T> s;
  102. ```
  103. Preferably, `T` is already comparable (as it is painful to create a comparator
  104. for sets).
  105. ### Adding, Removing, and Finding
  106. In order to add a value to a set:
  107. ```cpp
  108. // s is the set that we declared earlier.
  109. s.insert(value);
  110. ```
  111. In order to remove a value from a set:
  112. ```cpp
  113. s.erase(value);
  114. ```
  115. The `find` function for a set is the same as all data structures before.
  116. ### Finding maximum/minimum.
  117. There are two main functions that are used, `begin()` and `end()`;
  118. #### First element
  119. Note that `begin()` returns a pointer to the minimum element, so you probably
  120. want to do the following instead:
  121. ```cpp
  122. T first_element = *(s.begin());
  123. ```
  124. Pointers are a more advanced topic, but at a basic level, `s.begin()` is telling
  125. us _where_ in memory the first element is, and the asterisk allows us to know
  126. _what_ the first element actually is, based on its location.
  127. #### Last element
  128. As we have covered before, the `end()` method returns the pointer **one after**
  129. the last value. As a result, we need to subtract the pointer that it yields (by
  130. one) in order to get our desired results:
  131. ```cpp
  132. T last_element = *(--s.begin());
  133. ```