哈希表详解

目录

1.unordered系列关联式容器

2.哈希

3.unordered_map和unordered_set哈希实现

4.代码总和


1.unordered系列关联式容器

1.1unordered系列

        c++98的STL里面提供了底层为红黑树的关联式容器(set和map); 但是在结点数目很多的时候查询的效率就低了, 所以c++11里STL又提供了四个新的unordered的关联式容器;分别是unordered_map 和 unordered_set ; 以及unordered_multimap和unordered_multiset.


2.哈希

2.1哈希概念

(1)通过某种函数(HashFunc)使得元素的存储位置和它的关键码之间可以一一对应的映射关系;那么查找这个元素的时候就会很快.

(2)哈希方法中使用的转换函数就是哈希(散列)函数,构造出来的结构是哈希表(散列表).

2.2哈希函数

Hash(key) = key % capacity;​​​​​

(1).直接定值法: 值和位置都有一个唯一的对应关系; but直接定值法会导致值的分布很散,空间浪费比较大.

(2)除留余数法: hashi = key % len; key和位置建立关系.

肯定会造成不同值映射到同一个地方,就是造成哈希碰撞; 就只能够将这个值放到映射后面的其他位置.

(3)哈希函数设计的越好,产生的哈希碰撞就越少, 但非哈希碰撞能够避免.

 2.3哈希冲突的解决

(1)闭散列(开放定值法): 当发生哈希冲突的时候, 如果哈希表没有满,那么就还有可以存放数据的位置,就将这个数据放到冲突位置的下一个空位置即可.

(2)开散列(拉链法): 首先对关键码用散列函数计算散列地址, 具有相同地址的关键码放到同一个子集合(), 各个桶里面的元素通过数据结构单链表进行连接, 链表的头节点放到哈希表中.

 2.4哈希表的实现

2.4.1闭散列(开放定值法)

(1)哈希表的结构:首先是pair类型的数据; 以及哈希表的状态(这个结构对后面插入删除有很大作用);.

(2)闭散列又分为线性探测二次探测;

线性探测:

        插入数据就是如果发生哈希冲突就将这个数据放到下一个没有存放过数据的空位置;

        删除数据:不能随便删除数据, 因为如果删除数据可能影响后面数据的查找.比如删除4的话,那么查找44就会受到影响.所以线性探测都是采用伪删除方法(就是状态改变为delete).

线性探测的优缺点:

        优点:简单实现;       

        缺点: 一旦产生哈希碰撞的数据很多, 就会数据的堆积,那么搜索的效率就会很低.

二次探测:

        当表的数据已经超过负载因子并且为质数; 那么可以将哈希碰撞的数据重新映射到新表中在将数据重新排放.

有数学验证:

        当表的长度为质数且表负载因子不超过0.5时,新的表项一定能够插入,而且任

何一个位置都不会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在
搜索时可以不考虑表装满的情况,但在插入时必须确保表的装载因子a不超过0.5,如果超出
必须考虑增容。
    enum status
	{
		EMPTY,
		EXIST,
		DELETE
	};

	template<class K, class V>
	struct HashDate
	{
		pair<K, V> _kv;
		status _s;
	};

(2)哈希表的插入(包括查找和扩容)

         查找:首先可以判断一下这个值是否存在,存在就无需插入,不存在再插入; 通过key;先找到hashi下标,如果这个值的状态不为空(可能为delete或者exist); 那么再判断是否为exist和值是否相等,相等就返回,不相等就继续小标查找.最后还没找到就返回nullptr.

        插入 扩容: 扩容这里采用负载因子来检查, 就是用来减少哈希冲突的, 但是负载因子如果太大,就很容易冲突加剧, 效率就低, 负载因子太小冲突降低但是空间利用率就低了.所以合适的负载因子很重要,一般设置为0.7.而且扩容不是采用在原本空间扩容而是在新空间扩,原本的数据也要挪过来.

          插入数据: 先使用哈希函数找到下标,然后判断状态为非exist,最后插入数据即可.

        HashDate<K, V>* Find(const K& key)
		{
			size_t hashi = _kv.first % _tables.size();
			while (_tables[hashi]._s != EMPTY)
			{
				if (_tables[hashi]._s == EXIST && _tables[hashi]._kv.first == key)
				{
					return &_tables[hashi];
				}
				hashi++;
				hashi %= _tables.size();
			}
			return nullptr;
		}

		bool Insert(const pair<K, V>& kv)
		{
			if (Find(kv.first))  //先判断这个值是否存在
				return false;


			//负载因子;
			if (_n * 10 / _tables.size() == 7)
			{
				size_t newSize = _tables.size() * 2;
				HashTable<K, V> newHT;
				newHT._tables.resize(newSize);
				
				for (size_t i = 0; i < _table.size(); i++)
				{
					if (_tables[i]._s == EXIST)
					{
						newHT.Insert(_table[i]._kv);
					}
				}
				_table.swap(newHT._tables);
			}
			
			//哈希函数
			size_t hashi = kv.first % _tables.size();
			while (_tables[hashi]._s == EXIST)//如果存在就跳过
			{
				hashi++;
				hashi %= _tables.size();
			}

			//空位置或者删除位置就填写数据
			_tables[hashi]._kv = kv;
			_tables[hashi]._s = EXIST;
			_n++;
			return true;
		}
2.4.2 哈希桶( Hash_bucket)

       由于线性探测(闭散列)如果哈希冲突很多,搜索效率降低,那么我们想到开散列(拉链法),在每个表里面存放头节点,然后挂单链表.


 (1)哈希桶的结构(HashNode):

HashNode里面包含着next指针以及数据data;

    template<class T>
	struct HashNode
	{
		HashNode<T>* next;
		T _data;

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

(2)哈希桶的指针(__HTIterator):

1. 首先就是因为有使用到HashTable, 所以提前进行前置声明.

2. 迭代器的构造有const类型和非const类型的.

3. operator++: 因为哈希表挂的单链表, 那么迭代到下一个链表可能是非空(后面还有结点);还有就是已经遍历完了一个桶,那么就要找下一个桶. 然后前面传hashi就是这个用的, 找到哈希下标, 如果结点挂链表就赋给node, 直到遍历完还没有就nullptr;

4. operator* : 返回数据, 返回类型Ref就是T&;

5. operator->: 返回地址, 返回类型Ptr就是T*;

6. operator!= : 结点不相同.

	template<class K, class T, class KeyOfT, class Hash>
	class HashTable;


	template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
	struct __HTIterator
	{
		typedef HashNode<T> Node;
		typedef __HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> self;
		Node* _node;
		const HashTable<K, T, KeyOfT, Hash>* _pht;

		size_t _hashi;
		__HTIterator(Node* node, HashTable<K, T, KeyOfT, Hash>* pht, size_t hashi)
			:_node(node)
			, _pht(pht)
			, _hashi(hashi)
		{}


		__HTIterator(Node* node,const HashTable<K, T, KeyOfT, Hash>* pht, size_t hashi)
			:_node(node)
			, _pht(pht)
			, _hashi(hashi)
		{}

		//返回值为迭代器iterator.
		self& operator++()
		{
			//桶里面还有下一个结点
			if (_node->next)
			{
				_node = _node->next;
			}
			else//当前桶已经走完那么就到下一个桶
			{
				_hashi++;
				while (_hashi < _pht->_tables.size())
				{
					if (_pht->_tables[_hashi])
					{
						_node = _pht->_tables[_hashi];
						break;
					}
					++_hashi;
				}

				if (_hashi == _pht->_tables.size())
				{
					_node = nullptr;
				}
			}
			return *this;
		}

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

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

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

(3)哈希桶(HashTable):

1 迭代器的begin(): 

        因为是单链表结构, 先遍历哈希表, 找到有链表连接的第一个结点然后返回iterator, 如果最后没找到就是nullptr.

2 迭代器的end(): 

        结构规定,返回nullptr且hashi为-1.

3.HashTable析构:

        将哈希表挂的链表都delete. 哈希表的结点置空.

	template<class K, class T, class KeyOfT, class Hash>
	class HashTable
	{

		typedef HashNode<T> Node;

		template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
		friend struct __HTIterator;

	public:

		typedef __HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;
		typedef __HTIterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;

		iterator begin()
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				if (_tables[i])
				{
					return iterator(_tables[i], this, i);
				}
			}
			return end();
		}

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


		const_iterator begin()const
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				if (_tables[i])
				{
					return iterator(_tables[i], this, i);
				}
			}
			return end();
		}

		const_iterator end()const
		{
			return iterator(nullptr, this, -1);
		}

		HashTable()
		{
			_tables.resize(10);//初始化容量
		}

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

(4)哈希桶插入数据

1. 首先进行数据的查找, 如果存在就不需要插入,反之则要;

2. hf和kot都是仿函数,处理数据的.

       pair<iterator, bool> Insert(const T& date)
		{
			Hash hf;
			KeyOfT kot;

			//查找数据在否,不在就创建,在就不需要了.
			iterator it = Find(kot(date));
			if (it != end())
				return make_pair(it, false);


			//负载因子为1;
			if (_n == _tables.size())
			{
				vector<Node*> newTables;
				newTables.resize(_tables.size() * 2, nullptr);

				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->next;

						//重新使用哈希函数进行映射.
						size_t hashi = hf(kot(cur->_data)) % newTables.size();
						
						cur->next = newTables[i];
						newTables[hashi] = cur;

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

			size_t hashi = hf(kot(date)) % _tables.size();
			Node* newnode = new Node(date);

			//头插
			newnode->next = _tables[hashi];
			_tables[hashi] = newnode;
			++_n;

			return make_pair(iterator(newnode, this, hashi), true);
		}

(5)哈希桶的查找:

        通过key进行查找, 首先通过hashi进行查找到在哈希表的那个位置, 然后再根据cur链表进行遍历查找到date和key相同的结点.

        iterator Find(const K& key)
		{
			Hash hf;
			KeyOfT kot;

			size_t hashi = hf(key) % _tables.size();
			Node* cur = _tables[hashi];

			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur, this, hashi);
				}
				cur = cur->next;
			}
			return end();
		}

(6)哈希桶的删除

删除也是先找到要删除的结点, 定义prev结点和cur结点, 如果找到且prev非空那么就是将prev置给cur->next; prev为空就将_tables[hashi] 置给cur->next; 没找到就一直遍历链表.

        bool Erase(const K& key)
		{
			Hash hf;
			KeyOfT kot;

			size_t hashi = hf(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_date) == key)
				{
					if (prev == nullptr)
					{
						_tables[hashi] = cur->next;
					}
					else
					{
						prev->next = cur->next;
					}
					delete cur;
					return true;
				}

				prev = cur;
				cur = cur->next;
			}
			return false;
		}

(7)计算桶的大小

bucketsize是计算每个桶的大小;

bucketlen是计算每个桶的大小并且给sum.

maxbucketlen是计算最大的桶的大小;

averagebucketlen是计算平均每个桶的大小.

		//计算桶的各种大小
		void some()
		{
			size_t bucketSize = 0;//一个桶的大小
			size_t maxbucketLen = 0;
			size_t sum = 0;
			double averagebucketlen = 0.0;

			for (size_t i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];
				if (cur)
				{
					bucketSize++;
				}

				size_t bucketLen = 0;
				while (cur)
				{
					bucketLen++;
					cur = cur->next;
				}
				sum += bucketLen;//计算全部桶的大小

				if (bucketLen > maxbucketLen)
				{
					maxbucketLen = bucketLen;
				}
			}
			averagebucketlen = (double)sum / (double)bucketSize;
			
			printf("all bucketSize:%d\n", _tables.size());
			printf("bucketSize:%d\n", bucketSize);
			printf("maxBucketLen:%d\n", maxBucketLen);
			printf("averageBucketLen:%lf\n\n", averageBucketLen);
		}

 

3.unordered_map和unordered_set哈希实现.

3.1unordered_map

(1) 直接从HashTable.h文件里面去拿begin, end, insert, find, erase...

(2) unordered_map的时候对应HashTable里面的函数模板的参数,尤其是仿函数.

#pragma once
#include"HashTable.h"

namespace study1
{

	template<class K, class V, class Hash = HashFunc<K>>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename Hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;

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

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

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

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

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

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

		iterator earse(const K& key)
		{
			return _ht.Erase(key);
		}


	private:
		Hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;
	};

	void test_map()
	{
		unordered_map<string, string> dict;
		dict.insert(make_pair("sort", ""));
		dict.insert(make_pair("string", ""));
		dict.insert(make_pair("insert", ""));

		for (auto& kv : dict)
		{
			kv.second += 'x';
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;

		string arr[] = { "香蕉", "哈密瓜", "香蕉", "西瓜", "桃子" };
		unordered_map<string, int> cout_map;

		for (auto& e : arr)
		{
			cout_map[e]++;
		}

		for (auto& kv : cout_map)
		{
			cout << kv.first << ":" <<  kv.second << endl;
		}
		cout << endl;
	}
}

3.2unordered_set

同理处理完unordered_map, unordered_set也是一样的处理,只是接口和仿函数修改一点.

#pragma once
#include"HashTable.h"

namespace study2
{
	template<class K, class Hash = HashFunc<K>>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename Hash_bucket::HashTable<K, K, SetKeyOfT, Hash>::iterator iterator;
		typedef typename Hash_bucket::HashTable<K, K, SetKeyOfT, Hash>::const_iterator const_iterator;
		
		iterator begin()
		{
			return _hf.begin();
		}

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

		const_iterator begin()const
		{
			return _hf.begin();
		}

		const_iterator end()const
		{
			return _hf.end();
		}

		pair<const_iterator, bool> insert(const K& key)
		{
			auto ret = _hf.Insert(key);
			return pair<const_iterator, bool>(const_iterator(ret.first._node, ret.first._pht, ret.first._hashi), ret.second);
		}

		iterator find(const K& key)
		{
			return _hf.Find(key);
		}

		iterator erase(const K& key)
		{
			return _hf.Erase(key);
		}
	private:
		Hash_bucket::HashTable<K, K, SetKeyOfT, Hash> _hf;
	};

	void test_set()
	{
		unordered_set<int> us;
		us.insert(5);
		us.insert(15);
		us.insert(52);
		us.insert(3);

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

		for (auto e : us)
		{
			cout << e << " ";
		}
		cout << endl;
	}
}

4.代码总和

4.1Hash_Bucket的代码:

#pragma once
#include<iostream>
#include<vector>
#include<string>
using namespace std;

//仿函数
template<class K>
struct HashFunc
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};

template<>
struct HashFunc<string>
{
	size_t operator()(const string& key)
	{
		//BKDR;是一种算法用来查找的方法,避免哈希碰撞的,比较高效.
		size_t hash = 0;
		for (auto e : key)
		{
			hash *= 31;
			hash += e;
		}
		return hash;
	}
};


namespace open_address
{
	enum status
	{
		EMPTY,
		EXIST,
		DELETE
	};

	template<class K, class V>
	struct HashDate
	{
		pair<K, V> _kv;
		status _s;
	};

	//丢到全局去,因为hash_bucket也要使用.
	//template<class K>
	//struct HashFunc
	//{
	//	//重载()
	//	size_t operator()(const K& key)
	//	{
	//		return (size_t)key;
	//	}
	//};

	//template<>
	//struct HashFunc<string>
	//{
	//	//重载()string
	//	size_t operator()(const string& key)
	//	{
	//		size_t hash = 0;
	//		for (auto e : key)
	//		{
	//			hash *= 31;
	//			hash += e;
	//		}
	//		cout << key << ":" << hash << endl;
	//		return hash;
	//	}
	//};

	template<class K, class V, class Hash = HashFunc<K>>
	class HashTable
	{
	public:

		HashTable()
		{
			_tables.resize(10);  //空间初始化先开10个空间;
		}

		bool Insert(const pair<K, V>& kv)
		{
			if (Find(kv.first))  //先判断这个值是否存在
				return false;


			//负载因子;用来扩容操作,扩容直接开新的空间,还要将原来的数据移动到新空间,不用担心原来表是否释放,自动调用析构函数
			if (_n * 10 / _tables.size() == 7)
			{
				size_t newSize = _tables.size() * 2;
				HashTable<K, V> newHT;
				newHT._tables.resize(newSize);
				
				for (size_t i = 0; i < _tables.size(); i++)
				{
					if (_tables[i]._s == EXIST)
					{
						newHT.Insert(_tables[i]._kv);
					}
				}
				_tables.swap(newHT._tables);
			}
			
			//HashFunc
			Hash hf;

			//哈希函数
			size_t hashi = hf(kv.first) % _tables.size();
			while (_tables[hashi]._s == EXIST)//如果存在就跳过
			{
				hashi++;
				hashi %= _tables.size();
			}

			//空位置或者删除位置就填写数据
			_tables[hashi]._kv = kv;
			_tables[hashi]._s = EXIST;
			_n++;

			return true;
		}

		HashDate<K, V>* Find(const K& key)
		{
			Hash hf;

			size_t hashi = hf(key) % _tables.size();
			while (_tables[hashi]._s != EMPTY)
			{
				if (_tables[hashi]._s == EXIST && _tables[hashi]._kv.first == key)
				{
					return &_tables[hashi];
				}
				hashi++;
				hashi %= _tables.size();
			}
			return NULL;
		}

		bool Erase(const K& key)
		{
			HashDate<K, V>* ret = Find(key);
			if (ret)
			{
				ret->_s = DELETE;
				_n--;
				return true;
			}
			else
			{
				return false;
			}
		}

		void print()
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				if (_tables[i]._s == EXIST)
				{
					printf("[%d]-> %d\n", i, _tables[i]._kv.first);
				}
				else if (_tables[i]._s == EMPTY)
				{
					printf("[%d]-> E\n", i);
				}
				else
				{
					printf("[%d]-> D\n", i);
				}
			}
			cout << endl;
		}

		size_t Size()const
		{
			return _n;
		}

		bool Empty() const
		{
			return _n == 0;
		}
	private:
		vector< HashDate<K, V>> _tables;
		size_t _n = 0;//存储关键字的个数;
	};

	void TsetHT1()
	{
		HashTable<int, int> ht;
		int a[] = { 4, 14, 24, 34, 5, 7, 1 };
		for (auto e : a)
		{
			ht.Insert(make_pair(e, e));
		}

		//ht.print();

		ht.Insert(make_pair(3, 3));
		ht.Insert(make_pair(3, 3));
		ht.Insert(make_pair(-3, -3));
		ht.print();

		ht.Erase(3);
		ht.print();

		if (ht.Find(3))
		{
			cout << "3存在" << endl;
		}
		else
		{
			cout << "3不存在" << endl;
		}

		cout << ht.Size() << endl;
	}
}


namespace Hash_bucket
{
	template<class T>
	struct HashNode
	{
		HashNode<T>* next;
		T _data;

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

	//前置声明
	template<class K, class T, class KeyOfT, class Hash>
	class HashTable;


	template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
	struct __HTIterator
	{
		typedef HashNode<T> Node;
		typedef __HTIterator<K, T, Ref, Ptr, KeyOfT, Hash> self;
		Node* _node;
		const HashTable<K, T, KeyOfT, Hash>* _pht;

		size_t _hashi;
		__HTIterator(Node* node, HashTable<K, T, KeyOfT, Hash>* pht, size_t hashi)
			:_node(node)
			, _pht(pht)
			, _hashi(hashi)
		{}


		__HTIterator(Node* node,const HashTable<K, T, KeyOfT, Hash>* pht, size_t hashi)
			:_node(node)
			, _pht(pht)
			, _hashi(hashi)
		{}

		//返回值为迭代器iterator.
		self& operator++()
		{
			//桶里面还有下一个结点
			if (_node->next)
			{
				_node = _node->next;
			}
			else//当前桶已经走完那么就到下一个桶
			{
				_hashi++;
				while (_hashi < _pht->_tables.size())
				{
					if (_pht->_tables[_hashi])
					{
						_node = _pht->_tables[_hashi];
						break;
					}
					++_hashi;
				}

				if (_hashi == _pht->_tables.size())
				{
					_node = nullptr;
				}
			}
			return *this;
		}

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

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

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

	template<class K, class T, class KeyOfT, class Hash>
	class HashTable
	{

		typedef HashNode<T> Node;

		template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
		friend struct __HTIterator;

	public:

		typedef __HTIterator<K, T, T&, T*, KeyOfT, Hash> iterator;
		typedef __HTIterator<K, T, const T&, const T*, KeyOfT, Hash> const_iterator;

		iterator begin()
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				if (_tables[i])
				{
					return iterator(_tables[i], this, i);
				}
			}
			return end();
		}

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


		const_iterator begin()const
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				if (_tables[i])
				{
					return iterator(_tables[i], this, i);
				}
			}
			return end();
		}

		const_iterator end()const
		{
			return iterator(nullptr, this, -1);
		}

		HashTable()
		{
			_tables.resize(10);//初始化容量
		}

		~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<iterator, bool> Insert(const T& date)
		{
			Hash hf;
			KeyOfT kot;

			//查找数据在否,不在就创建,在就不需要了.
			iterator it = Find(kot(date));
			if (it != end())
				return make_pair(it, false);


			//负载因子为1;
			if (_n == _tables.size())
			{
				vector<Node*> newTables;
				newTables.resize(_tables.size() * 2, nullptr);

				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->next;

						//重新使用哈希函数进行映射.
						size_t hashi = hf(kot(cur->_data)) % newTables.size();
						
						cur->next = newTables[i];
						newTables[hashi] = cur;

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

			size_t hashi = hf(kot(date)) % _tables.size();
			Node* newnode = new Node(date);

			//头插
			newnode->next = _tables[hashi];
			_tables[hashi] = newnode;
			++_n;

			return make_pair(iterator(newnode, this, hashi), true);
		}

		iterator Find(const K& key)
		{
			Hash hf;
			KeyOfT kot;

			size_t hashi = hf(key) % _tables.size();
			Node* cur = _tables[hashi];

			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur, this, hashi);
				}
				cur = cur->next;
			}
			return end();
		}

		bool Erase(const K& key)
		{
			Hash hf;
			KeyOfT kot;

			size_t hashi = hf(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_date) == key)
				{
					if (prev == nullptr)
					{
						_tables[hashi] = cur->next;
					}
					else
					{
						prev->next = cur->next;
					}
					delete cur;
					return true;
				}

				prev = cur;
				cur = cur->next;
			}
			return false;
		}

		//计算桶的各种大小
		void some()
		{
			size_t bucketSize = 0;//一个桶的大小
			size_t maxbucketLen = 0;
			size_t sum = 0;
			double averagebucketlen = 0.0;

			for (size_t i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];
				if (cur)
				{
					bucketSize++;
				}

				size_t bucketLen = 0;
				while (cur)
				{
					bucketLen++;
					cur = cur->next;
				}
				sum += bucketLen;//计算全部桶的大小

				if (bucketLen > maxbucketLen)
				{
					maxbucketLen = bucketLen;
				}
			}
			averagebucketlen = (double)sum / (double)bucketSize;
			
			printf("all bucketSize:%d\n", _tables.size());
			printf("bucketSize:%d\n", bucketSize);
			printf("maxBucketLen:%d\n", maxbucketLen);
			printf("averageBucketLen:%lf\n\n", averagebucketlen);
		}

	private:
	    vector<Node*> _tables;
		size_t _n = 0;
	};
}

4.2 unordered_set代码:

#pragma once
#include"HashTable.h"

namespace study2
{
	template<class K, class Hash = HashFunc<K>>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename Hash_bucket::HashTable<K, K, SetKeyOfT, Hash>::iterator iterator;
		typedef typename Hash_bucket::HashTable<K, K, SetKeyOfT, Hash>::const_iterator const_iterator;
		
		iterator begin()
		{
			return _hf.begin();
		}

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

		const_iterator begin()const
		{
			return _hf.begin();
		}

		const_iterator end()const
		{
			return _hf.end();
		}

		pair<const_iterator, bool> insert(const K& key)
		{
			auto ret = _hf.Insert(key);
			return pair<const_iterator, bool>(const_iterator(ret.first._node, ret.first._pht, ret.first._hashi), ret.second);
		}

		iterator find(const K& key)
		{
			return _hf.Find(key);
		}

		iterator erase(const K& key)
		{
			return _hf.Erase(key);
		}
	private:
		Hash_bucket::HashTable<K, K, SetKeyOfT, Hash> _hf;
	};

	void test_set()
	{
		unordered_set<int> us;
		us.insert(5);
		us.insert(15);
		us.insert(52);
		us.insert(3);

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

		for (auto e : us)
		{
			cout << e << " ";
		}
		cout << endl;
	}
}

4.3 unordered_map的代码:

#pragma once
#include"HashTable.h"

namespace study1
{

	template<class K, class V, class Hash = HashFunc<K>>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename Hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash>::iterator iterator;

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

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

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

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

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

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

		iterator earse(const K& key)
		{
			return _ht.Erase(key);
		}


	private:
		Hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT, Hash> _ht;
	};

	void test_map()
	{
		unordered_map<string, string> dict;
		dict.insert(make_pair("sort", ""));
		dict.insert(make_pair("string", ""));
		dict.insert(make_pair("insert", ""));

		for (auto& kv : dict)
		{
			kv.second += 'x';
			cout << kv.first << ":" << kv.second << endl;
		}
		cout << endl;

		string arr[] = { "香蕉", "哈密瓜", "香蕉", "西瓜", "桃子" };
		unordered_map<string, int> cout_map;

		for (auto& e : arr)
		{
			cout_map[e]++;
		}

		for (auto& kv : cout_map)
		{
			cout << kv.first << ":" <<  kv.second << endl;
		}
		cout << endl;
	}
}

后言: 

        博主在这里讨个三联.xdm麻烦给博主个一键三联, 多支持支持,博主更加有动力更新更好的文章.谢谢xdm.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/565562.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mysql基础14——视图

视图 视图是一种虚拟表 可以把一段查询语句作为视图存储在数据库中 需要的时候把视图看作一个表&#xff0c;对里面的数据进行查询 视图并没有真正存储数据 避免了数据存储过程中可能产生的冗余 提高了存储的效率 子查询 嵌套在另一个查询中的查询 派生表 如果在查询中…

Java中的super

package day33; ​ public class Person {public String name;public int age; ​public Person() {System.out.println("调用了父类的无参构造");} } ​ package day33; ​ public class teacher extends Person{public teacher() {System.out.println("调用了…

Springboot+Vue项目-基于Java+MySQL的在线文档管理系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

单机单实例部署Kafka及测试

文章目录 部署Docker 测试启动测试创建Topic生产者消费者 集成Manager的部署dockerfile效果 参考资料 在我们做和kafka开发相关的工作时&#xff0c;往往希望独立部署一套kafka测试环境。而kafka部署时&#xff0c;不能只是简单安装kafka自身组件&#xff0c;还要安装zookeeper…

你准备好迎接它了吗?英伟达CEO黄仁勋预言:人形机器人将成为未来主流

在近日举行的“CadenceLIVE 硅谷 2024”大会上&#xff0c;英伟达公司的首席执行官黄仁勋与大会主办方Cadence公司的CEO进行了一场富有深度的对话。在这场引人瞩目的交流中&#xff0c;黄仁勋大胆预测&#xff0c;未来人形机器人将成为主流&#xff0c;引领科技发展的新潮流。 …

n!的位数:面向对象的解法

Description:针对每个非负整数n&#xff0c;计算其n!的位数。 Input:输入数据中含有一些整数n(0≤n<10^7)。 Output:根据每个整数n&#xff0c;输出其n!的位数&#xff0c;每个数占独立一行。Output:重新排列01串的顺序。使得串按基本描述的方式排序。 #include <iostr…

MATLAB——M文件

M文件 MATLAB允许编写两种程序文件- 脚本−脚本文件是扩展名为.m的程序文件。在这些文件中&#xff0c;您编写了一系列要一起执行的命令。脚本不接受输入&#xff0c;也不返回任何输出。它们对工作区中的数据进行操作。 函数−函数文件也是扩展名为.m的程序文件。函数可以接…

数图可视化品类空间管理系统入编《零售门店数字化赋能专项报告(2024年)》

数图可视化品类空间管理系统荣幸入编中国连锁经营协会发布的 《零售门店数字化赋能专项报告&#xff08;2024年&#xff09;》&#xff0c;报告以零售门店为切入点&#xff0c;通过引入“5P”的技术框架及梳理业内配套最佳实践方案&#xff0c;理出一套科学的、完整的零售门店数…

未来城市可视化,A3D引擎支持,免费搭建全新一代数字孪生!

AMRT3D数字孪生引擎https://www.amrt3d.com/#/ 什么是未来城市&#xff1f;它是新型数字化理念的载体&#xff0c;以数字孪生与物理世界城市的融合为核心&#xff0c;通过数字孪生技术在数字空间实时构建城市&#xff0c;采用数据整合和分析预测来实时模拟、预测、控制整体城市…

使用gdal均匀筛选点矢量

使用gdal均匀筛选点矢量 作用&#xff1a; 通过计算各点之间的欧式距离&#xff0c;筛选出符合目标的、均匀发布在空间中的N个数据点。 效果示意图 运行环境 python 3.10 安装&#xff1a;tqdm、numpy和tqdm这三个库 完整代码 import numpy as np from osgeo import ogr,…

星域社区原版APP源码/社区交友App源码/动态圈子群聊php源码

简介 初始版本是由RuleAPP规则之树开发的&#xff0c;而星域社区则是在此基础上进行了二次开发和美化。作者花了近一年的时间来打磨它&#xff0c;现在即将推出Pro版。如果你只想免费使用的话&#xff0c;可以使用原始的RuleAPP版本。但是&#xff0c;如果你想要获得更好的美观…

python3爬虫笔记2

1 urlpare模块 urlparse模块主要用于处理URL字符串&#xff0c;它的核心功能是将URL拆分为多个组成部分&#xff0c;并允许你通过名字属性或索引来访问这些部分。通过调用urlparse模块的相关函数&#xff0c;你可以轻松解析URL&#xff0c;获取其不同组件的信息&#xff0c;如…

究竟该怎么寄快递才能安全无误的送到手中呢?

最近&#xff0c;小编上班了发现有同事在吐槽快递送到手中的时间很晚了&#xff0c;比预计的时间差了很多&#xff0c;并且产品也有不同程度的损坏。这就让我们很是恼火了&#xff0c;但是细细研究后才发现有一部分的原因竟然是我们的原因才导致的寄快递出现了很多纰漏。 首先…

sql(ctfhub)

一.整数型注入 输入1 输入2 输入2-1&#xff0c;回显为1的结果&#xff0c;说明是数字型&#xff0c;只有数字型才可加减 判断字段数为2 查询数据库 查表 查列 显示flag内容 二.字符型注入 输入1 输入2 输入2-1&#xff0c;说明为字符型&#xff0c;不是数字型 判断闭合方式为…

MyBatis 框架学习(I)

MyBatis 框架学习(I) 文章目录 MyBatis 框架学习(I)1. 介绍2. 准备&测试3. MyBatis 注解基础操作3.1 日志输出3.2 Insert 操作3.3 Delete 操作3.4 Update 操作3.5 Select 操作 总结 1. 介绍 之前我们学习过利用JDBC操作数据库进行项目开发&#xff0c;但我们发现它操作起来…

mysql基础19——日志

日志 mysql的日志种类非常多 通用查询日志 慢查询日志 错误日志 与时间有关联 二进制日志 中继日志 与主从服务器的同步有关 重做日志 回滚日志 与数据丢失有关 通用查询日志 记录了所有用户的连接开始时间和截至时间 以及给mysql服务器发送的所有指令 当数据异常时&…

Skill Check: Build an LLM Application using OCI Generative AI Service

Skill Check: Build an LLM Application using OCI Generative AI Service

Oracle21C 引入HR实例(linux)

1、下载资源 https://github.com/oracle-samples/db-sample-schemas点击code&#xff08;代码&#xff09;下载 2、上传Sql文件 解压之后将human_resources里的文件复制到demo\schema\目录&#xff08;具体目录前面的路径是你安装的路径&#xff09;下&#xff0c;如下图 3、…

Zoom会议频繁中断怎么办?解决策略来了

在远程办公盛行的今天&#xff0c;Zoom会议成为众多团队沟通协作的工具。然而&#xff0c;不少用户在使用过程中遇到Zoom会议频繁中断的困扰&#xff0c;这不仅影响工作效率&#xff0c;更可能阻碍信息的及时传递。那么&#xff0c;该如何应对这一问题&#xff0c;确保Zoom会议…

HarmonyOS开发实例:【图片编辑应用】

介绍 本篇Codelab通过动态设置元素样式的方式&#xff0c;实现几种常见的图片操作&#xff0c;包括裁剪、旋转、缩放和镜像。效果如图所示&#xff1a; 相关概念 [image组件]&#xff1a;图片组件&#xff0c;用来渲染展示图片。[div组件]&#xff1a;基础容器组件&#xff0…