--- id: intro-ordered title: "Introduction to Ordered Sets" author: Darren Yao, Benjamin Qi prerequisites: - Bronze - Unordered Maps & Sets description: "" frequency: 2 --- import { Problem } from "../models"; export const metadata = { problems: { standard: [ new Problem("CSES", "Concert Tickets", "1091", "Easy", false, ["iterators"], "just do upper_bound"), new Problem("CSES", "Traffic Lights", "1163", "Normal", false, ["set"], "just insert into set one at a time"), ], } }; Sets, Maps, Set Iterators
In **ordered** sets and maps, the entries are sorted in order of key. Insertions, deletions, and searches are all $O(\log N)$, where $N$ is the number of elements in the set or map, but accessing or removing the next key higher or lower than some input $k$ is also supported. ## Ordered Sets ### C++ As well as those supported by `unordered_set`, the ordered set also allows four additional operations: `begin()`, which returns an iterator to the lowest element in the set, `end()`, which returns an iterator to the highest element in the set, `lower_bound`, which returns an iterator to the least element greater than or equal to some element `k`, and `upper_bound`, which returns an iterator to the least element strictly greater than some element `k`. ```cpp set s; s.insert(1); // [1] s.insert(14); // [1, 14] s.insert(9); // [1, 9, 14] s.insert(2); // [1, 2, 9, 14] cout << *s.upper_bound(7) << '\n'; // 9 cout << *s.upper_bound(9) << '\n'; // 14 cout << *s.lower_bound(5) << '\n'; // 9 cout << *s.lower_bound(9) << '\n'; // 9 cout << *s.begin() << '\n'; // 1 auto it = s.end(); cout << *(--it) << '\n'; // 14 s.erase(s.upper_bound(6)); // [1, 2, 14] ``` The primary limitation of the ordered set is that we can't efficiently access the $k^{th}$ largest element in the set, or find the number of elements in the set greater than some arbitrary $x$. These operations can be handled using a data structure called an order statistic tree (see Gold - Binary Indexed Trees). ### Java As well as those supported by the unordered set, the ordered set also allows four additional operations: `first`, which returns the lowest element in the set, `last`, which returns the highest element in the set, `lower`, which returns the greatest element strictly less than some element, and `higher`, which returns the least element strictly greater than it. ```java TreeSet set = new TreeSet(); set.add(1); // [1] set.add(14); // [1, 14] set.add(9); // [1, 9, 14] set.add(2); // [1, 2, 9, 14] System.out.println(set.higher(7)); // 9 System.out.println(set.higher(9)); // 14 System.out.println(set.lower(5)); // 2 System.out.println(set.first()); // 1 System.out.println(set.last()); // 14 set.remove(set.higher(6)); // [1, 2, 14] System.out.println(set.higher(23); // ERROR, no such element exists ``` ## Ordered Maps ### C++ The ordered map supports all of the operations that an unordered map supports, and additionally supports `lower_bound` and `upper_bound`, returning the iterator pointing to the lowest entry not less than the specified key, and the iterator pointing to the lowest entry strictly greater than the specified key respectively. ```cpp map m; m[3] = 5; // [(3, 5)] m[11] = 4; // [(3, 5); (11, 4)] m[10] = 491; // [(3, 5); (10, 491); (11, 4)] cout << m.lower_bound(10)->first << " " << m.lower_bound(10)->second << '\n'; // 10 491 cout << m.upper_bound(10)->first << " " << m.upper_bound(10)->second << '\n'; // 11 4 m.erase(11); // [(3, 5); (10, 491)] if (m.upper_bound(10) == m.end()) { cout << "end" << endl; // Prints end } ``` ### Java The ordered map supports all of the operations that an unordered map supports, and additionally supports `firstKey` / `firstEntry` and `lastKey` /` lastEntry`, returning the lowest key/entry and the highest key/entry, as well as `higherKey` /` higherEntry` and `lowerKey` / `lowerEntry`, returning the lowest key/entry strictly higher than the specified key, or the highest key/entry strictly lower than the specified key. ```java TreeMap map = new TreeMap(); map.put(3, 5); // [(3, 5)] map.put(11, 4); // [(3, 5); (11, 4)] map.put(10, 491); // [(3, 5); (10, 491); (11, 4)] System.out.println(map.firstKey()); // 3 System.out.println(map.firstEntry()); // (3, 5) System.out.println(map.lastEntry()); // (11, 4) System.out.println(map.higherEntry(4)); // (10, 491) map.remove(11); // [(3, 5); (10, 491)] System.out.println(map.lowerKey(4)); // 3 System.out.println(map.lowerKey(3)); // ERROR ``` ## Multisets Lastly, there is the multiset, which is essentially a sorted set that allows multiple copies of the same element. ### C++ In addition to all of the regular set operations, the multiset `count()` method returns the number of times an element is present in the multiset. However, this method takes time **linear** in the number of matches so you shouldn't use it in a contest. The `begin()`, `end()`, `lower_bound()`, and `upper_bound()` operations work the same way they do in the normal sorted set. **Warning:** If you want to remove a value *once*, make sure to use `multiset.erase(multiset.find(val))` rather than `multiset.erase(val)`. The latter will remove *all* instances of `val`. ```cpp multiset ms; ms.insert(1); // [1] ms.insert(14); // [1, 14] ms.insert(9); // [1, 9, 14] ms.insert(2); // [1, 2, 9, 14] ms.insert(9); // [1, 2, 9, 9, 14] ms.insert(9); // [1, 2, 9, 9, 9, 14] cout << ms.count(4) << '\n'; // 0 cout << ms.count(9) << '\n'; // 3 cout << ms.count(14) << '\n'; // 1 ms.erase(ms.find(9)); cout << ms.count(9) << '\n'; // 2 ms.erase(9); cout << ms.count(9) << '\n'; // 0 ``` ### Java While there is no `Multiset` in Java, we can implement one using the `TreeMap` from values to their respective frequencies. We declare the `TreeMap` implementation globally so that we can write functions for adding and removing elements from it. ```java static TreeMap multiset = new TreeMap(); public static void main(String[] args){ ... } static void add(int x){ if(multiset.containsKey(x)){ multiset.put(x, multiset.get(x) + 1); } else { multiset.put(x, 1); } } static void remove(int x){ multiset.put(x, multiset.get(x) - 1); if(multiset.get(x) == 0){ multiset.remove(x); } } ``` The first, last, higher, and lower operations still function as intended; just use `firstKey`, `lastKey`, `higherKey`, and `lowerKey` respectively. ## Standard Do roughly the first half of the Sorting and Searching section in the [CSES Problem Set](https://cses.fi/problemset/).