C++---哈希

目录

1. unordered系列关联式容器

1.1 unordered_map

1.1.1 unordered_map的介绍

1.1.2 unordered_map的接口说明

1.2 unordered_set

2. 底层结构

2.1 哈希概念

2.2 哈希冲突

2.3 哈希函数

2.4 哈希冲突解决

2.4.1 闭散列

2.4.2 开散列

3. 封装unorder_map和unorder_set

3.1 unorder_map 的封装

3.2 unorder_set 的封装

3.4 unordered_map 加迭代器的封装

3.5 unordered_set 加迭代器的封装

1. unordered系列关联式容器

1.1 unordered_map

1.1.1 unordered_map的介绍

        1. unordered_map是存储键值对的关联式容器,其允许通过keys快速的索引到与其对应的value。
        2. 在unordered_map中,键值通常用于惟一地标识元素,而映射值是一个对象,其内容与此
键关联。键和映射值的类型可能不同。
        3. 在内部,unordered_map没有对按照任何特定的顺序排序, 为了能在常数范围内找到key所对应的value,unordered_map将相同哈希值的键值对放在相同的桶中。
        4. unordered_map容器通过key访问单个元素要比map快,但它通常在遍历元素子集的范围
代方面效率较低
        5. unordered_map实现了直接访问操作符(operator[ ]),它允许使用key作为参数直接访问
value。
        6. 它的迭代器是正向迭代器

1.1.2 unordered_map的接口说明

1. unordered_map的构造

函数声明 功能介绍
unordered_map 构造不同格式的unordered_map对象

2. unordered_map的容量

函数声明 功能介绍
bool empty() const 检测unordered_map是否为空
size_t size() const 获取unordered_map的有效元素个数

3. unordered_map的迭代器

函数声明 功能介绍
begin 返回unordered_map第一个元素的迭代器
end 返回unordered_map最后一个元素下一个位置的迭代器
cbegin 返回unordered_map第一个元素的const迭代器
cend 返回unordered_map最后一个元素下一个位置的const迭代器

4. unordered_map的元素访问

函数声明 功能介绍
operator[] 返回与key对应的value,没有一个默认值

5. unordered_map的查询

函数声明 功能介绍
iterator find(const K& key) 返回key在哈希桶中的位置
size_t count(const K& key) 返回哈希桶中关键码为key的键值对的个数

注意:unordered_map中key是不能重复的,因此count函数的返回值最大为1
 

6. unordered_map的修改操作

函数声明 功能介绍
insert 向容器中插入键值对
erase 删除容器中的键值对
void clear() 清空容器中有效元素个数
void swap(unordered_map&) 交换两个容器中的元素

7. unordered_map的桶操作

函数声明 功能介绍
size_t bucket_count()const 返回哈希桶中桶的总个数
size_t bucket_size(size_t n)const 返回n号桶中有效元素的总个数
size_t bucket(const K& key) 返回元素key所在的桶号

1.2 unordered_set

与unordered_map类似,详情见文档unordered_set

2. 底层结构

unordered系列的关联式容器之所以效率比较高,是因为其底层使用了哈希结构

2.1 哈希概念

        顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N)平衡树中为树的高度,即
O(log_2 N)搜索的效率取决于搜索过程中元素的比较次数

        理想的搜索方法可以不经过任何比较,一次直接从表中得到要搜索的元素

        如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。

当向该结构中:

插入元素:
根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放。

搜索元素:
对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置
取元素比较,若关键码相等,则搜索成功

该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称
为哈希表(Hash Table)(或者称散列表)。

例如:数据集合{1,7,6,4,5,9};
哈希函数设置为:hash(key) = key % capacity ; capacity为存储元素底层空间总的大小。C++---哈希_第1张图片

问题:如果继续向上述哈希表中插入44,会出现什么问题?

2.2 哈希冲突

        上述问题中,不同关键字通过相同哈希函数计算出相同的哈希地址,该种现象称为哈希冲突
哈希碰撞

        把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。

        发生哈希冲突该如何处理呢?

2.3 哈希函数

引起哈希冲突的一个原因可能是:哈希函数设计不够合理

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

常见哈希函数:

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

例题:字符串中的第一个唯一字符C++---哈希_第2张图片

class Solution {
public:
    int firstUniqChar(string s) {
        int hash[256] = {0};
        for(int i = 0; i < s.size(); i++){
            hash[s[i]]++;
        }
        for(int i = 0; i < s.size(); i++){
            if(hash[s[i]] == 1){
                return i;
            }
        }
        return -1;
    }
};

2. 除留余数法--(常用)
设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,
按照哈希函数:Hash(key) = key% p(p<=m),将关键码转换成哈希地址。
C++---哈希_第3张图片

 注意:哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是无法避免哈希冲突

2.4 哈希冲突解决

解决哈希冲突两种常见的方法是闭散列开散列

2.4.1 闭散列

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

那如何寻找下一个空位置呢?

1. 线性探测

        比如2.1中的场景,现在需要插入元素44,先通过哈希函数计算哈希地址,hashAddr为4,
因此44理论上应该插在该位置,但是该位置已经放了值为4的元素,即发生哈希冲突。

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

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

C++---哈希_第4张图片

删除:
        采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素
会影响其他元素的搜索
。比如删除元素4,如果直接删除掉,44查找起来可能会受影响。因此线性探测采用标记的伪删除法来删除一个元素。

// 哈希表每个空间给个标记
// EMPTY此位置空, EXIST此位置已经有元素, DELETE元素已经删除
enum State
{EMPTY, EXIST, DELETE};

载荷因子:

        为了解决 “哈希表什么情况下进行扩容?如何扩容?” 的问题,我们引入载荷因子的概念。

        载荷因子 = 表中元素个数 / 散列表容量

        载荷因子越大,表中元素越多,产生冲突可能性越大;

        载荷因子越小,表中元素越少,产生冲突可能性越小;

        当达到载荷因子我们就扩容,如果载荷因子设置的太小,空间资源浪费就多,载荷因子设置越大,产生冲突的可能性就越大。因此,一般设置载荷因子为 0.7——0.8。

线性探测简要实现哈希表代码如下:

enum State//标志位
{
	EMPTY,
	EXIST,
	DELETE
};

template
struct HashData//节点元素包含键值对和标志位
{
	pair _kv;
	State _state = EMPTY;
};

template
struct HashFunc//用于方便比较的仿函数
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};

//特化
template<>
struct HashFunc
{
	size_t operator()(const string& key)
	{
		size_t val = 0;
		for (auto ch : key)
		{
			//防止出现abc = cba 这种情况,有大佬提出加每个字符前*131
			val *= 131;
			val += ch;
		}
		return val;
	}
};

//因为如果K为string类型,无法用 % 来实现映射关系,因此我们写个Hash仿函数
template>
class HashTable
{
public:
	bool Insert(const pair& kv)
	{
		if (Find(kv.first))
			return false;

		//表为空或载荷因子超70%就扩容
		if (_tables.size() == 0 || 10 * _size / _tables.size() >= 7)
		{
			size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
			HashTable newHT;
			newHT._tables.resize(newSize);
			//旧表数据映射到新表
			for (auto e : _tables)
			{
				if (e._state == EXIST)
				{
					newHT.Insert(e._kv);
				}
			}
			_tables.swap(newHT._tables);
		}

		Hash hash;
		size_t hashi = hash(kv.first) % _tables.size();
		//线性探测
		while (_tables[hashi]._state == EXIST)
		{
			hashi++;
			hashi %= _tables.size();
		}
		_tables[hashi]._kv = kv;
		_tables[hashi]._state = EXIST;
		++_size;

		return true;
	}

	HashData* Find(const K& key)
	{
		if (_tables.size() == 0)
		{
			return nullptr;
		}

		Hash hash;
		size_t hashi = hash(key) % _tables.size();
		while (_tables[hashi]._state != EMPTY)
		{
			if (_tables[hashi]._state != DELETE && _tables[hashi]._kv.first == key)
			{
				return &_tables[hashi];
			}
			hashi++;
			hashi %= _tables.size();
		}
		return nullptr;
	}

	bool Erase(const K& key)
	{
		HashData* ret = Find(key);
		if (ret)
		{
			ret->_state = DELETE;
			--_size;
			return true;
		}
		else
		{
			return false;
		}
	}


private:
	vector> _tables;
	size_t _size = 0;//存储多少有效数据
};

总结

        线性探测优点:实现非常简单
        线性探测缺点:某个位置冲突很多的情况下,互相占用,冲突一片,如下图:C++---哈希_第5张图片

2. 二次探测

二次探测不是探测二次,是探测2的 i 次方:C++---哈希_第6张图片

 二次探测只需要把原来线性探测略微改动一下即可:

bool Insert(const pair& kv)
	{
		if (Find(kv.first))
			return false;

		//表为空或载荷因子超70%就扩容
		if (_tables.size() == 0 || 10 * _size / _tables.size() >= 7)
		{
			size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
			HashTable newHT;
			newHT._tables.resize(newSize);
			//旧表数据映射到新表
			for (auto e : _tables)
			{
				if (e._state == EXIST)
				{
					newHT.Insert(e._kv);
				}
			}
			_tables.swap(newHT._tables);
		}

		Hash hash;
		size_t start = hash(kv.first) % _tables.size();
		size_t i = 0;
		size_t hashi = start;
		//二次探测
		while (_tables[hashi]._state == EXIST)
		{
			++i;
			hashi = start + i * i;
			hashi %= _tables.size();
		}
		_tables[hashi]._kv = kv;
		_tables[hashi]._state = EXIST;
		++_size;

		return true;
	}

2.4.2 开散列

开散列概念

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

哈希桶简单实现:

template
struct HashFunc
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};

//特化
template<>
struct HashFunc
{
	size_t operator()(const string& key)
	{
		size_t val = 0;
		for (auto ch : key)
		{
			//防止出现abc = cba 这种情况,有大佬提出加每个字符前*131
			val *= 131;
			val += ch;
		}
		return val;
	}
};

namespace HashBucket
{
	template
	struct HashNode
	{
		pair _kv;
		HashNode* _next;

		HashNode(const pair& kv)
			:_kv(kv)
			,_next(nullptr)
		{}
	};

	template>
	class HashTable
	{
		typedef HashNode Node;
	public:
		~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 pair& kv)
		{
			//去重
			if (Find(kv.first))
			{
				return false;
			}
			Hash hash;
			//负载因子到1就扩容
			if (_size == _tables.size())
			{
				size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				vector newTables;
				newTables.resize(newSize, nullptr);
				//旧表中节点移动映射到新表
				for (size_t i = 0; i < _tables.size(); ++i)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;

						size_t hashi = hash(cur->_kv.first) % newTables.size();
						cur->_next = newTables[hashi];
						newTables[hashi] = cur;

						cur = next;
					}
					_tables[i] = nullptr;
				}
				_tables.swap(newTables);
			}

			size_t hashi = hash(kv.first) % _tables.size();
			//头插
			Node* newnode = new Node(kv);
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;
			++_size;

			return true;
		}
		Node* Find(const K& key)
		{
			if (_tables.size() == 0)
			{
				return nullptr;
			}

			Hash hash;
			size_t hashi = hash(key) % _tables.size();
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					return cur;
				}
				cur = cur->_next;
			}
			return nullptr;
		}

		bool Erase(const K& key)
		{
			if (_tables.size() == 0)
			{
				return false;
			}

			Hash hash;
			size_t hashi = hash(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					// 1、头删
					// 2、中间删
					if (prev == nullptr)
					{
						_tables[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}

					delete cur;
					--_size;
					return true;
				}

				prev = cur;
				cur = cur->_next;
			}

			return false;
		}

		size_t Size()
		{
			return _size;
		}

		// 表的长度
		size_t TablesSize()
		{
			return _tables.size();
		}

		// 桶的个数
		size_t BucketNum()
		{
			size_t num = 0;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				if (_tables[i])
				{
					++num;
				}
			}

			return num;
		}

		size_t MaxBucketLenth()
		{
			size_t maxLen = 0;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				size_t len = 0;
				Node* cur = _tables[i];
				while (cur)
				{
					++len;
					cur = cur->_next;
				}

				//if (len > 0)
					//printf("[%d]号桶长度:%d\n", i, len);

				if (len > maxLen)
				{
					maxLen = len;
				}
			}

			return maxLen;
		}
	private:
		vector _tables;
		size_t _size = 0;//存储有效数据个数
	};
}

3. 封装unorder_map和unorder_set

简单修改上述哈希桶用于封装实现unorder_map和unorder_set.

首先,我们将原来节点中用来存储节点值的 pair键值对 改为 模板参数 T.


// 因为关联式容器中存储的是的键值对,因此
// k为key的类型,
// T: 如果是map,则为pair; 如果是set,则为k
// KeyOfT: 通过T来获取key的一个仿函数类
namespace Bucket
{
	template
	struct HashNode
	{
		T _data;
		HashNode* _next;

		HashNode(const T& data)
			:_data(data)
			,_next(nullptr)
		{}
	};

	template
	class HashTable
	{
		typedef HashNode Node;
	public:
		~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)
		{
			Hash hash;
			KeyOfT kot;
			//去重
			if (Find(kot(data)))
			{
				return false;
			}
			//负载因子到1就扩容
			if (_size == _tables.size())
			{
				size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				vector newTables;
				newTables.resize(newSize, nullptr);
				//旧表中节点移动映射到新表
				for (size_t i = 0; i < _tables.size(); ++i)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;

						size_t hashi = hash(kot(cur->_data)) % newTables.size();
						cur->_next = newTables[hashi];
						newTables[hashi] = cur;

						cur = next;
					}
					_tables[i] = nullptr;
				}
				_tables.swap(newTables);
			}

			size_t hashi = hash(kot(data)) % _tables.size();
			//头插
			Node* newnode = new Node(data);
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;
			++_size;

			return true;
		}
		Node* Find(const K& key)
		{
			if (_tables.size() == 0)
			{
				return nullptr;
			}

			Hash hash;
			KeyOfT kot;
			size_t hashi = hash(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)
		{
			if (_tables.size() == 0)
			{
				return false;
			}

			KeyOfT kot;
			Hash hash;
			size_t hashi = hash(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					// 1、头删
					// 2、中间删
					if (prev == nullptr)
					{
						_tables[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}

					delete cur;
					--_size;
					return true;
				}

				prev = cur;
				cur = cur->_next;
			}

			return false;
		}

		size_t Size()
		{
			return _size;
		}

		// 表的长度
		size_t TablesSize()
		{
			return _tables.size();
		}

		// 桶的个数
		size_t BucketNum()
		{
			size_t num = 0;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				if (_tables[i])
				{
					++num;
				}
			}

			return num;
		}

		size_t MaxBucketLenth()
		{
			size_t maxLen = 0;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				size_t len = 0;
				Node* cur = _tables[i];
				while (cur)
				{
					++len;
					cur = cur->_next;
				}

				//if (len > 0)
					//printf("[%d]号桶长度:%d\n", i, len);

				if (len > maxLen)
				{
					maxLen = len;
				}
			}

			return maxLen;
		}
	private:
		vector _tables;
		size_t _size = 0;//存储有效数据个数
	};
}

3.1 unorder_map 的封装

#include"HashTable.h"//HashTable.h即为上面哈希桶的实现

namespace zj
{
	template>
	class unorder_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair& kv)
			{
				return kv.first;
			}
		};
	public:
		bool Insert(const pair& kv)
		{
			return _ht.Insert(kv);
		}
	private:
		Bucket::HashTable, Hash, MapKeyOfT> _ht;
	};
}

3.2 unorder_set 的封装

#include"HashTable.h"//HashTable.h即为上面哈希桶的实现

namespace zj
{
	template>
	class unorder_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		bool Insert(const K& key)
		{
			return _ht.Insert(key);
		}
	private:
		Bucket::HashTable _ht;
	};
}

3.3 哈希桶迭代器模板

namespace Bucket
{
	template
	struct HashNode
	{
		T _data;
		HashNode* _next;

		HashNode(const T& data)
			:_data(data)
			,_next(nullptr)
		{}
	};
	//前置声明,不然__HashIterator里定义不了_pht
	template
	class HashTable;

	template
	struct __HashIterator
	{
		typedef HashNode Node;
		typedef HashTable HT;
		typedef __HashIterator Self;

		Node* _node;
		HT* _pht;

		__HashIterator(Node* node, HT* pht)
			:_node(node)
			,_pht(pht)
		{}

		T& operator*()
		{
			return _node->_data;
		}

		T* operator->()
		{
			return &_node->_data;
		}

		Self& operator++()
		{
			if (_node->_next)
			{
				// 当前桶中迭代
				_node = _node->_next;
			}
			else
			{
				// 找下一个桶
				Hash hash;
				KeyOfT kot;
				size_t i = hash(kot(_node->_data)) % _pht->_tables.size();
				++i;
				for (; i < _pht->_tables.size(); ++i)
				{
					if (_pht->_tables[i])
					{
						_node = _pht->_tables[i];
						break;
					}
				}

				// 说明后面没有有数据的桶了
				if (i == _pht->_tables.size())
				{
					_node = nullptr;
				}
			}

			return *this;
		}

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

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

	template
	class HashTable
	{
		typedef HashNode Node;

		template//模板参数友元要带声明
		friend struct __HashIterator;
	public:
		typedef __HashIterator 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()
		{
			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;
			}
		}

		pair Insert(const T& data)
		{
			Hash hash;
			KeyOfT kot;

			// 去重
			iterator ret = Find(kot(data));
			if (ret != end())
			{
				return make_pair(ret, false);
			}

			//负载因子到1就扩容
			if (_size == _tables.size())
			{
				size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				vector newTables;
				newTables.resize(newSize, nullptr);
				//旧表中节点移动映射到新表
				for (size_t i = 0; i < _tables.size(); ++i)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;

						size_t hashi = hash(kot(cur->_data)) % newTables.size();
						cur->_next = newTables[hashi];
						newTables[hashi] = cur;

						cur = next;
					}
					_tables[i] = nullptr;
				}
				_tables.swap(newTables);
			}

			size_t hashi = hash(kot(data)) % _tables.size();
			//头插
			Node* newnode = new Node(data);
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;
			++_size;

			return make_pair(iterator(newnode, this), true);
		}
		iterator Find(const K& key)
		{
			if (_tables.size() == 0)
			{
				return end();
			}

			Hash hash;
			KeyOfT kot;
			size_t hashi = hash(key) % _tables.size();
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur, this);
				}
				cur = cur->_next;
			}
			return end();
		}

		bool Erase(const K& key)
		{
			if (_tables.size() == 0)
			{
				return false;
			}

			KeyOfT kot;
			Hash hash;
			size_t hashi = hash(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					// 1、头删
					// 2、中间删
					if (prev == nullptr)
					{
						_tables[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}

					delete cur;
					--_size;
					return true;
				}

				prev = cur;
				cur = cur->_next;
			}

			return false;
		}

        ....

	private:
		vector _tables;
		size_t _size = 0;//存储有效数据个数
	};

}

3.4 unordered_map 加迭代器的封装

#include"HashTable.h"

namespace zj
{
	template>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename Bucket::HashTable, Hash, MapKeyOfT>::iterator iterator;

		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}

		pair Insert(const pair& kv)
		{
			return _ht.Insert(kv);
		}

		V& operator[](const K& key)
		{
			pair ret = _ht.Insert(make_pair(key, V()));
			return ret.first->second;
		}
	private:
		Bucket::HashTable, Hash, MapKeyOfT> _ht;
	};

	void test_map()
	{
		unordered_map dict;
		dict.Insert(make_pair("sort", ""));
		dict.Insert(make_pair("string", ""));
		dict.Insert(make_pair("left", ""));

		unordered_map::iterator it = dict.begin();
		while (it != dict.end())
		{
			cout << it->first << ":" << it->second << endl;
			++it;
		}
		cout << endl;

	}
}

3.5 unordered_set 加迭代器的封装

#include"HashTable.h"

namespace zj
{
	template>
	class unorder_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename Bucket::HashTable::iterator iterator;

		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}

		pair Insert(const K& key)
		{
			return _ht.Insert(key);
		}
	private:
		Bucket::HashTable _ht;
	};

	void test_set()
	{
		unordered_set s;
		s.insert(2);
		s.insert(3);
		s.insert(1);
		s.insert(2);
		s.insert(5);

		unordered_set::iterator it = s.begin();
		//auto it = s.begin();
		while (it != s.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}
}

你可能感兴趣的:(数据结构,C++,c++,哈希算法,开发语言)