Created
November 4, 2015 22:05
-
-
Save goldsborough/443cbbd719450d642391 to your computer and use it in GitHub Desktop.
A hash-table using separate-chaining.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
template<typename Key, typename Value> | |
class SeparateChainingHashTable | |
{ | |
public: | |
using size_t = std::size_t; | |
using pre_hash_t = std::function<size_t(const Key&)>; | |
static const size_t minimum_capacity; | |
SeparateChainingHashTable(const pre_hash_t& pre_hash = std::hash<Key>(), | |
size_t capacity = minimum_capacity, | |
size_t load_factor = 4) | |
: _size(0) | |
, _capacity(capacity/load_factor) | |
, _threshold(capacity) | |
, _load_factor(load_factor) | |
, _pre_hash(pre_hash) | |
, _nodes(new Node*[_capacity]) | |
{ | |
std::fill(_nodes, _nodes + _capacity, nullptr); | |
} | |
SeparateChainingHashTable(const std::initializer_list<std::pair<Key, Value>>& list, | |
const pre_hash_t& pre_hash = std::hash<Key>(), | |
size_t capacity = minimum_capacity, | |
size_t load_factor = 4) | |
: SeparateChainingHashTable(pre_hash, | |
std::max(capacity, list.size()), | |
load_factor) | |
{ | |
for (const auto& item : list) | |
{ | |
insert(item.first, item.second); | |
} | |
} | |
SeparateChainingHashTable(const SeparateChainingHashTable& other) | |
: _size(other._size) | |
, _capacity(other._capacity) | |
, _threshold(other._threshold) | |
, _pre_hash(other._pre_hash) | |
, _load_factor(other._load_factor) | |
, _nodes(new Node*[other._capacity]) | |
{ | |
for (size_t i = 0; i < _capacity; ++i) | |
{ | |
for (auto node = other._nodes[i]; node; node = node->next) | |
{ | |
auto new_node = new Node(node->key, | |
node->value, | |
_nodes[i]); | |
_nodes[i] = new_node; | |
} | |
} | |
} | |
SeparateChainingHashTable(SeparateChainingHashTable&& other) | |
: SeparateChainingHashTable() | |
{ | |
swap(other); | |
} | |
SeparateChainingHashTable& operator=(SeparateChainingHashTable other) | |
{ | |
swap(other); | |
return *this; | |
} | |
void swap(SeparateChainingHashTable& other) | |
{ | |
// Enable ADL | |
using std::swap; | |
swap(_size, other._size); | |
swap(_capacity, other._capacity); | |
swap(_threshold, other._threshold); | |
swap(_load_factor, other._load_factor); | |
swap(_pre_hash, other._pre_hash); | |
swap(_nodes, other._nodes); | |
} | |
friend void swap(SeparateChainingHashTable& first, | |
SeparateChainingHashTable& second) | |
{ | |
first.swap(second); | |
} | |
~SeparateChainingHashTable() | |
{ | |
for (size_t i = 0; i < _capacity; ++i) | |
{ | |
for (auto node = _nodes[i]; node; ) | |
{ | |
auto next = node->next; | |
delete node; | |
node = next; | |
} | |
} | |
delete [] _nodes; | |
} | |
void insert(const Key& key, const Value& value) | |
{ | |
auto index = _hash(key); | |
for (auto node = _nodes[index]; node; node = node->next) | |
{ | |
if (node->key == key) | |
{ | |
node->value = value; | |
return; | |
} | |
} | |
auto node = new Node(key, value, _nodes[index]); | |
_nodes[index] = node; | |
if (++_size == _threshold) _resize(); | |
} | |
Value& get(const Key& key) | |
{ | |
auto node = _find(key); | |
if (! node) | |
{ | |
throw std::invalid_argument("No such key!"); | |
} | |
return node->value; | |
} | |
const Value& get(const Key& key) const | |
{ | |
auto node = _find(key); | |
if (! node) | |
{ | |
throw std::invalid_argument("No such key!"); | |
} | |
return node->value; | |
} | |
bool contains(const Key& key) | |
{ | |
return _find(key) != nullptr; | |
} | |
void erase(const Key& key) | |
{ | |
auto index = _hash(key); | |
Node* node = _nodes[index]; | |
Node* previous = nullptr; | |
for ( ; node; previous = node, node = node->next) | |
{ | |
if (key == node->key) | |
{ | |
if (previous) previous->next = node->next; | |
else _nodes[index] = node->next; | |
delete node; | |
if (--_size == _threshold/4) _resize(); | |
} | |
} | |
throw std::invalid_argument("No such key!"); | |
} | |
void clear() | |
{ | |
for (size_t i = 0; i < _capacity; ++i) | |
{ | |
for (auto node = _nodes[i]; node; ) | |
{ | |
auto next = node->next; | |
delete node; | |
node = next; | |
} | |
} | |
delete [] _nodes; | |
_size = 0; | |
_threshold = minimum_capacity; | |
_capacity = _threshold / _load_factor; | |
_nodes = new Node*[_capacity]; | |
std::fill(_nodes, _nodes + _capacity, nullptr); | |
} | |
Value& operator[](const Key& key) | |
{ | |
auto index = _hash(key); | |
for (auto node = _nodes[index]; node; node = node->next) | |
{ | |
if (key == node->key) return node->value; | |
} | |
auto node = new Node(key, Value(), _nodes[index]); | |
_nodes[index] = node; | |
if (++_size == _threshold) _resize(); | |
return node->value; | |
} | |
void rehash(size_t buckets) | |
{ | |
resize(buckets * _load_factor); | |
} | |
void resize(size_t new_size) | |
{ | |
_threshold = std::max(new_size, minimum_capacity); | |
auto old_capacity = _capacity; | |
_capacity = _threshold / _load_factor; | |
auto old = _nodes; | |
_nodes = new Node*[_capacity]; | |
std::fill(_nodes, _nodes + _capacity, nullptr); | |
_rehash(old, old_capacity); | |
delete [] old; | |
} | |
size_t load_factor() const | |
{ | |
return _load_factor; | |
} | |
void load_factor(size_t alpha) | |
{ | |
_load_factor = alpha; | |
resize(_size); | |
} | |
size_t size() const | |
{ | |
return _size; | |
} | |
bool is_empty() const | |
{ | |
return _size == 0; | |
} | |
private: | |
struct Node | |
{ | |
Node(const Key& key_, | |
const Value& value_ = Value(), | |
Node* next_ = nullptr) | |
: key(key_) | |
, value(value_) | |
, next(next_) | |
{ } | |
Key key; | |
Value value; | |
Node* next; | |
}; | |
Node* _find(const Key& key) const | |
{ | |
auto index = _hash(key); | |
for (auto node = _nodes[index]; node; node = node->next) | |
{ | |
if (key == node->key) return node; | |
} | |
return nullptr; | |
} | |
size_t _hash(const Key& key) const | |
{ | |
return _pre_hash(key) % _capacity; | |
} | |
void _resize() | |
{ | |
resize(2 * _size); | |
} | |
void _rehash(Node** old, size_t old_capacity) | |
{ | |
for (size_t i = 0; i < old_capacity; ++i) | |
{ | |
for (auto node = old[i]; node; ) | |
{ | |
auto index = _hash(node->key); | |
auto next = node->next; | |
node->next = _nodes[index]; | |
_nodes[index] = node; | |
node = next; | |
} | |
} | |
} | |
size_t _size; | |
size_t _capacity; | |
size_t _threshold; | |
size_t _load_factor; | |
pre_hash_t _pre_hash; | |
Node** _nodes; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment