176 lines
No EOL
6.8 KiB
Text
176 lines
No EOL
6.8 KiB
Text
---
|
|
id: intro-ordered
|
|
title: "Introduction to Ordered Maps & 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", "Towers", "1073", "Easy", false, ["multiset", "greedy"]),
|
|
new Problem("CSES", "Traffic Lights", "1163", "Normal", false, ["set"]),
|
|
new Problem("CSES", "Room Allocation", "1164", "Normal", false, ["multiset", "greedy"]),
|
|
],
|
|
}
|
|
};
|
|
|
|
<resources>
|
|
<resource source="CPH" title="4.2 to 4.4: Data Structures" starred>Sets, Maps, Set Iterators</resource>
|
|
</resources>
|
|
|
|
<br />
|
|
|
|
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<int> 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<Integer> set = new TreeSet<Integer>();
|
|
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<int, int> 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<Integer, Integer> map = new TreeMap<Integer, Integer>();
|
|
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<int> 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<Integer, Integer> multiset = new TreeMap<Integer, Integer>();
|
|
|
|
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/).
|
|
|
|
<problems-list problems={metadata.problems.standard} /> |