文章目录
改造哈希表
我们先来看一段代码。
我们知道unordered_set和unordered_map的底层是用哈希表实现的
,而unordered_set的数据key值,unordered_map的数据是key和value的键值对。从上图的代码中可以看出,哈希表的节点可以支持存unordered_map的数据,但是unordered_set的数据呢?难道我们要再实现一个哈希表,它的节点里面存的是key吗?其实也不是不可以,但是这样大部分代码就冗余了不好。那么我们该怎样改造哈希表呢?
模板参数的改造
我们可以进行以上代码的改造,我们的底层哈希表确实不知道,要存的是key的数据,还是key和value的键值对数据,所以我们直接让哈希表节点的模板参数<class K,class V>变成<class T>,哈希表的模板参数也随之改变
这样底层的哈希表什么类型都可以接受,而这时就需要我们上层的unordered_set和unordered_map显示的传数据类型过去。
大家可能也看到了哈希表模板参数的后两个参数KeyOfT和HashFunc这里我们先不介绍,下面用到了我们在介绍。
插入的改造
我们先来看看模板参数改造后,原来的插入代码会暴露出哪些问题?
首先第一个问题就是参数问题
模板参数改造后存的数据类型已经不是键值对了,而是T类型。
接下来就迎来了第二个问题,插入位置计算问题。
我们知道插入数据时,要计算数据在哈希表中对应的坐标。那么改造后的T类型数据我们能不能直接进行计算呢?传过来的要是key类型我们可以进行计算,但是传过来的要是键值对呢?那么我们这时就需要增加一层仿函数
。我们的底层哈希表确实不知道传的是什么类型的数据,能不能用除留余数法进行计算。但是我们的封装层unordered_set和unordered_map知道,我们显示的传仿函数过去就可以,不同类型的数据,就调用不同类型的仿函数。
这里就是哈希表第三模板参数KeyOfT的作用。
蓝色框里面的hf是另一个问题,我们接下来再解答。
到这里我们又遇到一个问题,这里我们数据类型如果是整形我们可以用除留余数法计算坐标,但是如果是浮点型和字符串类型
呢?我们该如何计算?
这里我们就要用到仿函数,用仿函数把数据间接或者直接转化成整形
。
删除和查找的改造
删除和查找并没有什么其它特别的改造,主要面临的问题和插入几乎一致所以就不过多讲述。主要放代码让大家对比他们之间的区别。
到这哈希表本身的改造就完成了,我们还有一个重要的任务就是增加哈希表的迭代器。
增加迭代器
迭代器的结构
这里我们可以知道,哈希表的迭代器里面有两个成员变量,一个是节点指针,一个是哈希表指针
。那么这里就有一个问题了,为什么要多存一个哈希表指针呢?有什么用呢?其实这个哈希表的指针用处巨大,因为我们要用迭代器最重要的是要对迭代器进行++或者--
,没有哈希表我们要对哈希表的节点进行++或者–该如何进行呢?我们如何迭代的往下走?几乎没有办法,我们没有哈希表指针寸步难行。所以这里我们多加了一个哈希表指针。
构造函数
operator*和operator->的实现
operator!=和operator==的实现
前置operator++的实现
哈希表节点++的思想:
首先先看一下被++节点的后面还有没有数据,如果有那么下一个访问的节点就是它。
如果没有就继续往后找不为空的桶,如果找到了那么下一个访问的就是这个不为空桶的第一个节点。如果没有找到,那么就说明所有节点都访问完毕了。
举个例子:
我们假设it是迭代器指针的它指向11,it++后该指向的节点是谁呢?是21。
如果it指向31呢?
it++后该指向的节点是谁呢?是3。
就是应用这样的思路进行++。
代码实现如下:
后置operator++的实现
后置++和前置++思路一样。这里就不过多介绍。只不过是返回值的问题。大家有兴趣可以自己去了解一下
–的思路和++差不多,只不过–是从尾部开始往前走,++是从头部开始往后走。大家可以自己去尝试实现一下。
完整版代码
HashTable.h
#pragma once
#include <vector>
template<class K>
struct Hash
{
size_t operator()(const K& key)
{
return key;
}
};
template<>
struct Hash<string>
{
size_t operator()(const string& s)
{
size_t value = 0;
for (auto& ch : s)
{
value += ch;
}
return value;
}
};
namespace LinkHash
{
template<class T>
struct HashNode
{
T _data;
HashNode<T>* next;
HashNode(const T& data)
:_data(data)
,next(nullptr)
{}
};
template<class K, class T, class KeyOfT, class HashFunc>
class HashTable;
template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
struct HTIterator
{
typedef HashNode<T> Node;
typedef HTIterator<K, T, Ref, Ptr, KeyOfT, HashFunc> Self;
Node* _node;
HashTable<K, T, KeyOfT, HashFunc>* _pht;
HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
:_node(node)
,_pht(pht)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator==(const Self& it)
{
return _node == it._node;
}
bool operator!=(const Self& it)
{
return _node != it._node;
}
Self& operator++()
{
Node* cur = _node->next;
if (cur)
{
_node = cur;
}
else
{
KeyOfT kot;
HashFunc hf;
size_t index = hf(kot(_node->_data)) % _pht->_tables.size();
index++;
while (index < _pht->_tables.size() && !_pht->_tables[index])
{
index++;
}
if (index < _pht->_tables.size())
_node = _pht->_tables[index];
else
_node = nullptr;
}
return *this;
}
Self operator++(int)
{
Node* record = _node;
Node* cur = _node->next;
if (cur)
{
_node = cur;
}
else
{
KeyOfT kot;
HashFunc hf;
size_t index = hf(kot(_node->_data)) % _pht->_tables.size();
index++;
while (index < _pht->_tables.size() && !_pht->_tables[index])
{
index++;
}
if (index < _pht->_tables.size())
_node = _pht->_tables[index];
else
_node = nullptr;
}
return HTIterator(record,_pht);
}
};
template<class K, class T, class KeyOfT, class HashFunc>
class HashTable
{
typedef HashNode<T> Node;
template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
friend struct HTIterator;
typedef HashTable<K, T, KeyOfT, HashFunc> Self;
public:
typedef HTIterator<K, T, T&, T*, KeyOfT, HashFunc> iterator;
HashTable() = default;
HashTable(const Self& ht)
{
_tables.resize(ht._tables.size());
for (size_t i = 0; i < ht._tables.size(); i++)
{
Node* cur = ht._tables[i];
while (cur)
{
Node* newnode = new Node(cur->_data);
Node* next = _tables[i];
_tables[i] = newnode;
newnode->next = next;
cur = cur->next;
}
}
}
Self& operator=(Self ht)
{
swap(_size, ht._size);
_tables.swap(ht._tables);
return *this;
}
~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;
}
}
iterator begin()
{
size_t index = 0;
while (index < _tables.size() && !_tables[index])
{
index++;
}
return iterator(_tables[index], this);
}
iterator end()
{
return iterator(nullptr, this);
}
bool Erase(const K& key)
{
if (_size == 0)
{
return false;
}
HashFunc hf;
KeyOfT kot;
size_t index = hf(key) % _tables.size();
Node* cur = _tables[index];
Node* prev = nullptr;
while (cur)
{
if (kot(cur->_data) == key)
{
Node* next = cur->next;
if(prev)
prev->next = next;
else
_tables[index] = next;
delete cur;
cur = nullptr;
_size--;
return true;
}
else
{
prev = cur;
cur = cur->next;
}
}
return false;
}
Node* Find(const K& key)
{
if (_size == 0)
{
return nullptr;
}
HashFunc hf;
KeyOfT kot;
size_t index = hf(key) % _tables.size();
Node* cur = _tables[index];
while (cur)
{
if (kot(cur->_data) == key)
{
return cur;
}
else
{
cur = cur->next;
}
}
return nullptr;
}
bool Insert(const T& data)
{
KeyOfT kot;
Node* ret = Find(kot(data));
if (ret)
{
return false;
}
if (_tables.size() == 0 || _size*10 / _tables.size() == 10)
{
size_t newsize = _tables.size() == 0 ? 10 : 2 * _tables.size();
HashTable<K,T,KeyOfT,HashFunc> newHT;
newHT._tables.resize(newsize);
HashFunc hf;
for (int i = 0; i < _tables.size(); i++)
{
Node* cur = _tables[i];
while (cur)
{
Node* next = cur->next;
size_t index = hf(kot(cur->_data)) % newHT._tables.size();
if (newHT._tables[index])
{
Node* next = newHT._tables[index];
newHT._tables[index] = cur;
cur->next = next;
}
else
{
newHT._tables[index] = cur;
newHT._tables[index]->next = nullptr;;
}
newHT._size++;
cur = next;
}
_tables[i] = nullptr;
}
_tables.swap(newHT._tables);
}
HashFunc hf;
Node* cur = new Node(data);
size_t index = hf(kot(data)) % _tables.size();
if (_tables[index])
{
Node* next = _tables[index];
_tables[index] = cur;
cur->next = next;
}
else
{
_tables[index] = cur;
}
_size++;
return true;
}
private:
vector<Node*> _tables;
size_t _size = 0;
};
}
unordered_set.h
#pragma once
#include"HashTable.h"
namespace lzf
{
template<class K>
struct SetKeyOfT
{
const K& operator()(const K& key)
{
return key;
}
};
template<class K>
class unorderset
{
public:
typedef typename LinkHash::HashTable<K, K, SetKeyOfT<K>, Hash<K>>::iterator iterator;
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
bool insert(const K& key)
{
return _ht.Insert(key);
}
private:
LinkHash::HashTable<K, K, SetKeyOfT<K>,Hash<K>> _ht;
};
void test_unorderset()
{
unorderset<string> us;
us.insert("left");
us.insert("right");
us.insert("map");
us.insert("further");
us.insert("rate");
us.insert("grain");
unorderset<string>::iterator it = us.begin();
while (it != us.end())
{
cout << *it << " ";
++it;
}
cout << endl;
unorderset<string> uss = us;
unorderset<string>::iterator it1 = uss.begin();
while (it1 != uss.end())
{
cout << *it1 << " ";
++it1;
}
}
}
unordered_map.h
#pragma once
#include"HashTable.h"
#include<string>
namespace lzf
{
template<class K,class V>
struct MapKeyOfT
{
const K& operator()(const pair<K,V>& kv)
{
return kv.first;
}
};
template<class K,class V>
class unordermap
{
public:
typedef typename LinkHash::HashTable<K, pair<K,V>, MapKeyOfT<K,V>, Hash<K>>::iterator iterator;
iterator begin()
{
return _ht.begin();
}
iterator end()
{
return _ht.end();
}
bool insert(const pair<K,V>& kv)
{
return _ht.Insert(kv);
}
private:
LinkHash::HashTable<K, pair<K,V>, MapKeyOfT<K,V>, Hash<K>> _ht;
};
void testunordermap()
{
unordermap<string, string> um;
um.insert(make_pair("left", ""));
um.insert(make_pair("right", "ұ"));
um.insert(make_pair("map", "ͼ"));
um.insert(make_pair("set", ""));
unordermap<string, string>::iterator it = um.begin();
while (it != um.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
}
}