IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 哈希结构的实现 -> 正文阅读

[数据结构与算法]哈希结构的实现

目录

unordered系列关联式容器

哈希的简介

哈希表闭散列实现

哈希表开散列实现

用哈希表来封装map和set

位图

布隆过滤器与哈希切割

全部代码


unordered系列关联式容器

unordered系列关联式容器有四种,这里主要对unordered_map和unordered_set这两种容器进行介绍

unordered_map

unordered_map是存储<key, value>键值对的关联式容器,其允许通过keys快速的索引到与其对应的value

在内部,unordered_map没有对<kye, value>按照任何特定的顺序排序

unordered_map容器通过key访问单个元素要比map快,但它通常在遍历元素子集的范围迭代方面效率较低

unordered_maps实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问value

unordered_set

unordered_set和unordered_map在性质上差不多,因为底层结构是一致的,都是哈希结构,区别在与,unordered_set不支持[],因为它存储的是key与value相同,所以无需支持[]

哈希的简介

概念

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

哈希函数有多种,这里采用除留余数法

哈希冲突

比如14和4取模后都是4,而4对应的位置只能存储一个数字,那么就出现了哈希冲突,而解决哈希冲突的常见两种方法有闭散列和开散列

哈希表闭散列实现

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

寻找下一空位置有线性探测和二次探测两种

线性探测

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

?二次探测

从发生冲突的位置开始,跨越探测,直到寻找到下一个空位置为止

index是哈希表的下标

首先因为有闭散列和开散列两种,所以采用命名空间的形式来防止命名冲突

?

然后定义一个枚举类型,用来判断该下标的空间的状态,是存在数据,还是删除过数据,或者没有数据,便于插入,删除,及查找数据

封装节点,开始必然为每个下标所在的空间必然为空状态,所以初始化为空状态

?

因为要是除留取余法,必然要是整数,所以还需要借助仿函数来实现

内置类型

string类型

string类型转整形,可以将字符串的每个字母的ASCII码值加起来,但要是只是顺序不同,那总数也会相同,出现哈希冲突,为了尽量减少哈希冲突,就有了下图的方法,其中31是累乘因子

string类型有下列两种写法,任选其一即可

对于其它自定义类型,比如日期,则按照特定方式取整即可?

哈希表同样采用模板的形式实现,另外私有成员有中的_n是有效数据个数,便于后续插入数据扩容使用

?

查找数据

如果表为空,则直接返回nullptr即可

首先用待查找的key模表的长度取模作为待查找的初始下标,然后一直往后查找,同时往后走时,最后别忘了再取模一次,因为可能会存在初始下标空间的前面空间,找不到时,则直接返回nullptr

删除数据

首先先查找数据,找到数据了有效数据就减少一个,然后将那个节点的状态置为删除状态,删除成功返回true即可,没找到就表示删除失败,返回false

插入数据

首先先查找数据,如果存在,就表示待插入的数据已经有了,所以无需再插入,返回false即可

根据有效数据个数,得到负载因子,如果负载因子大于等于0.7则表示需要扩容了,初始没有空间时,也一样需要扩容

负载因子越小,冲突概率越低,效率越高,空间浪费越多

负载因子越大,冲突概率越高,效率越低,空间浪费越少

如果是初始时则开10个空间,其它情况则直接开原来的2倍空间

?

创建一个新表,开辟新空间大小的空间,再将其数据拷贝到新表,然后将新表对象中的vector成员与this对象中的vector成员交换即可

如果不需要开辟新空间

则先将key取模,找到对应的下标,如果有空间,则直接插入数据,没有,则继续往后面找,与find()类似,最后有效数据+1,返回true即可

闭散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷

哈希表开散列实现

概念

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

封装节点,其实就是单链表的节点封装

?

私有成员与闭散列一致,同时,还是采用除留去余法,所以会用到仿函数,与上面闭散列一致

查找数据

如果哈希表为空,则直接返回nullptr

找到待查找的数据节点的下标,以及声明一个局部变量来保存这条链的头节点

从头开始往后找,找到了返回节点地址,没找到返回nullptr

删除数据

如果哈希表为空,返回false,表示删除失败

找到待删除数据节点的那条链的头节点

如果找到了,则分为两种情况,如果就是头节点,那就直接头删,否则就中间删除

?

?如果没找到,则继续往后面找,找不到则返回false,表示删除失败

?插入数据

首先先查找数据,如果存在,就表示待插入的数据已经有了,所以无需再插入,返回false即可

?

如果负载因子等于1时,则需要扩容,初始开空间时开10个空间,其余情况开原来的2倍空间

然后将原表的数据按头插拷贝到新表,将原表每条链的表头置空,最后再交换this对象的vector成员与新表的vector成员即可

如果不需要扩容,则直接头插,然后将有效数据个数+1,再返回true即可

为了减少哈希冲突,每次扩容后的空间大小都为素数

??

用哈希表来封装map和set

改善哈希表

因为既要封装map,又要封装set,所以将数据类型从pair类型变为了T类型

迭代器

因为会用到哈希表,所以提前声明一下?

因为要封装map和set,所以多加了一个模板参数KeyOfT,采用了仿函数

因为map和set存储的数据不同,一个是pair<key,value>,一个是key所以对*和对->都要重载

判断相不相等,比较的是节点的地址相不相等

重载++运算符

如果这条链上还有节点,即没有遇到nullptr,则直接往后走即可

如果为nullptr,则从哈希表的下一个元素开始找,比如下图,走完11后,则继续往后面走,下一个元素表头为空,所以就走到了13,不为nullptr,则跳出循环

?

如果是循环自动结束的,即走完整个哈希表了,则直接置nullptr即可

?

如果是break出来的,则直接返回到那个节点,比如上述所说的13

?

拷贝构造

别忘了写一个默认构造函数,因为有了拷贝构造之后,编译器不会默认生成了

这里采用的是头插法,所以顺序会有变化

重载赋值运算符

将实参传给形参时会有一次拷贝构造,所以只需将this对象的两个成员与形参的交换即可

析构函数

从哈希表第一个元素开始销毁节点,直到走完哈希表结束

初始节点

如果哈希表第一个元素的头节点不为nullptr,则它就是初始节点,否则直接返回nullptr的迭代器

结尾节点,即nullptr

重载[]运算符

[]可查找value,也可修改或插入

位图

位图的作用:数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在,比如在一堆数据中找5在不在,数据很多时,可以开辟整形最大值那么大的以bit为单位的空间,然后一个整数对应一个比特位,如果5对应的比特位是1,那就在这堆数据中,是0就不在

1byte=8bit,所以开空间数如下图

将某个bit位置为1

将某个bit位置为0

检查某个bit位是否为1

0为假,非0为真

位图:节省空间,效率高,但只能处理整数

布隆过滤器与哈希切割

概念

布隆过滤器是由布隆在1970年提出的 一种紧凑型的、比较巧妙的概率型数据结构,特点是高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存在”,它是用多个哈希函数,将一个数据映射到位图结构中。既可以提升查询效率,也可以节省大量的内存空间

作用:用来解决字符串,自定义类型对象在不在的问题

如果采用将字符串转整数,即将字符串的各个字符加起来取模作为下标,那就可能会存在哈希冲突,比如下图中eat和ate

这种方式,判断不在是准确的,比如eat和ate那个位置的比特位为0,则eat肯定不在,只是判断在会存在误判

解决方式

将一个元素用多个哈希函数映射到一个位图中,比如ate,只要三个种有一个bit位为0,则就表示ate不在,降低了误判率

这里以哈希函数中三个不同的哈希函数为例模板参数

//hashTable-副本
#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)
	{
		//BKDR
		size_t value = 0;
		for (auto ch : s)
		{
			value *= 31;
			value += ch;
		}
		return value;
	}
};

namespace CloseHash
{
	enum Status
	{
		EXIST,
		EMPTY,
		DELETE
	};

	template<class K,class V>
	struct HashData
	{
		pair<K, V> _kv;
		Status _status = EMPTY;
	};

	struct HashStr
	{
		size_t operator()(const string& s)
		{
			//BKDR
			size_t value = 0;
			for (auto ch : s)
			{
				value *= 31;
				value += ch;
			}
			return value;
		}
	};

	template<class K,class V,class HashFunc = Hash<K>>
	class HashTable
	{
	public:
		bool Erase(const K& key)
		{
			HashData<K, V>* ret = Find(key);
			if (ret == nullptr)
			{
				return false;
			}
			else
			{
				--_n;
				ret->_status = DELETE;
				return true;
			}
		}

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

			HashFunc hf;
			size_t start = hf(key) % _tables.size();
			size_t i = 0;
			size_t index = start;
			//线性探测 or 二次探测
			while (_tables[index]._status != EMPTY)
			{
				if (_tables[index]._kv.first == key && _tables[index]._status == EXIST)
				{
					return &_tables[index];
				}

				++i;
				//index = start + i*i;
				index = start + i;

				index %= _tables.size();
			}

			return nullptr;
		}

		bool Insert(const pair<K, V>& kv)
		{
			HashData<K, V>* ret = Find(kv.first);
			if (ret)
			{
				return false;
			}

			//负载因子到0.7,就扩容
			//负载因子越小,冲突概率越低,效率越高,空间浪费越多
			//负载因子越大,冲突概率越高,效率越低,空间浪费越少
			if (_tables.size() == 0 || _n * 10 / _tables.size() >= 7)
			{
				//扩容
				size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				//vector<HashData<K,V>> newTables;
				//newTables.resize(newSize);
				//遍历原表,把原表中的数据,重新按newSize映射到新表
				//for(size_t i = 0;i < _tables.size();++i)
				//{
					//
				//}
				//_tables.swap(newTables);
				HashTable<K, V, HashFunc> newHT;
				newHT._tables.resize(newSize);
				for (size_t i = 0; i < _tables.size(); ++i)
				{
					if (_tables[i]._status == EXIST)
					{
						newHT.Insert(_tables[i]._kv);
				    }
				}

				_tables.swap(newHT._tables);
			}

			HashFunc hf;
			size_t start = hf(kv.first) % _tables.size();
			size_t i = 0;
			size_t index = start;
			//线性探测 or 二次探测
			while (_tables[index]._status == EXIST)
			{
				++i;
				//index = start + i * i;
				index = start + i;

				index %= _tables.size();
			}

			_tables[index]._kv = kv;
			_tables[index]._status = EXIST;
			++_n;

			return true;
		}
	private:
		vector<HashData<K, V>> _tables;
		size_t _n = 0;//有效数据个数
	};

	void TestHashTable1()
	{
		//HashTable<int,int,Hash<int>> ht;
		HashTable<int, int> ht;

		int a[] = { 2,12,22,32,42,52,62 };
		for (auto e : a)
		{
			ht.Insert(make_pair(e, e));
		}

		ht.Insert(make_pair(72, 72));
		ht.Insert(make_pair(72, 72));
		ht.Insert(make_pair(-1, -1));
		ht.Insert(make_pair(-999, -999));

		Hash<int> hs;
		cout << hs(9) << endl;
		cout << hs(-9) << endl;

		cout << ht.Find(12) << endl;
		ht.Erase(12);
		cout << ht.Find(12) << endl;
	}

	struct Date
	{

	};

	struct HashDate
	{
		size_t operator()(const Date& d)
		{
			//...
		}
	};

	void TestHashTable2()
	{
		HashStr hs;
		cout << hs("sort") << endl;
		cout << hs("insert") << endl;
		cout << hs("eat") << endl;
		cout << hs("ate") << endl;
		cout << hs("abcd") << endl;
		cout << hs("aadd") << endl;

		HashTable<string, string> ht;
		ht.Insert(make_pair("sort", "排序"));
		ht.Insert(make_pair("string", "字符串"));

		//当key是一个定义类型时,需要配置一个仿函数,将key转成整形
		HashTable<Date, string, HashDate> htds;
	}
}

namespace LinkHash
{
	template<class K,class V>
	struct HashNode
	{
		pair<K, V> _kv;
		HashNode<K, V>* _next;

		HashNode(const pair<K, V>& kv):_kv(kv),_next(nullptr)
		{}
	};

	template<class K,class V,class HashFunc = Hash<K>>
	class HashTable
	{
		typedef HashNode<K, V> Node;
	public:

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

			HashFunc hf;
			size_t index = hf(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[index];
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					if (prev == nullptr) //头删
					{
						_tables[index] = cur->_next;
					}
					else //中间删除
					{
						prev->_next = cur->_next;
					}

					--_n;

					delete cur;

					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}

			return false;
		}

		Node* Find(const K& key)
		{
			if (_tables.empty())
			{
				return nullptr;
			}

			HashFunc hf;
			size_t index = hf(key) % _tables.size();
			Node* cur = _tables[index];
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					return cur;
				}
				else
				{
					cur = cur->_next;
				}
			}

			return nullptr;
		}

		bool Insert(const pair<K, V>& kv)
		{
			Node* ret = Find(kv.first);
			if (ret)
				return false;

			HashFunc hf;
			if (_n == _tables.size())
			{
				size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				//...
				vector<Node*> newTables;
				newTables.resize(newSize);
				for (size_t i = 0; i < _tables.size(); ++i)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;

						size_t index = hf(cur->_kv.first) % newTables.size();
						//头插
						cur->_next = newTables[index];
						newTables[index] = cur;

						cur = next;
					}

					_tables[i] = nullptr;
				}

				_tables.swap(newTables);
			}
			size_t index = hf(kv.first) % _tables.size();
			Node* newnode = new Node(kv);
			//头插
			newnode->_next = _tables[index];
			_tables[index] = newnode;

			++_n;
			return true;
		}

	private:
		/*struct Data
		{
			forward_list<T> _list;
			set<T> _rbtree;
			size_t len;
		};
		vector<Data> _table;*/
		vector<Node*> _tables;

		size_t _n = 0;//有效数据的个数
	};

	void TestHashTable()
	{
		int a[] = { 4,24,14,7,37,27,57,67,34,14,54 };
		HashTable<int, int> ht;
		for (auto e : a)
		{
			ht.Insert(make_pair(e, e));
		}

		ht.Insert(make_pair(84, 84));
	}
};

//hashTable
#pragma once
#include<vector>
#include<string>

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

//特化
template<>
struct Hash< string >
{
	size_t operator()(const string& s)
	{
		//BKDR
		size_t value = 0;
		for (auto ch : s)
		{
			value *= 31;
			value += ch;
		}
		return value;
	}
};

namespace CloseHash
{
	enum Status
	{
		EXIST,
		EMPTY,
		DELETE
	};

	template<class K, class V>
	struct HashData
	{
		pair<K, V> _kv;
		Status _status = EMPTY;
	};

	//struct HashStr
	//{
	//	size_t operator()(const string& s)
	//	{
	//		//BKDR
	//		size_t value = 0;
	//		for (auto ch : s)
	//		{
	//			value *= 31;
	//			value += ch;
	//		}
	//		return value;
	//	}
	//};

	template<class K, class V, class HashFunc = Hash<K>>
	class HashTable
	{
	public:
		bool Erase(const K& key)
		{
			HashData<K, V>* ret = Find(key);
			if (ret == nullptr)
			{
				return false;
			}
			else
			{
				--_n;
				ret->_status = DELETE;
				return true;
			}
		}

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

			HashFunc hf;
			size_t start = hf(key) % _tables.size();
			size_t i = 0;
			size_t index = start;
			//线性探测 or 二次探测
			while (_tables[index]._status != EMPTY)
			{
				if (_tables[index]._kv.first == key && _tables[index]._status == EXIST)
				{
					return &_tables[index];
				}

				++i;
				//index = start + i*i;
				index = start + i;

				index %= _tables.size();
			}

			return nullptr;
		}

		bool Insert(const pair<K, V>& kv)
		{
			HashData<K, V>* ret = Find(kv.first);
			if (ret)
			{
				return false;
			}

			//负载因子到0.7,就扩容
			//负载因子越小,冲突概率越低,效率越高,空间浪费越多
			//负载因子越大,冲突概率越高,效率越低,空间浪费越少
			if (_tables.size() == 0 || _n * 10 / _tables.size() >= 7)
			{
				//扩容
				size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				//vector<HashData<K,V>> newTables;
				//newTables.resize(newSize);
				//遍历原表,把原表中的数据,重新按newSize映射到新表
				//for(size_t i = 0;i < _tables.size();++i)
				//{
					//
				//}
				//_tables.swap(newTables);
				HashTable<K, V, HashFunc> newHT;
				newHT._tables.resize(newSize);
				for (size_t i = 0; i < _tables.size(); ++i)
				{
					if (_tables[i]._status == EXIST)
					{
						newHT.Insert(_tables[i]._kv);
					}
				}

				_tables.swap(newHT._tables);
			}

			HashFunc hf;
			size_t start = hf(kv.first) % _tables.size();
			size_t i = 0;
			size_t index = start;
			//线性探测 or 二次探测
			while (_tables[index]._status == EXIST)
			{
				++i;
				//index = start + i * i;
				index = start + i;

				index %= _tables.size();
			}

			_tables[index]._kv = kv;
			_tables[index]._status = EXIST;
			++_n;

			return true;
		}
	private:
		vector<HashData<K, V>> _tables;
		size_t _n = 0;//有效数据个数
	};

	void TestHashTable1()
	{
		//HashTable<int,int,Hash<int>> ht;
		HashTable<int, int> ht;

		int a[] = { 2,12,22,32,42,52,62 };
		for (auto e : a)
		{
			ht.Insert(make_pair(e, e));
		}

		ht.Insert(make_pair(72, 72));
		ht.Insert(make_pair(72, 72));
		ht.Insert(make_pair(-1, -1));
		ht.Insert(make_pair(-999, -999));

		Hash<int> hs;
		cout << hs(9) << endl;
		cout << hs(-9) << endl;

		cout << ht.Find(12) << endl;
		ht.Erase(12);
		cout << ht.Find(12) << endl;
	}

	struct Date
	{

	};

	struct HashDate
	{
		size_t operator()(const Date& d)
		{
			//...
		}
	};

	void TestHashTable2()
	{
		/*HashStr hs;
		cout << hs("sort") << endl;
		cout << hs("insert") << endl;
		cout << hs("eat") << endl;
		cout << hs("ate") << endl;
		cout << hs("abcd") << endl;
		cout << hs("aadd") << endl;*/

		HashTable<string, string> ht;
		ht.Insert(make_pair("sort", "排序"));
		ht.Insert(make_pair("string", "字符串"));

		//当key是一个定义类型时,需要配置一个仿函数,将key转成整形
		HashTable<Date, string, HashDate> htds;
	}
}

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;
		}

		Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t index = hf(kot(_node->_data)) % _pht->_tables.size();
				++index;
				//找下一个不为空的值
				while (index < _pht->_tables.size())
				{
					if (_pht->_tables[index])
					{
						break;
					}
					else
					{
						++index;
					}
				}

				//表走完了,都没有找到下一个桶
				if (index == _pht->_tables.size())
				{
					_node = nullptr;
				}
				else
				{
					_node = _pht->_tables[index];
				}
			}
			return *this;
		}

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

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

	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()
		{}
		//显示指定编译器去生成默认构造函数
		//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* copy = new Node(cur->_data);
					copy->_next = _tables[i];
					_tables[i] = copy;

					cur = cur->_next;
				}
			}
		}

		//ht1 = ht2
		self& operator=(self copy)
		{
			swap(_n, copy._n);
			_tables.swap(copy._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()
		{
			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);
		}

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

			HashFunc hf;
			size_t index = hf(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[index];
			KeyOfT kot;
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr) //头删
					{
						_tables[index] = cur->_next;
					}
					else //中间删除
					{
						prev->_next = cur->_next;
					}

					--_n;

					delete cur;

					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}

			return false;
		}

		iterator Find(const K& key)
		{
			if (_tables.empty())
			{
				return end();
			}

			HashFunc hf;
			size_t index = hf(key) % _tables.size();
			Node* cur = _tables[index];
			KeyOfT kot;
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur,this);
				}
				else
				{
					cur = cur->_next;
				}
			}

			return end();
		}

		size_t GetNextPrime(size_t num)
		{
			static const unsigned long __stl_prime_list[28] =
			{
				53,97,193,389,769,
				1543,3079,6151,12289,24593,
				49157,98317,196613,393241,786433,
				1572869,3145739,6291469,12582917,25165843,
				50331653,100663319,201326611,402653189,805306457,
				1610612741,3221225473,4294967291
			};

			for(size_t i = 0;i < 28;++i)
			{
				if (__stl_prime_list[i] > num)
				{
					return __stl_prime_list[i];
				}
			}
			return __stl_prime_list[27];
		}

		pair<iterator,bool> Insert(const T& data)
		{
			KeyOfT kot;
			iterator ret = Find(kot(data));
			if (ret != end())
				return make_pair(ret,false);

			HashFunc hf;

			//负载因子 == 1时扩容
			if (_n == _tables.size())
			{
				//size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				//...
				size_t newSize = GetNextPrime(_tables.size());
				/*if (newSize == _tables.size())
				{
					break;
				}*/
				vector<Node*> newTables;
				newTables.resize(newSize);
				for (size_t i = 0; i < _tables.size(); ++i)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;

						size_t index = hf(kot(cur->_data)) % newTables.size();
						//头插
						cur->_next = newTables[index];
						newTables[index] = cur;

						cur = next;
					}

					_tables[i] = nullptr;
				}

				_tables.swap(newTables);
			}
			size_t index = hf(kot(data)) % _tables.size();
			Node* newnode = new Node(data);
			//头插
			newnode->_next = _tables[index];
			_tables[index] = newnode;

			++_n;
			return make_pair(iterator(newnode,this),false);
		}

	private:
		/*struct Data
		{
			forward_list<T> _list;
			set<T> _rbtree;
			size_t len;
		};
		vector<Data> _table;*/
		vector<Node*> _tables;

		size_t _n = 0;//有效数据的个数
	};
}

//unordered-map
#pragma once
#include"HashTable.h"

namespace bit
{
	template<class K,class V,class hash = Hash<K>>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename LinkHash::HashTable<K, pair<K, V>, MapKeyOfT, hash>::iterator iterator;
		iterator begin()
		{
			return _ht.begin();
		}

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

		V& operator[](const K& key)
		{
			auto ret = _ht.Insert(make_pair(key, V()));
			return ret.first->second;
		}

		pair<iterator,bool> insert(const pair<K, V>& kv)
		{
			return _ht.Insert(kv);
		}

	private:
		LinkHash::HashTable<K, pair<K,V>, MapKeyOfT, hash > _ht;
	};

	void test_unordered_map()
	{
		unordered_map<string, string> dict;
		dict.insert(make_pair("sort", "排序"));
		dict.insert(make_pair("string", "字符串"));
		dict.insert(make_pair("map", "地图、映射"));
		/*cout << dict["sort"] << endl;
		cout << dict["string"] << endl;
		cout << dict["map"] << endl;*/

		//dict["right"] = "右边";

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

		unordered_map<string, string> copy(dict);
		for (auto& kv : copy)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;
	}
}

//unordered-set
#pragma once
namespace bit
{
	template<class K,class hash = Hash<K>>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename LinkHash::HashTable<K, K, SetKeyOfT, hash>::iterator iterator;
		iterator begin()
		{
			return _ht.begin();
		}

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

		pair<iterator,bool> insert(const K& key)
		{
			return _ht.Insert(key);
		}
	private:
		LinkHash::HashTable<K, K, SetKeyOfT, hash> _ht;
	};

	void test_unordered_set()
	{
		unordered_set<int> us;
		us.insert(4);
		us.insert(14);
		us.insert(34);
		us.insert(7);
		us.insert(24);
		us.insert(17);
		unordered_set<int>::iterator it = us.begin();
		while (it != us.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		unordered_set<int> uss(us);
		unordered_set<int>::iterator It = uss.begin();
		while (It != uss.end())
		{
			cout << *It << " ";
			++It;
		}
		cout << endl;

		/*unordered_set<string> uss;
		uss.insert("sort");
		uss.insert("hash");*/

		/*unordered_set<int> us;
		for (size_t i = 0; i < 100; ++i)
		{
			us.insert(i);
		}*/
	}
}

//bitSet
#pragma once
#include<vector>

namespace bit
{
	template<size_t N>
	class bitset
	{
	public:
		bitset()
		{
			_bits.resize(N / 8 + 1, 0);
		}

		void set(size_t x)
		{
			size_t i = x / 8;
			size_t j = x % 8;

			_bits[i] |= (1 << j);
		}

		void reset(size_t x)
		{
			size_t i = x / 8;
			size_t j = x % 8;

			_bits[i] &= (~(1 << j));
		}

		bool test(size_t x)
		{
			size_t i = x / 8;
			size_t j = x % 8;

			return _bits[i] & (1 << j);
		}

	private:
		std::vector<char> _bits;
		//std::vector<int> _bits;
	};

	void test_bitset()
	{
		bitset<100> bs;
		bs.set(5);
		bs.set(4);
		bs.set(10);
		bs.set(20);

		cout << bs.test(5) << endl;
		cout << bs.test(4) << endl;
		cout << bs.test(10) << endl;
		cout << bs.test(20) << endl;
		cout << bs.test(21) << endl;
		cout << bs.test(6) << endl << endl;

		bs.reset(20);
		bs.reset(10);
		bs.reset(5);

		cout << bs.test(5) << endl;
		cout << bs.test(4) << endl;
		cout << bs.test(10) << endl;
		cout << bs.test(20) << endl;
		cout << bs.test(21) << endl;
		cout << bs.test(6) << endl;

		//bitset<0xffffffff> bs;
		//bitset<-1> bs;
	}
}

template<size_t N>
class TwoBitSet
{
public:
	void Set(size_t x)
	{
		if (!_bs1.test(x) && !_bs2.test(x)) //00 -> 01
		{
			_bs2.set(x);
		}
		else if (!_bs1.test(x) && _bs2.test(x)) //01 -> 10
		{
			_bs1.set(x);
			_bs2.reset(x);
		}
		//10 表示已经出现2次或以上,不用处理
	}

	void PrintOnceNum()
	{
		for (size_t i = 0; i < N; ++i)
		{
			if (!_bs1.test(i) && _bs2.test(i)) // 01
			{
				cout << i << endl;
			}
		}
	}
private:
	bit::bitset<N> _bs1;
	bit::bitset<N> _bs2;
};

void TestTwoBitSet()
{
	int a[] = { 99,0,4,50,33,44,2,5,99,0,50,99,50,2 };
	TwoBitSet<100> bs;
	for (auto e : a)
	{
		bs.Set(e);
	}
	bs.PrintOnceNum();
}

void TestFindIntersection()
{
	int a1[] = { 5,30,1,99,10 };
	int a2[] = { 8,10,11,9,30,10,30 };
}

//BloomFilter
#pragma once

#include<bitset>
#include<string>
#include<time.h>

struct BKDRHash
{
	size_t operator()(const string& s)
	{
		//BKDR
		size_t value = 0;
		for (auto ch : s)
		{
			value *= 3;
			value += ch;
		}
		return value;
	}
};
struct APHash
{
    size_t operator()(const string& s)
    {
        size_t hash = 0;
        for (long i = 0; i < s.size(); i++)
        {
            if ((i & 1) == 0)
            {
                hash ^= ((hash << 7) ^ s[i] ^ (hash >> 3));
            }
            else
            {
                hash ^= (~((hash << 11) ^ s[i] ^ (hash >> 5)));
            }
        }
        return hash;
    }
};

struct DJBHash
{
    size_t operator()(const string& s)
    {
        size_t hash = 5381;
        for(auto ch : s)
        {
            hash += (hash << 5) + ch;
        }
        return hash;
    }
};

template<size_t N,
size_t X = 8,
class K = string,
class HashFunc1 = BKDRHash,
class HashFunc2 = APHash,
class HashFunc3 = DJBHash>
class BloomFilter
{
public:
    void Set(const K& key)
    {
        size_t len = X * N;
        size_t index1 = HashFunc1()(key) % len;
        size_t index2 = HashFunc2()(key) % len;
        size_t index3 = HashFunc3()(key) % len;
        cout << index1 << endl;
        cout << index2 << endl;
        cout << index3 << endl << endl;

        _bs.set(index1);
        _bs.set(index2);
        _bs.set(index3);
    }

    bool Test(const K& key)
    {
        size_t len = X * N;
        size_t index1 = HashFunc1()(key) % len;
        if (_bs.test(index1) == false)
            return false;

        size_t index2 = HashFunc2()(key) % len;
        if (_bs.test(index2) == false)
            return false;

        size_t index3 = HashFunc3()(key) % len;
        if (_bs.test(index3) == false)
            return false;

        return true;  //存在误判的
    }

    //不支持删除,删除可能会影响其它值。
    void Reset(const K& key);
private:
    bitset<X* N> _bs;
};

void TestBloomFilter1()
{
    BloomFilter<100> bm;
    bm.Set("sort");
    bm.Set("left");
    bm.Set("right");
    bm.Set("eat");
    bm.Set("ate");
    bm.Set("https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html");

}

void TestBloomFilter2()
{
    BloomFilter<100> bf;
    bf.Set("张三");
    bf.Set("李四");
    bf.Set("牛魔王");
    bf.Set("红孩儿");
    bf.Set("eat");

    cout << bf.Test("张三") << endl;
    cout << bf.Test("李四") << endl;
    cout << bf.Test("牛魔王") << endl;
    cout << bf.Test("红孩儿") << endl;
    cout << bf.Test("孙悟空") << endl;
    cout << bf.Test("二郎神") << endl;
    cout << bf.Test("猪八戒") << endl;
    cout << bf.Test("ate") << endl;

    //BloomFilter<100> bf;

    srand(time(0));
    size_t N = 100;
    std::vector<std::string> v1;
    for (size_t i = 0; i < N; ++i)
    {
        std::string ur1 = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";
        ur1 += std::to_string(1234 + i);
        v1.push_back(ur1);
    }

    for (auto& str : v1)
    {
        bf.Set(str);
    }

    for (auto& str : v1)
    {
        cout << bf.Test(str) << endl;
    }
    cout << endl << endl;

    std::vector<std::string> v2;
    for (size_t i = 0; i < N; ++i)
    {
        std::string ur1 = "https://www.cnblogs.com/-clq/archive/2012/05/31/2528153.html";
        ur1 += std::to_string(6789 + i);
        v2.push_back(ur1);
    }

    size_t n2 = 0;
    for (auto& str : v2)
    {
        if (bf.Test(str))
        {
            ++n2;
        }
    }
    cout << "相似字符串误判率:" << (double)n2 / (double)N << endl;

    std::vector<std::string> v3;
    for (size_t i = 0; i < N; ++i)
    {
        string ur1 = "zhihu.com";
        //std::string ur1 = "https://mail.qq.com/cgi-bin/frame_html?sid=0QvJ6bvfhn3EC1Iw&r=5976c09322eae24513a5ff315428cd86&lang=zh";
        //std::string ur1 = "https://www.sogou.com/tx?query=%E5%88%98%E5%BC%BA%E4%B8%9C%E4%BA%8B%E4%BB%B6%20%E5%A5%B3%E6%96%B9%E7%A7%B0%E8%87%AA%E6%84%BF%E5%8F%91%E7%94%9F%E5%85%B3%E7%B3%BB&hdq=sogou-site-706608cfdbcc1886&ekv=3&ie=utf8&";
        //std::string ur1 = "https://www.sogou.com/sogou?ie=utf8&pid=sogou-wsse-af64b05ee108fa0c&query=%E4%BA%BA%E6%B0%91%E7%BD%91%3A%E5%BE%B7%E4%BA%91%E7%A4%BE%E8%AF%A5%E8%87%AA%E6%88%91%E6%A3%80%E8%A7%86%E4%BA%86";
        ur1 += std::to_string(rand());
        v3.push_back(ur1);
    }

    size_t n3 = 0;
    for (auto& str : v3)
    {
        if (bf.Test(str))
        {
            ++n3;
        }
    }
    cout << "不相似字符串误判率:" << (double)n3 / (double)N << endl;
}

设置为1

首先先用三个哈希函数算出对应的下标

再复用前面的位图的函数将其bit位设为1即可?

?

注意:当len越大时,即空间开的越大时,误判率会越低

检查在不在

三个bit位,只要有一个bit位为0,则表示不在

注意:布隆过滤器不支持删除,因为支持删除会消耗很多空间,而且会存在误删,比如上图中的ate和eat,如果删除ate,那就会改变eat中的其中一个bit位

哈希切割

例:给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法,这里就要用到哈希切割

注意:如果Ai和Bi都太大,可以考虑换个哈希函数,再切割一次?

全部代码

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-07-04 23:13:18  更:2022-07-04 23:16:34 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 23:11:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码