572 lines
16 KiB
C
572 lines
16 KiB
C
![]() |
#include <utility>
|
||
|
#include <list>
|
||
|
#include <vector>
|
||
|
#include <functional>
|
||
|
#include <cassert>
|
||
|
#include <iostream>
|
||
|
#include <memory>
|
||
|
#include <utility>
|
||
|
|
||
|
using std::prev;
|
||
|
|
||
|
template<typename T, typename Allocator=std::allocator<T>>
|
||
|
class List {
|
||
|
private:
|
||
|
class Node;
|
||
|
|
||
|
template<bool is_const>
|
||
|
class base_iterator;
|
||
|
public:
|
||
|
using iterator = base_iterator<false>;
|
||
|
using const_iterator = base_iterator<true>;
|
||
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||
|
|
||
|
explicit List(const Allocator& alloc = Allocator()) : allocator_node_(alloc) {
|
||
|
AllocateEnd();
|
||
|
}
|
||
|
|
||
|
List(size_t count, const T& value, const Allocator& alloc = Allocator()) : allocator_node_(alloc) {
|
||
|
AllocateEnd();
|
||
|
|
||
|
for (size_t i = 0; i < count; ++i) {
|
||
|
Node* tmp = allocator_node_.allocate(1);
|
||
|
alloc_traits_::construct(allocator_node_, tmp, value);
|
||
|
Link(end_->l, tmp);
|
||
|
}
|
||
|
size_ = count;
|
||
|
}
|
||
|
|
||
|
List(size_t count, const Allocator& alloc = Allocator()) : allocator_node_(alloc) {
|
||
|
AllocateEnd();
|
||
|
|
||
|
for (size_t i = 0; i < count; ++i) {
|
||
|
Node* tmp = allocator_node_.allocate(1);
|
||
|
alloc_traits_::construct(allocator_node_, tmp);
|
||
|
Link(end_->l, tmp);
|
||
|
}
|
||
|
size_ = count;
|
||
|
}
|
||
|
|
||
|
List(const List& another) {
|
||
|
allocator_node_ = alloc_traits_::select_on_container_copy_construction(another.allocator_node_);
|
||
|
AllocateEnd();
|
||
|
for (const T& i: another) {
|
||
|
Node* tmp = allocator_node_.allocate(1);
|
||
|
alloc_traits_::construct(allocator_node_, tmp, i);
|
||
|
Link(end_->l, tmp);
|
||
|
}
|
||
|
size_ = another.size();
|
||
|
}
|
||
|
|
||
|
List(List&& another) : end_(std::move(another.end_)), size_(std::move(another.size_)) {
|
||
|
another.end_ = nullptr;
|
||
|
}
|
||
|
|
||
|
List& operator=(const List& another) {
|
||
|
if (this == &another) return *this;
|
||
|
if (alloc_traits_::propagate_on_container_copy_assignment::value && allocator_node_ != another.allocator_node_) {
|
||
|
allocator_node_ = another.allocator_node_;
|
||
|
}
|
||
|
|
||
|
auto it1 = begin();
|
||
|
auto it2 = another.begin();
|
||
|
for (size_t i = 0; i < std::min(another.size(), size()); ++i) {
|
||
|
*it1 = *it2;
|
||
|
++it1;
|
||
|
++it2;
|
||
|
}
|
||
|
if (another.size() > size()) {
|
||
|
while (size() != another.size()) {
|
||
|
push_back(*it2);
|
||
|
++it2;
|
||
|
}
|
||
|
} else {
|
||
|
while (size() != another.size()) {
|
||
|
pop_back();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
List& operator=(List&& another) {
|
||
|
auto tmp(std::move(another));
|
||
|
std::swap(end_, tmp.end_);
|
||
|
std::swap(size_, tmp.size_);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
~List() {
|
||
|
if (end_) {
|
||
|
while (end_ != end_->r) {
|
||
|
erase(iterator(end_->r));
|
||
|
}
|
||
|
allocator_node_.deallocate(end_, 1); // TODO
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Allocator get_allocator() {
|
||
|
return allocator_node_;
|
||
|
}
|
||
|
|
||
|
void push_back(const T& value) {
|
||
|
insert(end(), value);
|
||
|
}
|
||
|
|
||
|
void push_back(T&& value) {
|
||
|
insert(end(), std::move(value));
|
||
|
}
|
||
|
|
||
|
void push_front(const T& value) {
|
||
|
insert(begin(), value);
|
||
|
}
|
||
|
|
||
|
void pop_back() {
|
||
|
erase(std::prev(end()));
|
||
|
}
|
||
|
|
||
|
void pop_front() {
|
||
|
erase(begin());
|
||
|
}
|
||
|
|
||
|
template<class... Args>
|
||
|
iterator emplace_back(Args&&... args) {
|
||
|
Node* tmp = allocator_node_.allocate(1);
|
||
|
tmp->l = nullptr;
|
||
|
tmp->r = nullptr;
|
||
|
|
||
|
Allocator tmp1 = allocator_node_;
|
||
|
std::allocator_traits<Allocator>::construct(tmp1, &tmp->value, std::forward<Args>(args)...);
|
||
|
|
||
|
Link(std::prev(end()).it, tmp);
|
||
|
++size_;
|
||
|
return iterator(tmp);
|
||
|
}
|
||
|
|
||
|
size_t size() const { return size_; }
|
||
|
|
||
|
iterator begin() { return iterator(end_->r); }
|
||
|
iterator end() { return iterator(end_); }
|
||
|
|
||
|
const_iterator begin() const { return const_iterator(end_->r); }
|
||
|
const_iterator end() const { return const_iterator(end_); }
|
||
|
|
||
|
const_iterator cbegin() const { return const_iterator(end_->r); }
|
||
|
const_iterator cend() const { return const_iterator(end_); }
|
||
|
|
||
|
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||
|
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||
|
|
||
|
const_reverse_iterator rbegin() const { return const_reverse_iterator(cend()); }
|
||
|
const_reverse_iterator rend() const { return const_reverse_iterator(cbegin()); }
|
||
|
|
||
|
const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); }
|
||
|
const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); }
|
||
|
|
||
|
iterator insert(const_iterator it, const T& value) {
|
||
|
Node* tmp = allocator_node_.allocate(1);
|
||
|
tmp->l = nullptr;
|
||
|
tmp->r = nullptr;
|
||
|
std::allocator_traits<decltype(allocator_node_)>::construct(allocator_node_, tmp, value);
|
||
|
Link(const_cast<Node*>(std::prev(it).it), tmp);
|
||
|
++size_;
|
||
|
return iterator(tmp);
|
||
|
}
|
||
|
|
||
|
iterator insert(const_iterator it, T&& value) {
|
||
|
Node* tmp = allocator_node_.allocate(1);
|
||
|
tmp->l = nullptr;
|
||
|
tmp->r = nullptr;
|
||
|
|
||
|
alloc_traits_::construct(allocator_node_, tmp, std::move(value));
|
||
|
|
||
|
Link(const_cast<Node*>(std::prev(it).it), tmp);
|
||
|
++size_;
|
||
|
return iterator(tmp);
|
||
|
}
|
||
|
|
||
|
void erase(const_iterator it) {
|
||
|
Node* tmp = const_cast<Node*>(it.it);
|
||
|
tmp->l->r = tmp->r;
|
||
|
tmp->r->l = tmp->l;
|
||
|
std::allocator_traits<decltype(allocator_node_)>::destroy(allocator_node_, tmp);
|
||
|
allocator_node_.deallocate(tmp, 1);
|
||
|
--size_;
|
||
|
}
|
||
|
|
||
|
void relink(iterator a, iterator b) {
|
||
|
Link(a.it, b.it);
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
|
||
|
void AllocateEnd() {
|
||
|
end_ = allocator_node_.allocate(1);
|
||
|
end_->l = end_;
|
||
|
end_->r = end_;
|
||
|
}
|
||
|
|
||
|
void Link(Node* l, Node* x) {
|
||
|
Node* r = l->r;
|
||
|
if (r == x)
|
||
|
return;
|
||
|
auto xl = x->l, xr = x->r;
|
||
|
if (xl)
|
||
|
xl->r = xr;
|
||
|
if (xr)
|
||
|
xr->l = xl;
|
||
|
x->l = l;
|
||
|
x->r = r;
|
||
|
l->r = x;
|
||
|
r->l = x;
|
||
|
}
|
||
|
|
||
|
template<bool is_const>
|
||
|
class base_iterator {
|
||
|
friend List;
|
||
|
std::conditional_t<is_const, const Node*, Node*> it;
|
||
|
using U = std::conditional_t<is_const, const T, T>;
|
||
|
public:
|
||
|
|
||
|
using iterator_category = std::bidirectional_iterator_tag;
|
||
|
using value_type = U;
|
||
|
using pointer = U*;
|
||
|
using reference = U&;
|
||
|
using difference_type = int;
|
||
|
|
||
|
base_iterator() : it(nullptr) {}
|
||
|
base_iterator(Node* v) { it = v; }
|
||
|
|
||
|
base_iterator(const base_iterator&) = default;
|
||
|
operator base_iterator<true>() { return base_iterator<true>(it); }
|
||
|
|
||
|
U& operator*() const { return it->value; }
|
||
|
U* operator->() const { return &it->value; }
|
||
|
base_iterator& operator++()& {
|
||
|
it = it->r;
|
||
|
return *this;
|
||
|
}
|
||
|
base_iterator operator++(int)& {
|
||
|
base_iterator tmp = *this;
|
||
|
it = it->r;
|
||
|
return tmp;
|
||
|
}
|
||
|
base_iterator& operator--()& {
|
||
|
it = it->l;
|
||
|
return *this;
|
||
|
}
|
||
|
base_iterator operator--(int)& {
|
||
|
base_iterator tmp = *this;
|
||
|
it = it->l;
|
||
|
return tmp;
|
||
|
}
|
||
|
|
||
|
friend bool operator==(base_iterator first, base_iterator second) { return first.it == second.it; }
|
||
|
|
||
|
friend bool operator!=(base_iterator first, base_iterator second) { return first.it != second.it; }
|
||
|
|
||
|
base_iterator& operator=(const base_iterator<false>& another) {
|
||
|
it = another.it;
|
||
|
return *this;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct Node {
|
||
|
Node* l = nullptr;
|
||
|
Node* r = nullptr;
|
||
|
T value;
|
||
|
Node() = default;
|
||
|
Node(const T& value) : value(value) {}
|
||
|
Node(T&& value) : value(std::move(value)) {}
|
||
|
Node& operator=(const Node& another) { value = another.value; }
|
||
|
};
|
||
|
typename std::allocator_traits<Allocator>::template rebind_alloc<Node> allocator_node_;
|
||
|
using alloc_traits_ = std::allocator_traits<decltype(allocator_node_)>;
|
||
|
Node* end_ = nullptr;
|
||
|
size_t size_ = 0;
|
||
|
};
|
||
|
|
||
|
template<typename Key, typename Value, typename Hash = std::hash<Key>, typename Equal = std::equal_to<Key>, typename Alloc = std::allocator<std::pair<const Key, Value>>>
|
||
|
class UnorderedMap {
|
||
|
private:
|
||
|
using NonConstNodeType = std::pair<Key, Value>;
|
||
|
using ListNonConstNodeType = List<NonConstNodeType, typename std::allocator_traits<Alloc>::template rebind_alloc<std::pair<Key, Value>>>;
|
||
|
|
||
|
struct Node {
|
||
|
std::pair<Key, Value> pair;
|
||
|
size_t hash;
|
||
|
Node() {}
|
||
|
Node(const Node& another) : pair(another.pair), hash(another.hash) {}
|
||
|
Node(Node&& another) : pair(std::move(another.pair)), hash(another.hash) { another.hash = 0; }
|
||
|
Node(Key&& key, Value&& value, const Hash& hash_) : pair(std::make_pair(std::move(key), std::move(value))) {
|
||
|
hash = hash_(key);
|
||
|
}
|
||
|
Node(const Key& key, const Value& value, const Hash& hash_) : pair(std::make_pair(key, value)){
|
||
|
hash = hash_(key);
|
||
|
}
|
||
|
Node(std::pair<Key, Value>&& pair_, const Hash& hash_) : pair(std::move(pair_)) {
|
||
|
hash = hash_(pair.first);
|
||
|
}
|
||
|
Node(const std::pair<Key, Value>& pair_, const Hash& hash_) : pair(pair_) {
|
||
|
hash = hash_(pair.first);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
using NewList = List<Node, typename std::allocator_traits<Alloc>::template rebind_alloc<Node>>;
|
||
|
|
||
|
NewList new_all_pairs;
|
||
|
std::vector<typename NewList::iterator> new_hash_table;
|
||
|
|
||
|
size_t GetBucketIdx(const Key& key) {
|
||
|
return hash_(key) % new_hash_table.size();
|
||
|
}
|
||
|
size_t GetBucketIdx(const Node& node) {
|
||
|
return node.hash % new_hash_table.size();
|
||
|
}
|
||
|
public:
|
||
|
using new_iterator = typename NewList::iterator;
|
||
|
|
||
|
template<bool is_const>
|
||
|
struct base_iterator {
|
||
|
using value_type = std::conditional_t<is_const, const std::pair<const Key, Value>, std::pair<const Key, Value>>;
|
||
|
using iterator_category = std::bidirectional_iterator_tag;
|
||
|
using pointer = value_type*;
|
||
|
using reference = value_type&;
|
||
|
using difference_type = int;
|
||
|
|
||
|
new_iterator it;
|
||
|
base_iterator() {}
|
||
|
base_iterator(const base_iterator&) = default;
|
||
|
base_iterator(const new_iterator& another) : it(another) {}
|
||
|
operator base_iterator<true>() { return base_iterator<true>(it); }
|
||
|
base_iterator& operator=(const base_iterator&) = default;
|
||
|
reference operator*() { return *reinterpret_cast<std::pair<const Key, Value>*>(&it->pair); }
|
||
|
pointer operator->() { return reinterpret_cast<std::pair<const Key, Value>*>(&it->pair); }
|
||
|
base_iterator& operator++()& { ++it; return *this; }
|
||
|
base_iterator operator++(int)& { base_iterator tmp(*this); ++it; return tmp; }
|
||
|
base_iterator& operator--()& { --it; return *this; }
|
||
|
base_iterator operator--(int)& { base_iterator tmp(*this); --it; return tmp; }
|
||
|
bool operator==(const base_iterator& another) { return it == another.it; }
|
||
|
bool operator!=(const base_iterator& another) { return it != another.it; }
|
||
|
};
|
||
|
using iterator = base_iterator<false>;
|
||
|
using const_iterator = base_iterator<true>;
|
||
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||
|
using NodeType = std::pair<const Key, Value>;
|
||
|
|
||
|
UnorderedMap() {
|
||
|
new_hash_table.resize(1000, new_all_pairs.end());
|
||
|
}
|
||
|
|
||
|
UnorderedMap(const UnorderedMap& another) : new_all_pairs(another.new_all_pairs), hash_(another.hash_), equal_(another.equal_) {
|
||
|
new_hash_table.resize(another.new_hash_table.size(), new_all_pairs.end());
|
||
|
new_hash_table[GetBucketIdx(*new_all_pairs.begin())] = new_all_pairs.begin();
|
||
|
|
||
|
for (auto it = std::next(new_all_pairs.begin()); it != new_all_pairs.end(); ++it) {
|
||
|
if (GetBucketIdx(*it) != GetBucketIdx(*prev(it)))
|
||
|
new_hash_table[GetBucketIdx(*it)] = it;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UnorderedMap(UnorderedMap&&) = default;
|
||
|
|
||
|
UnorderedMap& operator=(const UnorderedMap& another) {
|
||
|
this->all_pairs = another.all_pairs;
|
||
|
this->hash_table_ = another.hash_table_;
|
||
|
this->hash_ = another.hash_;
|
||
|
this->equal_ = another.equal_;
|
||
|
this->max_load_factor_ = another.max_load_factor_;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
UnorderedMap& operator=(UnorderedMap&&) = default;
|
||
|
|
||
|
size_t size() const {
|
||
|
return new_all_pairs.size();
|
||
|
}
|
||
|
|
||
|
Value& at(const Key& key) {
|
||
|
auto it = find(key);
|
||
|
|
||
|
if (it == end()) {
|
||
|
throw std::out_of_range("uhaha");
|
||
|
} else {
|
||
|
return it->second;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Value& operator[](const Key& key) {
|
||
|
return insert(std::make_pair(key, Value())).first->second;
|
||
|
}
|
||
|
|
||
|
template<class... Args>
|
||
|
std::pair<iterator, bool> emplace(Args&&... args) {
|
||
|
if (new_hash_table.size() < new_all_pairs.size())
|
||
|
rehash(new_hash_table.size() * 1.8);
|
||
|
|
||
|
new_all_pairs.emplace_back(std::forward<Args>(args)..., hash_);
|
||
|
auto& node = *std::prev(new_all_pairs.end());
|
||
|
auto& key = node.pair.first;
|
||
|
size_t hash = GetBucketIdx(node);
|
||
|
|
||
|
if (new_hash_table[hash] != new_all_pairs.end()) {
|
||
|
auto i = new_hash_table[hash];
|
||
|
while (i != prev(new_all_pairs.end()) && GetBucketIdx(*i) == hash) {
|
||
|
if (equal_(i->pair.first, key)) break;
|
||
|
++i;
|
||
|
}
|
||
|
if (i != std::prev(new_all_pairs.end()) && equal_(i->pair.first, key)) {
|
||
|
new_all_pairs.pop_back();
|
||
|
return {i, false};
|
||
|
} else {
|
||
|
auto it = std::prev(new_all_pairs.end());
|
||
|
new_all_pairs.relink(std::prev(i), it);
|
||
|
return {it, true};
|
||
|
}
|
||
|
} else {
|
||
|
new_hash_table[hash] = prev(new_all_pairs.end());
|
||
|
return {new_hash_table[hash], true};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::pair<iterator, bool> insert(const std::pair<Key, Value>& tmp) {
|
||
|
return emplace(tmp);
|
||
|
}
|
||
|
|
||
|
std::pair<iterator, bool> insert(std::pair<Key, Value>&& tmp) {
|
||
|
return emplace(std::move(tmp));
|
||
|
}
|
||
|
|
||
|
template<class InputIterator>
|
||
|
void insert(InputIterator begin, InputIterator end) {
|
||
|
for (; begin != end; ++begin) {
|
||
|
insert(*begin);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
iterator erase(const Key& key) {
|
||
|
return erase(find(key));
|
||
|
}
|
||
|
|
||
|
iterator erase(iterator it1) {
|
||
|
auto it = it1.it;
|
||
|
size_t hash = GetBucketIdx(*it);
|
||
|
|
||
|
if (new_hash_table[hash] != it) {
|
||
|
auto i = std::next(it);
|
||
|
new_all_pairs.erase(it);
|
||
|
return i;
|
||
|
} else {
|
||
|
if (GetBucketIdx(*std::next(it)) == hash) {
|
||
|
auto i = std::next(it);
|
||
|
new_all_pairs.erase(it);
|
||
|
new_hash_table[hash] = i;
|
||
|
return i;
|
||
|
} else {
|
||
|
auto i = std::next(it);
|
||
|
new_all_pairs.erase(it);
|
||
|
new_hash_table[hash] = new_all_pairs.end();
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
iterator erase(iterator begin, iterator end) {
|
||
|
for (; begin != end;) {
|
||
|
begin = erase(begin);
|
||
|
}
|
||
|
return end;
|
||
|
}
|
||
|
|
||
|
const_iterator erase(const_iterator begin, const_iterator end) {
|
||
|
for (; begin != end; ++begin) {
|
||
|
erase(begin);
|
||
|
}
|
||
|
return end;
|
||
|
}
|
||
|
|
||
|
iterator find(const Key& key) {
|
||
|
size_t hash = GetBucketIdx(key);
|
||
|
if (new_hash_table[hash] != new_all_pairs.end()) {
|
||
|
auto i = new_hash_table[hash];
|
||
|
while (i != new_all_pairs.end() && GetBucketIdx(*i) == hash) {
|
||
|
if (equal_(i->pair.first, key)) break;
|
||
|
++i;
|
||
|
}
|
||
|
if (i != new_all_pairs.end() && equal_(i->pair.first, key)) {
|
||
|
return i;
|
||
|
} else {
|
||
|
return end();
|
||
|
}
|
||
|
} else {
|
||
|
return end();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const_iterator find(const Key& key) const {
|
||
|
return const_cast<UnorderedMap&>(*this).find(key);
|
||
|
}
|
||
|
|
||
|
void rehash(size_t count) {
|
||
|
if (count <= new_hash_table.size())
|
||
|
return;
|
||
|
std::vector<new_iterator> v(count, new_all_pairs.end());
|
||
|
|
||
|
for (auto it = begin_(); it != end_(); ++it) {
|
||
|
auto& i = *it;
|
||
|
size_t hash = hash_(i.pair.first);
|
||
|
if (v[hash % v.size()] != new_all_pairs.end()) {
|
||
|
new_all_pairs.relink(v[hash % v.size()], it);
|
||
|
} else {
|
||
|
v[hash % v.size()] = it;
|
||
|
}
|
||
|
}
|
||
|
std::swap(new_hash_table, v);
|
||
|
}
|
||
|
|
||
|
void reserve(size_t count) {
|
||
|
rehash(count);
|
||
|
}
|
||
|
|
||
|
float load_factor() const {
|
||
|
return static_cast<float>(size()) / new_hash_table.size();
|
||
|
}
|
||
|
|
||
|
float max_load_factor() const {
|
||
|
return max_load_factor_;
|
||
|
}
|
||
|
|
||
|
void max_load_factor(float ml) {
|
||
|
max_load_factor_ = ml;
|
||
|
}
|
||
|
|
||
|
iterator begin() { return new_all_pairs.begin(); }
|
||
|
iterator end() { return new_all_pairs.end(); }
|
||
|
|
||
|
new_iterator begin_() { return new_all_pairs.begin(); }
|
||
|
new_iterator end_() { return new_all_pairs.end(); }
|
||
|
|
||
|
const_iterator begin() const { return const_iterator(const_cast<NewList&>(new_all_pairs).begin()); }
|
||
|
const_iterator end() const { return const_iterator(const_cast<NewList&>(new_all_pairs).end()); }
|
||
|
|
||
|
const_iterator cbegin() const { return const_iterator(const_cast<NewList&>(new_all_pairs).begin()); }
|
||
|
const_iterator cend() const { return const_iterator(const_cast<NewList&>(new_all_pairs).end()); }
|
||
|
|
||
|
reverse_iterator rbegin() { return reverse_iterator(end()); }
|
||
|
reverse_iterator rend() { return reverse_iterator(begin()); }
|
||
|
|
||
|
const_reverse_iterator rbegin() const { return const_reverse_iterator(cend()); }
|
||
|
const_reverse_iterator rend() const { return const_reverse_iterator(cbegin()); }
|
||
|
|
||
|
const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); }
|
||
|
const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); }
|
||
|
|
||
|
private:
|
||
|
Hash hash_;
|
||
|
Equal equal_;
|
||
|
|
||
|
float max_load_factor_;
|
||
|
};
|