【C++】unordered_map & unordered_set 底层刨析

字节连结 2024-07-11 15:05:03 阅读 52

文章目录

1. 哈希表的改造2. unordered_map3. unordered_set

在这里插入图片描述

C++ STL 库中,unordered_map 和 unordered_set 容器的底层为哈希表,本文将简单模拟哈希表(哈希桶),unordered_map 和 unordered_set 只需封装哈希表的接口即可实现。

1. 哈希表的改造

模板参数列表的改造

K:关键码类型T:不同容器 T 的类型不同,如果是 unordered_map,T 代表一个键值对,如果是 unordered_set,T 为 KKeyOfT:从 T 中获取 KeyHash:哈希函数仿函数对象类型,哈希函数使用除留余数法,需要将 Key 转换为整型数字才能取模

<code>template<class K, class T, class KeyOfT, class Hash>

class HashTable;

增加迭代器操作

// 为了实现简单,在哈希桶的迭代器类中需要用到HashTable本身

template<class K, class T, class KeyOfT, class Hash>

class HashTable;

// 注意:因为哈希桶在底层是单链表结构,所以哈希桶的迭代器不需要--操作

template<class K, class T, class KeyOfT, class Hash>

struct __HTIterator

{

typedef HashNode<T> Node;

typedef HashTable<K, T, KeyOfT, Hash> HT;

typedef __HTIterator<K, T, KeyOfT, Hash> Self;

Node* _node;// 当前迭代器关联的节点

HT* _ht;// 哈希桶 - 主要是为了找下一个空桶时候方便

__HTIterator(Node* node, HT* ht)

: _node(node)

, _ht(ht)

{ }

T& operator*()

{

return _node->_data;

}

Self& operator++()

{

if (_node->_next)

{

// 当前桶还是节点

_node = _node->_next;

}

else

{

// 当前桶走完了,找下一个桶

KeyOfT kot;

Hash hs;

size_t hashi = hs(kot(_node->_data)) % _ht->_tables.size();

// 找下一个桶

hashi++;

while (hashi < _ht->_tables.size())

{

if (_ht->_tables[hashi])

{

_node = _ht->_tables[hashi];

break;

}

hashi++;

}

// 后面没有桶了

if (hashi == _ht->_tables.size())

{

_node = nullptr;

}

}

return *this;

}

bool operator!=(const Self& s)

{

return _node != s._node;

}

};

增加通过 T 获取 key 操作

template<class K, class T, class KeyOfT, class Hash>

class HashTable

{

template<class K, class T, class KeyOfT, class Hash>

friend struct __HTIterator;

typedef HashNode<T> Node;

public:

typedef __HTIterator<K, T, KeyOfT, Hash> iterator;

iterator begin()

{

for (size_t i = 0; i < _tables.size(); i++)

{

// 找到第一个桶的第一个节点

if (_tables[i])

{

return iterator(_tables[i], this);

}

}

return end();

}

iterator end()

{

return iterator(nullptr, this);

}

HashTable()

{

_tables.resize(10, nullptr);

_n = 0;

}

~HashTable()

{

for (size_t i = 0; i < _tables.size(); i++)

{

Node* cur = _tables[i];

while (cur)

{

Node* next = cur->_next;

delete cur;

cur = next;

}

_tables[i] = nullptr;

}

}

bool Insert(const T& data)

{

KeyOfT kot;

if (Find(kot(data)))

return false;

Hash hs;

// 负载因子到1就扩容

if (_n == _tables.size())

{

vector<Node*> newTables(_tables.size() * 2, nullptr);

for (size_t i = 0; i < _tables.size(); i++)

{

// 取出旧表中节点,重新计算挂到新表桶中

Node* cur = _tables[i];

while (cur)

{

Node* next = cur->_next;

// 头插到新表

size_t hashi = hs(kot(cur->_data)) % newTables.size();

cur->_next = newTables[hashi];

newTables[hashi] = cur;

cur = next;

}

_tables[i] = nullptr;

}

_tables.swap(newTables);

}

size_t hashi = hs(kot(data)) % _tables.size();

Node* newnode = new Node(data);

// 头插

newnode->_next = _tables[hashi];

_tables[hashi] = newnode;

++_n;

return true;

}

Node* Find(const K& key)

{

KeyOfT kot;

Hash hs;

size_t hashi = hs(key) % _tables.size();

Node* cur = _tables[hashi];

while (cur)

{

if (kot(cur->_data) == key)

{

return cur;

}

cur = cur->_next;

}

return nullptr;

}

bool Erase(const K& key)

{

KeyOfT kot;

Hash hs;

size_t hashi = hs(key) % _tables.size();

Node* prev = nullptr;

Node* cur = _tables[hashi];

while (cur)

{

if (kot(cur->_data) == key)

{

// 删除

if (prev)

{

prev->_next = cur->_next;

}

else

{

_tables[hashi] = cur->_next;

}

delete cur;

--_n;

return true;

}

prev = cur;

cur = cur->_next;

}

return false;

}

private:

vector<Node*> _tables;// 指针数组

size_t _n;

};

2. unordered_map

unordered_map 中存储的是 pair<K, V> 的键值对,K 为 key 的类型,V 为 value 的类型,Hash 为哈希函数类型unordered_map 在实现时,只需将 HashTable 中的接口重新封装即可

template<class K, class V, class Hash = HashFunc<K>>

class unordered_map

{

// 通过T获取key的操作

struct MapKeyOfT

{

const K& operator()(const pair<K, V>& kv)

{

return kv.first;

}

};

public:

typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;

iterator begin()

{

return _ht.begin();

}

iterator end()

{

return _ht.end();

}

bool insert(const pair<K, V>& kv)

{

return _ht.Insert(kv);

}

private:

hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;

};

3. unordered_set

unordered_set 的实现类似于 unordered_map

template<class K, class Hash = HashFunc<K>>

class unordered_set

{

struct SetKeyOfT

{

const K& operator()(const K& key)

{

return key;

}

};

public:

typedef typename hash_bucket::HashTable<K, const K, SetKeyOfT, Hash>::iterator iterator;

iterator begin()

{

return _ht.begin();

}

iterator end()

{

return _ht.end();

}

bool insert(const K& key)

{

return _ht.Insert(key);

}

private:

hash_bucket::HashTable<K, const K, SetKeyOfT, Hash> _ht;

};


END



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。