Hash Map(可変長版)
(data-structure/hash-map-variable-length.hpp)
- View this file on GitHub
- Last update: 2020-12-05 07:59:51+09:00
- Include:
#include "data-structure/hash-map-variable-length.hpp"
Hash Map
ハッシュマップとは、$\mathrm{O}(1)$で一点更新・一点取得を行うことのできるデータ構造である。
概要
ハッシュマップとは、ハッシュをkeyとして値を管理することで、keyの値の範囲によらず一点取得・更新が$\mathrm{O}(1)$で行えるデータ構造を実現したものである。
同様のデータ構造にC++のunordered_map
があるが、unordered_map
はハッシュ衝突の脆弱性があり、CodeForcesのようなHackの存在するコンテストで無造作に使用するのは危険である。(参考)
このライブラリでは最低限の機能を持ったHash Mapを実装している。実装には大きく分けて連鎖法と開番地法があるが、開番地法の方がメモリの消費が少なく定数倍もよい。yosupo judgeの提出は以下の通りで、入出力の時間も考慮すると開番地法はunordered_map
のおよそ4倍程度の速さで動作しているとわかる。
- 自作Hash Map(開番地法) 100ms
- 自作Hash Map(連鎖法) 146ms
- ハッシュ衝突の対策をしたunordered_map 263ms
- (入出力にかかる時間は最大50ms程度と考えられる。)
Required by
動的二次元Binary Indexed Tree (data-structure-2d/dynamic-binary-indexed-tree-2d.hpp)
動的Binary Indexed Tree (data-structure/dynamic-binary-indexed-tree.hpp)
segment-tree/dynamic-li-chao-tree.hpp
Verified with
verify/verify-yosupo-ds/yosupo-dynamic-li-chao-tree.test.cpp
verify/verify-yosupo-ds/yosupo-hash-map-variable-length.test.cpp
verify/verify-yosupo-ds/yosupo-point-add-rectangle-sum-bit2d.test.cpp
Code
#pragma once
template <typename Key, typename Val>
struct HashMap {
using u32 = uint32_t;
using u64 = uint64_t;
u32 cap, s;
vector<Key> keys;
vector<Val> vals;
vector<bool> flag;
u64 r;
u32 shift;
Val DefaultValue;
static u64 rng() {
u64 m = chrono::duration_cast<chrono::nanoseconds>(
chrono::high_resolution_clock::now().time_since_epoch())
.count();
m ^= m >> 16;
m ^= m << 32;
return m;
}
void reallocate() {
cap <<= 1;
vector<Key> k(cap);
vector<Val> v(cap);
vector<bool> f(cap);
u32 sh = shift - 1;
for (int i = 0; i < (int)flag.size(); i++) {
if (flag[i]) {
u32 hash = (u64(keys[i]) * r) >> sh;
while (f[hash]) hash = (hash + 1) & (cap - 1);
k[hash] = keys[i];
v[hash] = vals[i];
f[hash] = 1;
}
}
keys.swap(k);
vals.swap(v);
flag.swap(f);
--shift;
}
explicit HashMap()
: cap(8),
s(0),
keys(cap),
vals(cap),
flag(cap),
r(rng()),
shift(64 - __lg(cap)),
DefaultValue(Val()) {}
Val& operator[](const Key& i) {
u32 hash = (u64(i) * r) >> shift;
while (true) {
if (!flag[hash]) {
if (s + s / 4 >= cap) {
reallocate();
return (*this)[i];
}
keys[hash] = i;
flag[hash] = 1;
++s;
return vals[hash] = DefaultValue;
}
if (keys[hash] == i) return vals[hash];
hash = (hash + 1) & (cap - 1);
}
}
// exist -> return pointer of Val
// not exist -> return nullptr
const Val* find(const Key& i) const {
u32 hash = (u64(i) * r) >> shift;
while (true) {
if (!flag[hash]) return nullptr;
if (keys[hash] == i) return &(vals[hash]);
hash = (hash + 1) & (cap - 1);
}
}
// return vector< pair<const Key&, val& > >
vector<pair<Key, Val>> enumerate() const {
vector<pair<Key, Val>> ret;
for (u32 i = 0; i < cap; ++i)
if (flag[i]) ret.emplace_back(keys[i], vals[i]);
return ret;
}
int size() const { return s; }
// set default_value
void set_default(const Val& val) { DefaultValue = val; }
};
/**
* @brief Hash Map(可変長版)
* @docs docs/data-structure/hash-map.md
*/
#line 2 "data-structure/hash-map-variable-length.hpp"
template <typename Key, typename Val>
struct HashMap {
using u32 = uint32_t;
using u64 = uint64_t;
u32 cap, s;
vector<Key> keys;
vector<Val> vals;
vector<bool> flag;
u64 r;
u32 shift;
Val DefaultValue;
static u64 rng() {
u64 m = chrono::duration_cast<chrono::nanoseconds>(
chrono::high_resolution_clock::now().time_since_epoch())
.count();
m ^= m >> 16;
m ^= m << 32;
return m;
}
void reallocate() {
cap <<= 1;
vector<Key> k(cap);
vector<Val> v(cap);
vector<bool> f(cap);
u32 sh = shift - 1;
for (int i = 0; i < (int)flag.size(); i++) {
if (flag[i]) {
u32 hash = (u64(keys[i]) * r) >> sh;
while (f[hash]) hash = (hash + 1) & (cap - 1);
k[hash] = keys[i];
v[hash] = vals[i];
f[hash] = 1;
}
}
keys.swap(k);
vals.swap(v);
flag.swap(f);
--shift;
}
explicit HashMap()
: cap(8),
s(0),
keys(cap),
vals(cap),
flag(cap),
r(rng()),
shift(64 - __lg(cap)),
DefaultValue(Val()) {}
Val& operator[](const Key& i) {
u32 hash = (u64(i) * r) >> shift;
while (true) {
if (!flag[hash]) {
if (s + s / 4 >= cap) {
reallocate();
return (*this)[i];
}
keys[hash] = i;
flag[hash] = 1;
++s;
return vals[hash] = DefaultValue;
}
if (keys[hash] == i) return vals[hash];
hash = (hash + 1) & (cap - 1);
}
}
// exist -> return pointer of Val
// not exist -> return nullptr
const Val* find(const Key& i) const {
u32 hash = (u64(i) * r) >> shift;
while (true) {
if (!flag[hash]) return nullptr;
if (keys[hash] == i) return &(vals[hash]);
hash = (hash + 1) & (cap - 1);
}
}
// return vector< pair<const Key&, val& > >
vector<pair<Key, Val>> enumerate() const {
vector<pair<Key, Val>> ret;
for (u32 i = 0; i < cap; ++i)
if (flag[i]) ret.emplace_back(keys[i], vals[i]);
return ret;
}
int size() const { return s; }
// set default_value
void set_default(const Val& val) { DefaultValue = val; }
};
/**
* @brief Hash Map(可変長版)
* @docs docs/data-structure/hash-map.md
*/