哈希结构以及用哈希结构实现unordered_map和unordered_set

哈希的概念

unordered系列的关联式容器之所以效率比较高,是因为底层使用了哈希结构。那么问题来了,什么是哈希呢?
顺序结构和平衡树中,元素关键码与其存储位置之间没有对应关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(logN),搜索的效率取决于搜索过程中元素的比较次数。
理想的搜索方法:不经过任何比较,一次直接从表中得到要搜索的元素。如果构造一种存储结构,通过某种函数(HashFunc)使元素的存储位置与它的关键码之间能建立起一一对应的映射关系,那么在查找时通过该函数可以很快找到该元素。该方法即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(HashTable)(或者称为散列表)
哈希函数设置为:hash(key) = key % size size为存储元素底层空间的总大小。
哈希结构以及用哈希结构实现unordered_map和unordered_set_第1张图片

哈希冲突

按照以上方式进行搜索不必进行多次关键码的比较,因此搜索的速度比较快。但是问题来了,如果向集合中插入元素14,会出现什么问题呢?很显然,14%10等于4,那么4和14会在同一个位置,即不同的关键字通过相同哈希函数计算出相同的哈希,这种现象称为哈希冲突或哈希碰撞。
引起哈希冲突的一个原因是:哈希函数的设计不够合理。哈希函数的设计原则:

  • 哈希函数的定义域必须包含需要存储的全部关键码,如果散列表有m个地址时,其值域必须在0~m-1之间。
  • 哈希函数计算出来的地址能均匀分布在整个空间中。
  • 哈希函数应该比较简单。

常见的哈希函数

1.直接定址法(常用)(小范围限定的局部问题)
取关键字的某个线性函数为散列地址:Hash(key)= A*key+B 优点:简单、均匀。缺点:需要事先知道关键字的分布情况。使用场景:适合查找比较小且连续的情况。

2.除留余数法(常用)(大范围的广泛问题)
设散列表中允许的地址数为m,取一个不大于m,但最接近或等于m的质数p作为除数,按照哈希函数:Hash(key) = key % p(p <=m),将关键码转换成哈希地址。

不常用的有平方取中法、折叠法、随机数法、数学分析法。(了解)
注:哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是无法避免哈希冲突。

解决哈希冲突
解决哈希冲突的两种方法:闭散列开散列
闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那就可以把key存放到冲突位置的下一个空位置中去。找下一个空位置的方法:

1.线性探测:从发生冲突的位置开始,依次向后探测,知道找到下一个空位置为止。

  • 插入操作
    通过哈希函数获取待插入元素在哈希表中的位置。如果该位置中没有元素,则直接插入新函数,如果该位置已经有元素则发生了哈希冲突,使用线性探测找到下一个空位置,插入新函数。

  • 删除
    采用闭散列处理哈希冲突,不能随便物理处理删除哈希表中已有的元素,若直接删除元素会影响其他元素的搜索。因此线性探测采用标记的伪删除法来删除元素。

用闭散列实现一个哈希结构

#include
namespace CLOSE
{
     
	enum State
	{
     
		EMPTY,
		EXIST,
		DELETE,
	};
	template<class K,class V>
	struct HashNode
	{
     
		std::pair<K, V> _kv;
		State _state;
	};
	template<class K,class V>
	struct HashTable
	{
     
		typedef HashNode<K, V> HashNode;
	public:
		HashTable(size_t N = 10)
		{
     
			_table.resize(N);
			for (size_t i = 0; i < _table.size(); ++i)
			{
     
				_table[i]._state = EMPTY;
			}
			_size = 0;
		}
		bool Insert(const std::pair<K, V>& kv)
		{
     
			//先检查哈希表底层空间是否充足
			CheckCapacity();
			size_t index = kv.first % _table.size();
			while (_table[index]._state == EXIST)
			{
     
				if (_table[index]._kv.first == kv.first)
				{
     
					return false;
				}
				//线性探测,解决哈希冲突
				++index;
				if (index == _table.size())
					index = 0;
			}
			//插入元素
			_table[index]._kv = kv;
			_table[index]._state = EXIST;
			++_size;
			return true;
		}
		void CheckCapacity()
		{
     
			//什么时机增容,如何增容
			//散列表的荷载因子定义为:a = 填入表中的元素个数/散列表的长度
			//a是散列表装满程度的标志因子。由于表长是定值,a与填入表中的元素个数成正比,所以,a越大,表明填入表中的元素越多,产生冲突的可能性就越大;
			//反之,a越小。标明填入表中的元素越少,产生冲突的可能性就越小。实际上,散列表的平均查找长度是荷载因子a的函数,只是不同处理冲突的方法有不同的函数。
			//对于开放定址法,荷载因子是特别重要的因素,应该严格限制在0.7~0.8,超过0.8,查表时的CPU缓存不命中,按照指数曲线上升。
			if (_table.size() == 0 || _size * 10 / _table.size() >= 7)
			{
     
				size_t newsize = _table.size() == 0 ? 10 : _table.size() * 2;
				HashTable<K, V> _newht(newsize);
				//将旧表中的数据重新在新表中算其所在的位置
				for (size_t i = 0; i < _table.size(); ++i)
				{
     
					if (_table[i]._state == EXIST)
					{
     
						_newht.Insert(_table[i]._kv);
					}
				}
				_table.swap(_newht._table);
			}
		}
		HashNode* Find(const K& key)
		{
     
			size_t index = key % _table.size();
			while (_table[index]._state != EMPTY)
			{
     
				if (_table[index]._kv.first == key && _table[index]._state == EXIST)
				{
     
					return &_table[index];
				}
				++index;
				if (index == _table.size())
				{
     
					index = 0;
				}
			}
			return nullptr;
		}
		bool Erase(const K& key)
		{
     
			HashNode* node = Find(key);
			if (node == nullptr)
			{
     
				return false;
			}
			else
			{
     
				node->_state = DELETE;
				--_size;
				return true;
			}
		}
	private:
		std::vector<HashNode> _table;
		size_t _size;//有效数据的个数
	};
	void TestHashTable()
	{
     
		HashTable<int, int> ht;
		ht.Insert(std::make_pair(1, 1));
		ht.Insert(std::make_pair(3, 3));
		ht.Insert(std::make_pair(5, 5));
		ht.Insert(std::make_pair(2, 2));
		ht.Insert(std::make_pair(3, 3));
		ht.Find(5);
		ht.Erase(3);
	}
}

线性探测优点:实现非常简单。缺点:一旦发生哈希冲突,所有的冲突可能连在一起,容易产生数据“堆积”,即:不同关键码占据了可利用的空位置,使得寻找某关键码的位置需要多次比较,导致效率降低。我们还可以通过二次探测来缓解哈希冲突

2.二次探测
线性探测的缺陷是产生冲突的数据堆积到一块,这与其找下一个空位置有关系,因为找空位置的方式就是挨着往后逐个去找。二次探测的方法是:Hi = (H0 + i^2) % m,或Hi = (H0 - i^2) % m。其中i=1,2,3…,H0是通过散列函数Hash(x)对元素的关键码key进行计算得到的位置,m表示表的大小。

研究表明:当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在搜索时可以不考虑表装 满的情况,但在插入时必须确保表的装载因子a不超过0.5,如果超出必须考虑增容。,因此闭散列最大的缺陷就是空间利用率比较低

开散列
开散列法又叫链地址法(开链法或哈希桶),首先对关键码用散列函数计算散列地址,具有相同地址的关键码归于同一个子集,每个子集合称为一个桶,各个桶中的元素通过单链表链接起来,各链表的头结点存储在哈希表中。

哈希结构以及用哈希结构实现unordered_map和unordered_set_第2张图片
上图中,开散列中每个桶中放的是发生哈希冲突的元素

unordered_map和unordered_set底层封装的是哈希结构。以下代码封装了哈希表,并巧妙的运用了模板模拟实现了unordered_map和unordered_set.

1.用开散列法实现一个哈希表

template<class V>
struct HashNode
{
     
	HashNode<V>* _next;
	V _value;

	HashNode(const V& v)
		: _next(nullptr)
		, _value(v)
	{
     }
};
template<class K,class V,class KeyOfValue,class HashFunc>
class HashTable
{
     
	typedef HashNode<V> Node;
	template<class K,class V,class KeyOfValue,class HashFunc>
	friend struct HTIterator;
public:
	typedef HTIterator<K, V, KeyOfValue, HashFunc> iterator;
	HashTable()
		: _size(0)
	{
     }
	~HashTable()
	{
     
		Clear();
	}
	KeyOfValue kov;
	iterator Begin()
	{
     
		for (size_t i = 0; i < _table.size(); i++)
		{
     
			if (_table[i] != nullptr)
			{
     
				return iterator(_table[i], this);//this代表哈希表的指针
			}
		}
		return iterator(nullptr,this);
	}
	iterator End()
	{
     
		return iterator(nullptr, this);
	}

	pair<iterator, bool> Insert(const V& v)
	{
     
		CheckCapacity();
		size_t index = HashIndex(kov(v), _table.size());
		Node* cur = _table[index];
		while (cur)
		{
     
			if (kov(cur->_value) == kov(v))
				return make_pair(iterator(cur, this), false);
			cur = cur->_next;
		}
		Node* newnode = new Node(v);
		newnode->_next = _table[index];
		_table[index] = newnode;
		++_size;
		return make_pair(iterator(newnode, this), true);
	}

	iterator Find(const K& key)
	{
     
		size_t index = HashIndex(key, _table.size());
		Node* cur = _table[index];
		while (cur)
		{
     
			if (kov(cur->_value) == key)
			{
     
				return iterator(cur,this);
			}
			cur = cur->_next;
		}
		return iterator(nullptr, this);
	}

	bool Erase(const K& key)
	{
     
		size_t index = HashIndex(key, _table.size());
		Node* prev = nullptr;
		Node* cur = _table[index];
		while (cur)
		{
     
			if (kov(cur->_value) == key)
			{
     
				if (prev == nullptr)
					_table[index] = cur->_next;
				else
					prev->_next = cur->_next;
				--_size;
				delete cur;
				return true;
			}
			prev = cur;
			cur = cur->_next;
		}
		return false;
	}

	size_t Size()const
	{
     
		return _size;
	}
	void Clear()
	{
     
		for (size_t i = 0; i < _table.size(); i++)
		{
     
			Node* cur = _table[i];
			while (cur)
			{
     
				_table[i] = cur->_next;
				delete cur;
				cur = _table[i];
			}
		}
		_size = 0;
	}
	bool Empty()const
	{
     
		return 0 == _size;
	}
	size_t Count(const K& key)
	{
     
		size_t index = HashIndex(key, _table.size());
		Node* cur = _table[i];
		while (cur)
		{
     
			if (kov(cur->_value) == key)
				break;
			cur = cur->_next;
		}
		size_t count = 0;
		while (cur)
		{
     
			if (kov(cur->_value) == key)
			{
     
				count++;
				cur = cur->_next;
			}
			else
				break;
		}
		return count;
	}
private:
//开散列增容的方法是,在元素个数刚好等于桶的个数时,可以给哈希表增容,每次以二倍的方式增容
	void CheckCapacity()
	{
     
		if (_size == _table.size())
		{
     
			size_t newsize = _table.size() == 0 ? 10 : _table.size() * 2;
			vector<Node*> _newtable;
			_newtable.resize(newsize,nullptr);
			for (size_t i = 0; i < _table.size(); i++)
			{
     
				Node* cur = _table[i];//将旧表结点插入到新表
				while (cur)
				{
     
					Node* next = cur->_next;
					size_t index = HashIndex(kov(cur->_value), _newtable.size());
					//头插到新表
					cur->_next = _newtable[index];
					_newtable[index] = cur;
					cur = next;
				}
				_table[i] = nullptr;
			}
			_table.swap(_newtable);
		}
	}
	size_t HashIndex(const K& key, size_t size)
	{
     
		HashFunc hf;
		return hf(key) % size;
	}

private:
	vector<Node*> _table;
	size_t _size = 0;
};

这里需要解释一下模板中各个参数的意义:

K:关键码类型。 V:不同容器的V的类型不同,如果是unoredered_map,V代表一个键值对,如果是unordered_set,V为K
KeyOfValue:因为V的类型不同,通过value取key的方式也就不同。
HashFunc:哈希函数仿函数对象类型,哈希函数使用除留余数法,需要将key转换成整形数字才能取模。

2.实现一个哈希表的迭代器类

//为了实现简单,在哈希表的迭代器类中需要用到HashTable本身,所以采用前置声明
template<class K,class V,class KeyOfValue,class HashFunc>
class HashTable;

template<class K, class V, class KeyOfValue,class HashFunc>
struct HTIterator
{
     
	typedef HashNode<V> Node;
	typedef HTIterator<K, V, KeyOfValue, HashFunc> Self;
	Node* _node;
	HashTable<K, V, KeyOfValue, HashFunc>* _ht;

	HTIterator(Node* node, HashTable<K, V, KeyOfValue, HashFunc>* ht)
		: _node(node)
		, _ht(ht)
	{
     }
	//因为哈希表在底层是单链表结构,所以哈希表的迭代器不需要实现--操作
	Self& operator++()
	{
     
		if (_node->_next)
		{
     
			_node = _node->_next;
		}
		else
		{
     
			KeyOfValue kov;
			size_t index = _ht->HashIndex(kov(_node->_value), _ht->_table.size());
			++index;
			while (index < _ht->_table.size())
			{
     
				if (_ht->_table[index])
				{
     
					_node = _ht->_table[index];
					break;
				}
				++index;
			}
			if (index == _ht->_table.size())
				_node = nullptr;
		}
		return *this;
	}
	Self operator++(int)
	{
     
		Self temp(*this);
		++(*this);
		return temp;
	}

	V& operator*()
	{
     
		return _node->_value;
	}

	V* operator->()
	{
     
		return &_node->_value;
	}

	bool operator==(const Self& s)
	{
     
		return _node == s._node && _ht == s._ht;
	}

	bool operator!=(const Self& s)
	{
     
		return _node != s._node;
	}
};

3.封装哈希表,简单实现unordered_map
unordered_map中存储的是pair的键值对,K为key的类型,V为value的类型,HashFunc为哈希函数类型

template<class K, class V,class HashFunc = HashFunc<K>>
class UnorderedMap
{
     
	typedef pair<K, V> ValueType;
	
	struct KeyOfValue
	{
     
		const K& operator()(const ValueType& kv)
		{
     
			return kv.first;
		}
	};
public:
	typedef typename HashTable<K, ValueType, KeyOfValue, HashFunc>::iterator iterator;
	UnorderedMap()
		: _ht()
	{
     }
	//iterator
	iterator begin()
	{
     
		return _ht.Begin();
	}
	iterator end()
	{
     
		return _ht.End();
	}

	//capacity
	size_t size()const
	{
     
		return _ht.Size();
	}
	bool empty()const
	{
     
		return _ht.Empty();
	}

	//Acess
	V& operator[](const K& key)
	{
     
		pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
		return ret.first->second;
	}

	iterator find(const K& key)
	{
     
		return _ht.Find(key);
	}
	size_t count(const K& key)
	{
     
		return _ht.Count();
	}

	//modify
	pair<iterator, bool> insert(const ValueType& kv)
	{
     
		return _ht.Insert(kv);
	}
private:
	HashTable<K,ValueType, KeyOfValue,HashFunc> _ht;
};

void TestUnorderedMap()
{
     
	UnorderedMap<int, int> m;
	m.insert(make_pair(1, 1));
	m.insert(make_pair(3, 3));
	m.insert(make_pair(4, 4));
	m.insert(make_pair(1, 1));
	m.insert(make_pair(5, 5));
	m.insert(make_pair(2, 2));
	m.insert(make_pair(15, 15));
	m.find(5);
	
	UnorderedMap<int, int>::iterator it = m.begin();
	
	while (it != m.end())
	{
     
		cout << it->first << " ";
		++it;
	}
	cout << endl;
	UnorderedMap<string, string> dict;
	dict.insert(make_pair("string", "字符串"));
	dict.insert(make_pair("left", "左边"));
	dict["left"] = "剩余";

	UnorderedMap<string, string>::iterator it2 = dict.begin();

	while (it2 != dict.end())
	{
     
		cout << it2->first << " ";
		++it2;
	}
	cout << endl;
}

4.封装哈希表,简单实现unordered_set

class UnorderSet
{
     
	typedef K ValueType;
	struct KeyOfValue
	{
     
		const K& operator()(const ValueType& v)
		{
     
			return v;
		}
	};
public:
	typedef typename HashTable<K, ValueType, KeyOfValue, HashFunc>::iterator iterator;
	pair<iterator, bool> insert(const ValueType& kv)
	{
     
		return _ht.Insert(kv);
	}
	iterator find(const K& key)
	{
     
		return _ht.Find(key);
	}
	size_t count(const K& key)
	{
     
		return _ht.Count();
	}
	iterator begin()
	{
     
		return _ht.Begin();
	}
	iterator end()
	{
     
		return _ht.End();
	}
private:
		HashTable<K, ValueType, KeyOfValue, HashFunc> _ht;
};
void TestUnorderedSet()
{
     
	UnorderSet<int> s;
	s.insert(2);
	s.insert(6);
	s.insert(4);
	s.insert(14);
	s.insert(16);
	s.find(2);
	UnorderSet<int>::iterator it = s.begin();
	while (it != s.end())
	{
     
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

至此,我们就成功用封装了哈希表,实现了一个简单的unordered_map和unordered_set容器。

完整的代码结构:

HashTable.h

#pragma once
#include
#include
#include
using namespace std;

template<class V>
struct HashNode
{
     
	HashNode<V>* _next;
	V _value;

	HashNode(const V& v)
		: _next(nullptr)
		, _value(v)
	{
     }
};
template<class K,class V,class KeyOfValue,class HashFunc>
class HashTable;

template<class K, class V, class KeyOfValue,class HashFunc>
struct HTIterator
{
     
	typedef HashNode<V> Node;
	typedef HTIterator<K, V, KeyOfValue, HashFunc> Self;
	Node* _node;
	HashTable<K, V, KeyOfValue, HashFunc>* _ht;

	HTIterator(Node* node, HashTable<K, V, KeyOfValue, HashFunc>* ht)
		: _node(node)
		, _ht(ht)
	{
     }
	//因为哈希表在底层是单链表结构,所以哈希表的迭代器不需要实现--操作
	Self& operator++()
	{
     
		if (_node->_next)
		{
     
			_node = _node->_next;
		}
		else
		{
     
			KeyOfValue kov;
			size_t index = _ht->HashIndex(kov(_node->_value), _ht->_table.size());
			++index;
			while (index < _ht->_table.size())
			{
     
				if (_ht->_table[index])
				{
     
					_node = _ht->_table[index];
					break;
				}
				++index;
			}
			if (index == _ht->_table.size())
				_node = nullptr;
		}
		return *this;
	}
	Self operator++(int)
	{
     
		Self temp(*this);
		++(*this);
		return temp;
	}

	V& operator*()
	{
     
		return _node->_value;
	}

	V* operator->()
	{
     
		return &_node->_value;
	}

	bool operator==(const Self& s)
	{
     
		return _node == s._node && _ht == s._ht;
	}

	bool operator!=(const Self& s)
	{
     
		return _node != s._node;
	}
};
template<class K,class V,class KeyOfValue,class HashFunc>
class HashTable
{
     
	typedef HashNode<V> Node;
	template<class K,class V,class KeyOfValue,class HashFunc>
	friend struct HTIterator;
public:
	typedef HTIterator<K, V, KeyOfValue, HashFunc> iterator;
	HashTable()
		: _size(0)
	{
     }
	~HashTable()
	{
     
		Clear();
	}
	KeyOfValue kov;
	iterator Begin()
	{
     
		for (size_t i = 0; i < _table.size(); i++)
		{
     
			if (_table[i] != nullptr)
			{
     
				return iterator(_table[i], this);//this代表哈希表的指针
			}
		}
		return iterator(nullptr,this);
	}
	iterator End()
	{
     
		return iterator(nullptr, this);
	}

	pair<iterator, bool> Insert(const V& v)
	{
     
		CheckCapacity();
		size_t index = HashIndex(kov(v), _table.size());
		Node* cur = _table[index];
		while (cur)
		{
     
			if (kov(cur->_value) == kov(v))
				return make_pair(iterator(cur, this), false);
			cur = cur->_next;
		}
		Node* newnode = new Node(v);
		newnode->_next = _table[index];
		_table[index] = newnode;
		++_size;
		return make_pair(iterator(newnode, this), true);
	}

	iterator Find(const K& key)
	{
     
		size_t index = HashIndex(key, _table.size());
		Node* cur = _table[index];
		while (cur)
		{
     
			if (kov(cur->_value) == key)
			{
     
				return iterator(cur,this);
			}
			cur = cur->_next;
		}
		return iterator(nullptr, this);
	}

	bool Erase(const K& key)
	{
     
		size_t index = HashIndex(key, _table.size());
		Node* prev = nullptr;
		Node* cur = _table[index];
		while (cur)
		{
     
			if (kov(cur->_value) == key)
			{
     
				if (prev == nullptr)
					_table[index] = cur->_next;
				else
					prev->_next = cur->_next;
				--_size;
				delete cur;
				return true;
			}
			prev = cur;
			cur = cur->_next;
		}
		return false;
	}

	size_t Size()const
	{
     
		return _size;
	}
	void Clear()
	{
     
		for (size_t i = 0; i < _table.size(); i++)
		{
     
			Node* cur = _table[i];
			while (cur)
			{
     
				_table[i] = cur->_next;
				delete cur;
				cur = _table[i];
			}
		}
		_size = 0;
	}
	bool Empty()const
	{
     
		return 0 == _size;
	}
	size_t Count(const K& key)
	{
     
		size_t index = HashIndex(key, _table.size());
		Node* cur = _table[i];
		while (cur)
		{
     
			if (kov(cur->_value) == key)
				break;
			cur = cur->_next;
		}
		size_t count = 0;
		while (cur)
		{
     
			if (kov(cur->_value) == key)
			{
     
				count++;
				cur = cur->_next;
			}
			else
				break;
		}
		return count;
	}
private:
	void CheckCapacity()
	{
     
		if (_size == _table.size())
		{
     
			size_t newsize = _table.size() == 0 ? 10 : _table.size() * 2;
			vector<Node*> _newtable;
			_newtable.resize(newsize,nullptr);
			for (size_t i = 0; i < _table.size(); i++)
			{
     
				Node* cur = _table[i];//将旧表结点插入到新表
				while (cur)
				{
     
					Node* next = cur->_next;
					size_t index = HashIndex(kov(cur->_value), _newtable.size());
					//头插到新表
					cur->_next = _newtable[index];
					_newtable[index] = cur;
					cur = next;
				}
				_table[i] = nullptr;
			}
			_table.swap(_newtable);
		}
	}
	size_t HashIndex(const K& key, size_t size)
	{
     
		HashFunc hf;
		return hf(key) % size;
	}

private:
	vector<Node*> _table;
	size_t _size = 0;
};

common.h

#pragma once

template<class K>
struct HashFunc
{
     
	const K& operator()(const K& key)
	{
     
		return key;
	}
};

template<>
struct HashFunc<string>
{
     
	size_t operator()(const std::string& s)
	{
     
		size_t hash = 0;
		for (size_t i = 0; i < s.size(); i++)
		{
     
			hash = hash * 131 + s[i];
		}
		return hash;
	}
};

unordered_map.h

#pragma once
#include"HashTable.h"
#include"common.h"
template<class K, class V,class HashFunc = HashFunc<K>>
class UnorderedMap
{
     
	typedef pair<K, V> ValueType;
	
	struct KeyOfValue
	{
     
		const K& operator()(const ValueType& kv)
		{
     
			return kv.first;
		}
	};
public:
	typedef typename HashTable<K, ValueType, KeyOfValue, HashFunc>::iterator iterator;
	UnorderedMap()
		: _ht()
	{
     }
	//iterator
	iterator begin()
	{
     
		return _ht.Begin();
	}
	iterator end()
	{
     
		return _ht.End();
	}

	//capacity
	size_t size()const
	{
     
		return _ht.Size();
	}
	bool empty()const
	{
     
		return _ht.Empty();
	}

	//Acess
	V& operator[](const K& key)
	{
     
		pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
		return ret.first->second;
	}

	iterator find(const K& key)
	{
     
		return _ht.Find(key);
	}
	size_t count(const K& key)
	{
     
		return _ht.Count();
	}

	//modify
	pair<iterator, bool> insert(const ValueType& kv)
	{
     
		return _ht.Insert(kv);
	}
private:
	HashTable<K,ValueType, KeyOfValue,HashFunc> _ht;
};

void TestUnorderedMap()
{
     
	UnorderedMap<int, int> m;
	m.insert(make_pair(1, 1));
	m.insert(make_pair(3, 3));
	m.insert(make_pair(4, 4));
	m.insert(make_pair(1, 1));
	m.insert(make_pair(5, 5));
	m.insert(make_pair(2, 2));
	m.insert(make_pair(15, 15));
	m.find(5);
	
	UnorderedMap<int, int>::iterator it = m.begin();
	
	while (it != m.end())
	{
     
		cout << it->first << " ";
		++it;
	}
	cout << endl;
	UnorderedMap<string, string> dict;
	dict.insert(make_pair("string", "字符串"));
	dict.insert(make_pair("left", "左边"));
	dict["left"] = "剩余";

	UnorderedMap<string, string>::iterator it2 = dict.begin();

	while (it2 != dict.end())
	{
     
		cout << it2->first << " ";
		++it2;
	}
	cout << endl;
}

unordered_set.h

#pragma once
#include"HashTable.h"
#include"common.h"
template<class K,class HashFunc = HashFunc<K>>
class UnorderSet
{
     
	typedef K ValueType;
	struct KeyOfValue
	{
     
		const K& operator()(const ValueType& v)
		{
     
			return v;
		}
	};
public:
	typedef typename HashTable<K, ValueType, KeyOfValue, HashFunc>::iterator iterator;
	pair<iterator, bool> insert(const ValueType& kv)
	{
     
		return _ht.Insert(kv);
	}
	iterator find(const K& key)
	{
     
		return _ht.Find(key);
	}
	size_t count(const K& key)
	{
     
		return _ht.Count();
	}
	iterator begin()
	{
     
		return _ht.Begin();
	}
	iterator end()
	{
     
		return _ht.End();
	}
private:
		HashTable<K, ValueType, KeyOfValue, HashFunc> _ht;
};
void TestUnorderedSet()
{
     
	UnorderSet<int> s;
	s.insert(2);
	s.insert(6);
	s.insert(4);
	s.insert(14);
	s.insert(16);
	s.find(2);
	UnorderSet<int>::iterator it = s.begin();
	while (it != s.end())
	{
     
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

main.h

#include"unordered_map.h"
#include"unordered_set.h"
int main()
{
     
	//TestUnorderedMap();
	TestUnorderedSet();
	return 0;
}

你可能感兴趣的:(C++)