Miscellany / Policy-Based Data Structures
8.6 Policy-Based Data Structures
8-Miscellany/8.6_Policy-Based_Data_Structures.cpp
GNU policy-based data structures provide ordered sets and maps with order statistics. They are not part of the C++ standard library, but they are available on many GNU C++ contest judges.
OrderedSet<T>behaves like a set withfind_by_order(k)andorder_of_key(x).OrderedMap<K, V>behaves like a map withfind_by_order(k)andorder_of_key(x).HashMap<K, V>andHashSet<K>are fast hash tables usinggp_hash_table. The defaultSplitMix64Hasheris for integer-like keys; pass a custom hash for other key types. It randomizes the hash seed to avoid weak adversarial integer hashes.OrderedMultiset<T>stores duplicates by pairing each value with a unique ID.OrderedMultimap<K, V>stores duplicate keys by pairing each key with a unique ID.PairingHeap<T>is a meldable priority queue;push()returns an iterator that can be passed tomodify()orerase().BinaryHeap<T>,BinomialHeap<T>, andRcBinomialHeap<T>are alternative GNU priority queue tags with the same interface.
The order statistic operations are 0-indexed. The priority queue join() operation melds another heap into the current one. Since this depends on __gnu_pbds, keep a standard library fallback in mind when portability matters.
Implementation
#include <cassert>
#include <chrono>
#include <cstdint>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <functional>
#include <utility>
template<class K, class V, class Compare = std::less<K>>
using OrderedMap = __gnu_pbds::tree<
K, V, Compare, __gnu_pbds::rb_tree_tag, __gnu_pbds::tree_order_statistics_node_update>;
template<class T, class Compare = std::less<T>>
using OrderedSet = OrderedMap<T, __gnu_pbds::null_type, Compare>;
struct SplitMix64Hasher {
// SplitMix64 mixer.
static uint64_t mix64(uint64_t x) {
x += 0x9e3779b97f4a7c15ULL;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL;
x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL;
return x ^ (x >> 31);
}
size_t operator()(uint64_t x) const {
static const uint64_t FIXED_RANDOM =
std::chrono::steady_clock::now().time_since_epoch().count();
return mix64(x + FIXED_RANDOM);
}
};
template<class K, class V, class Hash = SplitMix64Hasher>
using HashMap = __gnu_pbds::gp_hash_table<K, V, Hash>;
template<class K, class Hash = SplitMix64Hasher>
using HashSet = __gnu_pbds::gp_hash_table<K, __gnu_pbds::null_type, Hash>;
template<class T>
class OrderedMultiset {
OrderedSet<std::pair<T, int>> tree;
int timer = 0;
public:
void insert(const T &x) { tree.insert({x, timer++}); }
bool erase_one(const T &x) {
auto it = tree.lower_bound({x, -1});
if (it == tree.end() || it->first != x) {
return false;
}
tree.erase(it);
return true;
}
int order_of_key(const T &x) const { return tree.order_of_key({x, -1}); }
T find_by_order(int k) const { return tree.find_by_order(k)->first; }
int size() const { return static_cast<int>(tree.size()); }
};
template<class K, class V>
class OrderedMultimap {
OrderedMap<std::pair<K, int>, V> tree;
int timer = 0;
public:
void insert(const K &key, const V &value) { tree.insert({{key, timer++}, value}); }
bool erase_one(const K &key) {
auto it = tree.lower_bound({key, -1});
if (it == tree.end() || it->first.first != key) {
return false;
}
tree.erase(it);
return true;
}
int order_of_key(const K &key) const { return tree.order_of_key({key, -1}); }
std::pair<K, V> find_by_order(int k) const {
auto it = tree.find_by_order(k);
return {it->first.first, it->second};
}
int size() const { return static_cast<int>(tree.size()); }
};
template<class T, class Compare = std::less<T>>
using PairingHeap = __gnu_pbds::priority_queue<T, Compare, __gnu_pbds::pairing_heap_tag>;
template<class T, class Compare = std::less<T>>
using BinaryHeap = __gnu_pbds::priority_queue<T, Compare, __gnu_pbds::binary_heap_tag>;
template<class T, class Compare = std::less<T>>
using BinomialHeap = __gnu_pbds::priority_queue<T, Compare, __gnu_pbds::binomial_heap_tag>;
template<class T, class Compare = std::less<T>>
using RcBinomialHeap = __gnu_pbds::priority_queue<T, Compare, __gnu_pbds::rc_binomial_heap_tag>;
Example Usage
int main() {
OrderedSet<int> s;
s.insert(10);
s.insert(20);
s.insert(30);
assert(*s.find_by_order(1) == 20);
assert(s.order_of_key(25) == 2);
HashMap<int, int> hm;
hm[10] = 1;
hm[20] = 2;
assert(hm[10] == 1);
HashSet<int> hs;
hs.insert(7);
assert(hs.find(7) != hs.end());
OrderedMultiset<int> ms;
ms.insert(5);
ms.insert(5);
ms.insert(7);
assert(ms.size() == 3);
assert(ms.order_of_key(7) == 2);
assert(ms.find_by_order(1) == 5);
assert(ms.erase_one(5));
assert(ms.order_of_key(7) == 1);
OrderedMultimap<int, char> mp;
mp.insert(5, 'a');
mp.insert(5, 'b');
mp.insert(7, 'c');
assert(mp.order_of_key(7) == 2);
assert(mp.find_by_order(1).first == 5);
assert(mp.erase_one(5));
assert(mp.order_of_key(7) == 1);
PairingHeap<int> h1, h2;
auto it = h1.push(10);
h1.push(5);
h1.modify(it, 20);
h2.push(7);
h1.join(h2);
assert(h1.top() == 20);
h1.pop();
assert(h1.top() == 7);
return 0;
}
/*
GNU policy-based data structures provide ordered sets and maps with order statistics. They are not
part of the C++ standard library, but they are available on many GNU C++ contest judges.
- `OrderedSet<T>` behaves like a set with `find_by_order(k)` and `order_of_key(x)`.
- `OrderedMap<K, V>` behaves like a map with `find_by_order(k)` and `order_of_key(x)`.
- `HashMap<K, V>` and `HashSet<K>` are fast hash tables using `gp_hash_table`. The default
`SplitMix64Hasher` is for integer-like keys; pass a custom hash for other key types. It randomizes
the hash seed to avoid weak adversarial integer hashes.
- `OrderedMultiset<T>` stores duplicates by pairing each value with a unique ID.
- `OrderedMultimap<K, V>` stores duplicate keys by pairing each key with a unique ID.
- `PairingHeap<T>` is a meldable priority queue; `push()` returns an iterator that can be passed to
`modify()` or `erase()`.
- `BinaryHeap<T>`, `BinomialHeap<T>`, and `RcBinomialHeap<T>` are alternative GNU priority queue
tags with the same interface.
The order statistic operations are 0-indexed. The priority queue `join()` operation melds another
heap into the current one. Since this depends on `__gnu_pbds`, keep a standard library fallback in
mind when portability matters.
Time Complexity:
- O(log n) per operation.
Space Complexity:
- O(n).
*/
#include <cassert>
#include <chrono>
#include <cstdint>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <functional>
#include <utility>
template<class K, class V, class Compare = std::less<K>>
using OrderedMap = __gnu_pbds::tree<
K, V, Compare, __gnu_pbds::rb_tree_tag, __gnu_pbds::tree_order_statistics_node_update>;
template<class T, class Compare = std::less<T>>
using OrderedSet = OrderedMap<T, __gnu_pbds::null_type, Compare>;
struct SplitMix64Hasher {
// SplitMix64 mixer.
static uint64_t mix64(uint64_t x) {
x += 0x9e3779b97f4a7c15ULL;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL;
x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL;
return x ^ (x >> 31);
}
size_t operator()(uint64_t x) const {
static const uint64_t FIXED_RANDOM =
std::chrono::steady_clock::now().time_since_epoch().count();
return mix64(x + FIXED_RANDOM);
}
};
template<class K, class V, class Hash = SplitMix64Hasher>
using HashMap = __gnu_pbds::gp_hash_table<K, V, Hash>;
template<class K, class Hash = SplitMix64Hasher>
using HashSet = __gnu_pbds::gp_hash_table<K, __gnu_pbds::null_type, Hash>;
template<class T>
class OrderedMultiset {
OrderedSet<std::pair<T, int>> tree;
int timer = 0;
public:
void insert(const T &x) { tree.insert({x, timer++}); }
bool erase_one(const T &x) {
auto it = tree.lower_bound({x, -1});
if (it == tree.end() || it->first != x) {
return false;
}
tree.erase(it);
return true;
}
int order_of_key(const T &x) const { return tree.order_of_key({x, -1}); }
T find_by_order(int k) const { return tree.find_by_order(k)->first; }
int size() const { return static_cast<int>(tree.size()); }
};
template<class K, class V>
class OrderedMultimap {
OrderedMap<std::pair<K, int>, V> tree;
int timer = 0;
public:
void insert(const K &key, const V &value) { tree.insert({{key, timer++}, value}); }
bool erase_one(const K &key) {
auto it = tree.lower_bound({key, -1});
if (it == tree.end() || it->first.first != key) {
return false;
}
tree.erase(it);
return true;
}
int order_of_key(const K &key) const { return tree.order_of_key({key, -1}); }
std::pair<K, V> find_by_order(int k) const {
auto it = tree.find_by_order(k);
return {it->first.first, it->second};
}
int size() const { return static_cast<int>(tree.size()); }
};
template<class T, class Compare = std::less<T>>
using PairingHeap = __gnu_pbds::priority_queue<T, Compare, __gnu_pbds::pairing_heap_tag>;
template<class T, class Compare = std::less<T>>
using BinaryHeap = __gnu_pbds::priority_queue<T, Compare, __gnu_pbds::binary_heap_tag>;
template<class T, class Compare = std::less<T>>
using BinomialHeap = __gnu_pbds::priority_queue<T, Compare, __gnu_pbds::binomial_heap_tag>;
template<class T, class Compare = std::less<T>>
using RcBinomialHeap = __gnu_pbds::priority_queue<T, Compare, __gnu_pbds::rc_binomial_heap_tag>;
/*** Example Usage ***/
int main() {
OrderedSet<int> s;
s.insert(10);
s.insert(20);
s.insert(30);
assert(*s.find_by_order(1) == 20);
assert(s.order_of_key(25) == 2);
HashMap<int, int> hm;
hm[10] = 1;
hm[20] = 2;
assert(hm[10] == 1);
HashSet<int> hs;
hs.insert(7);
assert(hs.find(7) != hs.end());
OrderedMultiset<int> ms;
ms.insert(5);
ms.insert(5);
ms.insert(7);
assert(ms.size() == 3);
assert(ms.order_of_key(7) == 2);
assert(ms.find_by_order(1) == 5);
assert(ms.erase_one(5));
assert(ms.order_of_key(7) == 1);
OrderedMultimap<int, char> mp;
mp.insert(5, 'a');
mp.insert(5, 'b');
mp.insert(7, 'c');
assert(mp.order_of_key(7) == 2);
assert(mp.find_by_order(1).first == 5);
assert(mp.erase_one(5));
assert(mp.order_of_key(7) == 1);
PairingHeap<int> h1, h2;
auto it = h1.push(10);
h1.push(5);
h1.modify(it, 20);
h2.push(7);
h1.join(h2);
assert(h1.top() == 20);
h1.pop();
assert(h1.top() == 7);
return 0;
}