哈希

一.哈希概念

​ 一种采用直接寻址方式(哈希函数)高效查找数据的数据结构

哈希冲突:不同关键字通过相同哈希函数计算出相同的哈希地址的现象

二.哈希冲突解决方案

1.闭散列

闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把新元素存放到冲突位置中的下一个空位置中去,而下一个空位置的查找又有线性探测和二次探测两种方式

(1)线性探测

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

代码实现
#pragma once

#include <vector>

//哈希表中节点状态
enum State {
	EMPTY,
	EXIST,
	DELETE
};

//哈希表中数据节点
template<class V>
struct HashData {
	V _data;
	State _state;
};

//哈希表
template<class K, class V,class KeyOfV>
class HashTable {
public:
	typedef HashData<V> HashData;

	//插入
	bool Insert(const V& val) 
	{
		KeyOfV kofv;//根据value计算key

		//表如果满了,插入位置的查找就会循环下去
		//因此引入负载因子,负载因子 = 表中有效元素的个数 / 表的大小
		//负载因子越大,越容易发生哈希冲突
		//负载因子越小,冲突概率越小,整体效率越高,浪费空间越大
		//负载因子一般取一个折中值
		//哈希表并不是满了才增容,一般负载因子大于等于0.7就开始增容
		if (_tables.size() == 0 || _num * 10 / _tables.size() >= 7)
		{
			//1.增容的传统写法
			重新开辟一个二倍大小的新表
			//int newC = (_tables.size() == 0) ? 10 : 2 * _tables.size();
			//std::vector<HashData> newTables;
			//newTables.resize(newC);

			将旧表数据重新映射
			//for (size_t i = 0; i < _tables.size(); ++i)
			//{
			//	if (_tables[i]._state == EXIST)
			//	{
			//		int newPos = kofv(_tables[i]._data) % newC;
			//		while (newTables[newPos]._state == EXIST)
			//		{
			//			++newPos;
			//			if (newPos == newC)
			//				newPos = 0;
			//		}
			//		newTables[newPos] = _tables[i];
			//	}
			//}
			释放旧表空间
			//_tables.swap(newTables);//新表和旧表交换,除了作用域自动释放临时变量

			//增容的现代写法
			//1.建一个新的哈希对象
			HashTable<K,V,KeyOfV> newHt;
			int newC = _tables.size() == 0 ? 10 : 2 * _tables.size();
			newHt._tables.resize(newC);
			//2.插入数据
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				if (_tables[i]._state == EXIST)
				{
					newHt.Insert(_tables[i]._data);
				}
			}
			//3.交换两张表中的内容
			_tables.swap(newHt._tables);
		}

		//1.计算哈希位置
		size_t pos = kofv(val) % _tables.size();

		//2.寻找插入位置
		while (_tables[pos]._state == EXIST)
		{
			//不允许重复
			if (kofv(_tables[pos]._data) == kofv(val))
				return false;
			//线性探测
			++pos;
			if (pos == _tables.size())
				pos = 0;
		}

		//3.插入数据
		_tables[pos]._data = val;
		_tables[pos]._state = EXIST;
		++_num;

		return true;
	}

	//查找
	HashData* Find(const K& key)
	{
		KeyOfV kofv;
		//计算哈希位置
		int pos = key % _tables.size();
		while (_tables[pos]._state != EMPTY)
		{
			if (kofv(_tables[pos]._data) == key)
			{
				if (_tables[pos]._state == EXIST)
					return &_tables[pos];
				else if (_tables[pos]._state == DELETE)
					return nullptr;
			}

			++pos;
			if (pos == _tables.size())
				pos = 0;
		}

		return nullptr;//找不到
	}

	//删除
	bool Erase(const K& key)
	{
		//找到对应位置的数值
		HashData* pos = Find(key);
		if (pos != nullptr)
		{
			pos->_state = DELETE;
			--_num;
			return true;
		}
		return false;
	}
private:
	std::vector<HashData> _tables;	//表中数据
	size_t _num = 0;				//有效元素个数
};
(2)二次探测

二次探测:每次以二次方的形式递增,寻找下一个空位置

代码实现
#pragma once

#include <vector>

//哈希表中节点状态
enum State {
	EMPTY,
	EXIST,
	DELETE
};

//哈希表中数据节点
template<class V>
struct HashData {
	V _data;
	State _state;
};

//哈希表
template<class K, class V,class KeyOfV>
class HashTable {
public:
	typedef HashData<V> HashData;

	//插入
	bool Insert(const V& val) 
	{
		KeyOfV kofv;//根据value计算key

		//表如果满了,插入位置的查找就会循环下去
		//因此引入负载因子,负载因子 = 表中有效元素的个数 / 表的大小
		//负载因子越大,越容易发生哈希冲突
		//负载因子越小,冲突概率越小,整体效率越高,浪费空间越大
		//负载因子一般取一个折中值
		//哈希表并不是满了才增容,一般负载因子大于等于0.7就开始增容
		if (_tables.size() == 0 || _num * 10 / _tables.size() >= 7)
		{
			//1.增容的传统写法
			重新开辟一个二倍大小的新表
			//int newC = (_tables.size() == 0) ? 10 : 2 * _tables.size();
			//std::vector<HashData> newTables;
			//newTables.resize(newC);

			将旧表数据重新映射
			//for (size_t i = 0; i < _tables.size(); ++i)
			//{
			//	if (_tables[i]._state == EXIST)
			//	{
			//		int newPos = kofv(_tables[i]._data) % newC;
			//		while (newTables[newPos]._state == EXIST)
			//		{
			//			++newPos;
			//			if (newPos == newC)
			//				newPos = 0;
			//		}
			//		newTables[newPos] = _tables[i];
			//	}
			//}
			释放旧表空间
			//_tables.swap(newTables);//新表和旧表交换,除了作用域自动释放临时变量

			//增容的现代写法
			//1.建一个新的哈希对象
			HashTable<K,V,KeyOfV> newHt;
			int newC = _tables.size() == 0 ? 10 : 2 * _tables.size();
			newHt._tables.resize(newC);
			//2.插入数据
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				if (_tables[i]._state == EXIST)
				{
					newHt.Insert(_tables[i]._data);
				}
			}
			//3.交换两张表中的内容
			_tables.swap(newHt._tables);
		}

		//1.计算哈希位置
		size_t pos = kofv(val) % _tables.size();
		
		size_t start = kofv(val) % _tables.size();//起始位置
		pos = start;
		int i = 1;
		//2.寻找插入位置有两种方式,线性探测和二次探测
		while (_tables[pos]._state == EXIST)
		{
			//不允许重复
			if (kofv(_tables[pos]._data) == kofv(val))
				return false;

			//二次探测
			pos = start + i * i;
			pos %= _tables.size();
			++i;
		}

		//3.插入数据
		_tables[pos]._data = val;
		_tables[pos]._state = EXIST;
		++_num;

		return true;
	}

	//查找
	HashData* Find(const K& key)
	{
		KeyOfV kofv;
		//计算哈希位置
		int pos = key % _tables.size();
		while (_tables[pos]._state != EMPTY)
		{
			if (kofv(_tables[pos]._data) == key)
			{
				if (_tables[pos]._state == EXIST)
					return &_tables[pos];
				else if (_tables[pos]._state == DELETE)
					return nullptr;
			}

			++pos;
			if (pos == _tables.size())
				pos = 0;
		}

		return nullptr;//找不到
	}

	//删除
	bool Erase(const K& key)
	{
		//找到对应位置的数值
		HashData* pos = Find(key);
		if (pos != nullptr)
		{
			pos->_state = DELETE;
			--_num;
			return true;
		}
		return false;
	}
private:
	std::vector<HashData> _tables;	//表中数据
	size_t _num = 0;				//有效元素个数
};

测试代码:

#include <iostream>
#include "Hash.h"

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

void test()
{
	HashTable<int, int,setKeyOfValue<int>> table;
	table.Insert(3);
	table.Insert(5);
	table.Insert(6);
	table.Insert(4);
	table.Insert(13);
	table.Insert(23);
	table.Insert(14);
	table.Insert(15);

	HashData<int> *target = table.Find(3);
	if (target != nullptr)
		std::cout << target->_data << std::endl;
	else
		std::cout << "Not found" << std::endl;

	table.Erase(3);
	table.Erase(23);
	table.Erase(4);
}
int main()
{
	test();
	return 0;
}

2.开散列

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

#pragma once

/
//开散列
/

#include <vector>

//哈希节点
template<class V>
struct HashNode {
	V _data;
	HashNode<V>* _next;
	HashNode(const V& data)
		:_data(data)
		, _next(nullptr)
	{}
};


template<class K, class V, class KeyOfV>
class Hash {
	typedef HashNode<V> Node;
public:
	bool Insert(const V& val)
	{
		KeyOfV kofv;
		//负载因子等于1开始增容,避免大量的哈希冲突
		if (_tables.size() == 0 || _num >= _tables.size())
		{
			//1) 开新表
			std::vector<Node*> newTables;
			size_t newSz = _tables.size() == 0 ? 10 : 2 * _tables.size();
			newTables.resize(newSz, nullptr);

			//2) 将旧表中的数据重新映射
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				Node* curNode = _tables[i];
				while (curNode)
				{
					Node* next = curNode->_next;

					//重新计算位置,并头插到新链表
					size_t index = kofv(curNode->_data) % newSz;
					curNode->_next = newTables[index];
					newTables[index] = curNode;

					curNode = next;
				}
				_tables[i] = nullptr;
			}

			//3) 释放旧表
			_tables.swap(newTables);
		}

		//1.计算哈希位置
		size_t index = kofv(val) % _tables.size();

		//2.检查表中是否存在相同的值
		Node* node = _tables[index];
		while (node)
		{
			if (kofv(node->_data) == kofv(val))
				return false;
			node = node->_next;
		}

		//3.头插挂到链表中
		Node* cur = new HashNode<V>(val);
		cur->_next = _tables[index];
		_tables[index] = cur;
		++_num;

		return true;
	}

	Node* Find(const K& key)
	{
		KeyOfV kofv;
		if (key < 0)
			return nullptr;
		size_t index = key % _tables.size();

		Node* cur = _tables[index];
		while (cur)
		{
			if (kofv(cur->_data) == key)
				return cur;
			else
				cur = cur->_next;
		}
		return nullptr;
	}
	
	bool Erase(const K& key)
	{
		KeyOfV kofv;
		if (key < 0 )
			return false;
		size_t index = key % _tables.size();

		Node* cur = _tables[index];
		Node* prev = nullptr;
		while (cur)
		{
			if (kofv(cur->_data) == key)
			{
				if (prev == nullptr)
				{
					_tables[key] = cur->_next;
				}
				else
				{
					prev->_next = cur->_next;
				}
				--_num;
				delete cur;

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

		return false;
	}
private:
	std::vector<Node*> _tables;
	size_t _num = 0;//有效元素个数
};

测试代码:

#include <iostream>
#include "hash.hpp"

using namespace std;

template<class K>
struct setKeyOfValue
{
	const K& operator()(const K& key)
	{
		return key;
	}
};
void test()
{
	Hash<int, int, setKeyOfValue<int>> ht;
	ht.Insert(2);
	ht.Insert(3);
	ht.Insert(5);
	ht.Insert(15);
	ht.Insert(1);
	ht.Insert(25);
	ht.Insert(13);
	ht.Insert(7);
	ht.Insert(22);
	ht.Insert(23);
	ht.Insert(46);

	HashNode<int>* node = ht.Find(3);
	if (node)
		cout << "node: " << node->_data << endl;
	else
		cout << "node not exist" << endl;

	ht.Erase(3);
	ht.Erase(2);
}
int main()
{
	test();

	return 0;
}
开散列一些桶挂的数据很多,哈希冲突很严重,如何解决?

1)当一个桶中链的长度超过一定值时,将链表换成红黑树;

2)控制负载因子;

三.unordered_map&unordered_set模拟实现

hash.hpp

#pragma once

/
//开散列
/

#include <vector>
#include <string>

using std::vector;
using std::string;

//哈希节点
template<class V>
struct HashNode {
	V _data;
	HashNode<V>* _next;
	HashNode(const V& data)
		:_data(data)
		, _next(nullptr)
	{}
};

//前置声明
template<class K, class V, class KeyOfV, class Hash>
class HashTable;

//迭代器
template<class K,class V,class KeyOfV,class Hash>
struct HashTableIterator {
	typedef HashTableIterator<K, V, KeyOfV,Hash> Self;
	typedef HashNode<V> Node;
	typedef HashTable<K, V, KeyOfV, Hash> HT;

	Node* _node;
	HT* _ht;

	//构造函数传入哈希表指针,因为需要通过遍历哈希表完成迭代器的++
	HashTableIterator(Node* node,HT* ht)
		:_node(node)
		,_ht(ht)
	{}

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

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

	//前置++
	Self operator++()
	{
		//一个桶没走完,继续走
		if (_node->_next)
			_node = _node->_next;
		//一个桶走完了,走到下一个同进行遍历
		else
		{
			KeyOfV kofv;
			//计算哈希位置
			size_t i = _ht->HashFunc(kofv(_node->_data)) % _ht->_tables.size();
			++i;
			for (;i < _ht->_tables.size(); ++i)
			{
				Node* cur = _ht->_tables[i];
				if (cur)
				{
					_node = cur;
					return *this;
				}
			}

			//哈希桶全为空
			_node = nullptr;
			return *this;
		}
	}

	//后置++
	Self operator++(int tmp)
	{
		//一个桶没走完,继续走
		if (_node->_next)
			_node = _node->_next;
		//一个桶走完了,走到下一个同进行遍历
		else
		{
			KeyOfV kofv;
			//计算哈希位置
			size_t i = _ht->HashFunc(kofv(_node->_data)) % _ht->_tables.size();
			++i;
			for (; i < _ht->_tables.size(); ++i)
			{
				Node* cur = _ht->_tables[i];
				if (cur)
				{
					_node = cur;
					return *this;
				}
			}

			//哈希桶全为空
			_node = nullptr;
			return *this;
		}
	}

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

//将key转换为可以取模类型的默认仿函数
template<class K>
struct _Hash {
	const K& operator()(const K& key)
	{
		return key;
	}
};

//针对string为Key,进行模板特化
template<>
struct _Hash<string> {
	const size_t operator()(const string& key)
	{
		//BKDR算法
		int ret = 0;
		for (int i = 0; i < key.size(); ++i)
		{
			ret *= 131;
			ret += key[i];
		}

		return ret;
	}
};

template<class K, class V, class KeyOfV, class Hash>
class HashTable {
public:
	typedef HashNode<V> Node;
	typedef HashTableIterator<K, V, KeyOfV, Hash> iterator;
	friend struct HashTableIterator<K,V,KeyOfV,Hash>;
	iterator begin()
	{
		for (int i = 0; i < _tables.size(); ++i)
		{
			if (_tables[i])
			{
				return iterator(_tables[i],this);
			}
		}
		return end();
	}

	iterator end()
	{
		return iterator(nullptr,this);
	}

	~HashTable()
	{
		Clear();
	}

	void Clear()
	{
		for (int i = 0; i < _tables.size(); ++i)
		{
			Node* cur = _tables[i];
			while (cur)
			{
				Node* next = cur->_next;
				delete cur;
				cur = next;
			}
			_tables[i] = nullptr;
		}
	}
	
	//将key转换为可以取模的类型
	size_t HashFunc(const K& key)
	{
		Hash hash;
		return hash(key);
	}

	bool Insert(const V& val)
	{
		KeyOfV kofv;
		//负载因子等于1开始增容,避免大量的哈希冲突
		if (_tables.size() == 0 || _num >= _tables.size())
		{
			//1) 开新表
			std::vector<Node*> newTables;
			size_t newSz = _tables.size() == 0 ? 10 : 2 * _tables.size();
			newTables.resize(newSz, nullptr);

			//2) 将旧表中的数据重新映射
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				Node* curNode = _tables[i];
				while (curNode)
				{
					Node* next = curNode->_next;

					//重新计算位置,并头插到新链表
					size_t index = HashFunc(kofv(curNode->_data)) % newSz;
					curNode->_next = newTables[index];
					newTables[index] = curNode;

					curNode = next;
				}
				_tables[i] = nullptr;
			}

			//3) 释放旧表
			_tables.swap(newTables);
		}

		//1.计算哈希位置
		size_t index = HashFunc(kofv(val)) % _tables.size();

		//2.检查表中是否存在相同的值
		Node* node = _tables[index];
		while (node)
		{
			if (HashFunc(kofv(node->_data)) == HashFunc(kofv(val)))
				return false;
			node = node->_next;
		}

		//3.头插挂到链表中
		Node* cur = new HashNode<V>(val);
		cur->_next = _tables[index];
		_tables[index] = cur;
		++_num;

		return true;
	}

	Node* Find(const K& key)
	{
		KeyOfV kofv;
		if (key < 0)
			return nullptr;
		size_t index = HashFunc(key) % _tables.size();

		Node* cur = _tables[index];
		while (cur)
		{
			if (kofv(cur->_data) == key)
				return cur;
			else
				cur = cur->_next;
		}
		return nullptr;
	}
	
	bool Erase(const K& key)
	{
		KeyOfV kofv;
		if (key < 0 )
			return false;
		size_t index = HashFunc(key) % _tables.size();

		Node* cur = _tables[index];
		Node* prev = nullptr;
		while (cur)
		{
			if (HashFunc(kofv(cur->_data)) == HashFunc(key))
			{
				if (prev == nullptr)
				{
					_tables[key] = cur->_next;
				}
				else
				{
					prev->_next = cur->_next;
				}
				--_num;
				delete cur;

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

		return false;
	}
private:
	vector<Node*> _tables;
	size_t _num = 0;//有效元素个数
};

MyUnorderedMap.h

#pragma once

#include "hash.hpp"
#include <utility>

namespace cxp
{
	template<class K, class V, class Hash = _Hash<K>>
	class Unordered_map 
	{
	private:
		struct MapKOfV
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};

	public:
		//typename进行模板声明
		typedef typename HashTable<K, K, MapKOfV, Hash>::iterator iterator;

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

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

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

	private:
		HashTable<K, pair<K,V>, MapKOfV, Hash> _ht;
	};
}

MyUnorderedSet.h

#pragma once

#include "hash.hpp"
#include <utility>

namespace cxp
{
	template<class K, class Hash = _Hash<K>>
	class Unordered_Set
	{
	private:
		struct SetKOfV
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

	public:
		//typename进行模板声明
		typedef typename HashTable<K, K, SetKOfV, Hash>::iterator iterator;

		bool Insert(const K& key)
		{
			return _ht.Insert(key);
		}

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

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

	private:
		HashTable<K, K, SetKOfV, Hash> _ht;
	};
}

四.位图

1.位图思想

用每一位来存放某种状态的数据结构,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在。
位图查找的时间复杂度为O(1)

2.位图代码实现

#pragma once

#include <vector>
using std::vector;

namespace cxp
{
	class BitSet
	{
	public:
		//传入位图中要存储的元素个数
		BitSet(int Num)
			:_set((Num >> 5) + 1)
			,_num(0)
		{}

		//将key位的比特位置为1
		void Set(int key)
		{
			int index = key >> 5;	//一个整形有32位,计算在_set所在下标,下标从0开始
			int pos = key % 32;		//取余得到对应位,位从0开始
			
			// 1<<pos之后,pos位为1,将其和_set[index]相或就可以将该位添加进位图
			_set[index] |= (1 << pos);

			++_num;
		}

		//将key位的比特位置为0
		void Reset(int key)
		{
			int index = key >> 5;
			int pos = key % 32;
		
			//1 << pos之后,第pos位为1
			//将其取反之后,为1的位变为0,为0的位变为1
			//和_set[index]相与,则key对应的位变为0,其它位和1相与不变
			_set[index] &= ~(1 << pos);

			--_num;
		}

		//key位的比特位是否为1
		bool isExist(int key)
		{
			int index = key >> 5;
			int pos = key % 32;
			//return (_set[index] >> pos) & 1;
			return _set[index] & (1 << pos);
		}

		//位图中位个数
		int getSum()
		{
			return _set.size() * 32;
		}

		int validSize()
		{
			return _num;
		}

	private:
		vector<int> _set;
		int _num;	//位数
	};
}

3.位图优点

占用存储空间小,查询效率高(O(1));


五.布隆过滤器

1.思想

位图+哈希算法:
将一个字符串通过多个不同的哈希算法映射到一个位图多个位置,当这些位置全为1则说明这个字符串存在。

布隆过滤器判断字符串不存在是准确的,判断字符串存在有一定概率出错,因为其他字符串计算的位可能会该字符串造成干扰。
可以通过映射位置个数的增加来减少误判,哈希函数的个数的计算公式如下:

k:哈希函数的个数
m:需要的bit位大小
n:元素个数
k= m / n * ln2
m = k * n * 1.4

2.常见的字符串哈希算法


//BKDR哈希算法
size_t BKDRHash(const string& str)
{
	size_t hash = 0;
	for (int i = 0; i < str.size(); ++i)
	{
		// 也可以乘以31、131、1313、13131、131313..     
		hash = hash * 131 + str[i];
	}
	return hash;
}

//RS哈希算法
size_t RSHash(const string& str)
{
	size_t hash = 0;
	size_t magic = 63689;
	for(int i = 0; i < str.size(); ++i)
	{
		hash = hash * magic + str[i];
		magic *= 378551;
	}
	return hash;
}

//SDBM哈希算法
size_t SDBMHash(const string& str)
{
	size_t hash = 0;
	for(int i =0 ; i<str.size();++i)
	{
		hash = 65599 * hash + str[i];
		//hash = (size_t)ch + (hash << 6) + (hash << 16) - hash;  
	}
	return hash;
}

3.代码实现

bitSet.h

#pragma once

#include <vector>
using std::vector;

namespace cxp
{
	class BitSet
	{
	public:
		//传入位图中要存储的元素个数
		BitSet(int Num)
			:_set((Num >> 5) + 1)
			,_num(0)
		{}

		//将key位的比特位置为1
		void Set(int key)
		{
			int index = key >> 5;	//一个整形有32位,计算所在下标
			int pos = key % 32;		//取余得到对应位
			_set[index] |= (1 << pos);

			++_num;
		}

		//将key位的比特位置为0
		void Reset(int key)
		{
			int index = key >> 5;
			int pos = key % 32;
			_set[index] &= ~(1 << pos);

			--_num;
		}

		//key位的比特位是否为1
		bool isExist(int key)
		{
			int index = key >> 5;
			int pos = key % 32;
			return _set[index] & (1 << pos);
		}

		//位图中位个数
		int getSum()
		{
			return _set.size() * 32;
		}

		int validSize()
		{
			return _num;
		}

	private:
		vector<int> _set;
		int _num;	//位数
	};
}

BloomFilter.h

#pragma once

#include <string>
#include "bitSet.h"

using std::string;
using cxp::BitSet;

struct BKDRHash
{
	size_t operator()(const string& str)
	{
		size_t hash = 0;
		for (int i = 0; i < str.size(); ++i)
		{
			// 也可以乘以31、131、1313、13131、131313..     
			hash = hash * 131 + str[i];
		}
		return hash;
	}
};

struct RSHash
{
	size_t operator()(const string& str)
	{
		size_t hash = 0;
		size_t magic = 63689;
		for(int i = 0; i < str.size(); ++i)
		{
			hash = hash * magic + str[i];
			magic *= 378551;
		}
		return hash;
	}
};


struct SDBMHash
{
	size_t operator()(const string& str)
	{
		size_t hash = 0;
		for(int i =0 ; i<str.size();++i)
		{
			hash = 65599 * hash + str[i];
			//hash = (size_t)ch + (hash << 6) + (hash << 16) - hash;  
		}
		return hash;
	}
};

//查找的时间复杂度为O(k),k为哈希函数的个数
template<class T = string,
	class Hash1 = BKDRHash, 
	class Hash2 = RSHash ,
	class Hash3 = SDBMHash>
class BloomFilter
{
public:
	//num:元素个数
	/*
		k:哈希函数的个数
		m:需要的bit位大小
		n:元素个数
		k= m / n * ln2
		m = k * n * 1.4
		比特位个数=3*1.4*num,取5*num
	*/
	BloomFilter(size_t num)
		:_set(5 * num)
		,_size(5 * num)
	{}

	//使用多个bit位存储信息,标记是否存在该字符串
	void Set(const string& str)
	{
		//通过哈希函数计算的位置可能大于总的比特位个数,需要进行规整(%)
		size_t index1 = Hash1()(str) % _size;
		size_t index2 = Hash2()(str) % _size;
		size_t index3 = Hash3()(str) % _size;

		_set.Set(index1);
		_set.Set(index2);
		_set.Set(index3);
	}

	//判断是否存在有误差
	//判断不存在没有误差
	bool isExist(const string& str)
	{
		size_t index1 = Hash1()(str) % _size;;
		size_t index2 = Hash2()(str) % _size;
		size_t index3 = Hash3()(str) % _size;

		bool result = true;
		result = _set.isExist(index1);
		if (!result)
			return false;
		result = _set.isExist(index2);
		if (!result)
			return false;
		result = _set.isExist(index3);
		if (!result)
			return false;
		
		//字符串可能存在
		return true;
	}

	//布隆过滤器如果需要删除,则要给每个位加一个计数器,不能直接置为0
private:
	BitSet _set;
	size_t _size; //总的比特位个数
};

六.一致性哈希

大佬博客:https://www.zsythink.net/archives/1182

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值