Detailed explanation of c++---hash packaging

Hash bucket code

Through the previous study, you should be able to easily write the following code:

#pragma once
#include<iostream>
#include<vector>
using namespace std;
template<class K,class V>
struct HashNode
{
    
    
	HashNode(const pair<K,V>& kv)
		:_kv(kv)
		,_next(nullptr)
	{
    
    }
	pair<K, V> _kv;
	HashNode* _next;
};
template<class K>
struct HashFunc
{
    
    
	size_t operator()(const K& key)
	{
    
    
		return (size_t)key;
	}
};
template<>
struct HashFunc<string>
{
    
    
	size_t operator()(const string& s)
	{
    
    
		size_t res = 0;
		for (auto& ch : s)
		{
    
    
			res *= 131;
			res += ch;
		}
		return res;
	}
};
template<class K, class V, class Hash = HashFunc<K>>
class BucketTable
{
    
    
	typedef HashNode<K, V> Node;
public:
	BucketTable()
		:_n(0)
	{
    
    
		_tables.resize(__stl_next_prime(_tables.size()));
	}
	Node* Find(const K& key)
	{
    
    
		Hash hf;
		size_t pos = hf(key) % _tables.size();
		Node* cur = _tables[pos];
		while (cur)
		{
    
    
			if (cur->_kv.first == key)
			{
    
    
				return cur;
			}
			else
			{
    
    
				cur = cur->_next;
			}
		}
		return nullptr;
	}
	bool insert(const pair<K, V>& kv)
	{
    
    
		if (Find(kv.first))
		{
    
    
			return false;
		}
		if (_n / _tables.size() == 1)//平衡因子为1就更新
		{
    
    
			vector<Node*> newBH;
			newBH.resize(__stl_next_prime(_tables.size()));
			for (int i = 0; i < _tables.size(); i++)
			{
    
    
				Node* cur = _tables[i];
				while (cur)
				{
    
    
					Node* next = cur->_next;
					size_t pos = Hash()(cur->_kv.first);
					cur->_next = newBH[pos];
					newBH[pos] = cur;
					cur = next;
				}
				_tables[i] = nullptr;
			}
		}
		Hash hf;
		size_t pos = hf(kv.first) % _tables.size();
		Node* newnode = new HashNode<K,V>(kv);
		newnode->_next = _tables[pos];
		_tables[pos] = newnode;
		++_n;
		return true;
	}
	bool erase(const K& key)
	{
    
    
		HashFunc<K> HF;
		size_t pos = HF(key) % _tables.size();
		Node* cur = _tables[pos];
		Node* prev = cur;
		while (cur)
		{
    
    
			if (cur->_kv.first == key)
			{
    
    
				if (cur == _tables[pos])
				{
    
    
					_tables[pos] = cur->_next;
				}
				else
				{
    
    
					prev->_next = cur->_next;
				}
				delete cur;
				_n--;
				return true;
			}
			else
			{
    
    
				prev = cur;
				cur = cur->_next;
			}
		}
		return false;
	}
	~BucketTable()
	{
    
    
		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;
		}
	}
	inline unsigned long __stl_next_prime(unsigned long n)
	{
    
    
		static const int __stl_num_primes = 28;
		static const unsigned long __stl_prime_list[__stl_num_primes] =
		{
    
    
			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 (int i = 0; i < __stl_num_primes; ++i)
		{
    
    
			if (__stl_prime_list[i] > n)
			{
    
    
				return __stl_prime_list[i];
			}
		}
		return __stl_prime_list[__stl_num_primes - 1];
	}


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

Hash bucket modifications

First of all, we have to use a hash bucket to encapsulate the set and map. Based on the previous experience of the red-black tree, we know that although the set container only stores one kind of data, it still has to use two template parameters to process the data during encapsulation. Encapsulation, because the parameter of the insert function must be value, and the parameter of the find function is key, so in order to ensure the unity of encapsulation, two parameters are used to represent the internal data in the hash bucket. For the two parameters of the set container node template Each parameter is key. For the map container node template, the two parameters are key and value. Then the code describing the node class is as follows:

template<class T>
struct HashNode
{
    
    
	HashNode(const T& data)
		:_data(data)
		, _next(nullptr)
	{
    
    }
	T _data;//这里的_data既可以是map的内部数据也可以是set的内部数据
	HashNode* _next;
};

Since the node class has been modified, the insert function of the hash bucket must also be modified to convert the pair type of the original parameter into the second type T of the template, and because the second parameter may store a pair or a pair It is the key, but the find function and the insert function have always used the first parameter of the pair for comparison, so a functor parameter must be added here to obtain the key value of the data inside the container, and the insert function, find function, and erase function Data comparison is done by creating functor objects, so we have to make changes here one by one. The previous method of manually obtaining the first data through pair is modified to obtain the key through the functor. For example, it originally looked like this: , then cur->_kv.firstmodify After that, it should become like this: kot(cur->_data)Modify all the above hash bucket situations to complete the first step. Next, implement the unorderedmap and unorderedset classes. First of all, these two classes are based on hash bucket. It is implemented by hash bucket, so both classes have a private variable of hash bucket. Secondly, when we use hash table, we can pass different functors, so the classes here also have to add a functor template parameter. And because the hash bucket needs a functor to obtain the key in the data, a functor must be created in the outer set and map containers and passed to the internal hash bucket. Then the code here is as follows:

template<class K,class HashFunc=HashFunc<K>>
class UnorderedSet
{
    
    
public:
	struct KeyOfT
	{
    
    
		const K& operator()(const K& key)
		{
    
    
			return key;
		}
	};
private:
	BucketTable<K, K, KeyOfT, HashFunc> _ht;
};

#include"HashBucket.h"
template<class K, class T,class HashFunc = HashFunc<K>>
class UnorderedMap
{
    
    
public:
	struct KeyOfT
	{
    
    
		const K& operator()(const pair<K,T>& key)
		{
    
    
			return key.first;
		}
	};
private:
	BucketTable<K, pair<const K,T>, KeyOfT, HashFunc> _ht;
};

After the basic framework is implemented, the internal find function, insert function, and erase function can be completed. The implementation principle here is to implement the function by calling the internal function of the hash bucket, so the code here is as follows:

template<class K,class HashFunc=HashFunc<K>>
class UnorderedSet
{
    
    
public:
	struct KeyOfT
	{
    
    
		const K& operator()(const K& key)
		{
    
    
			return key;
		}
	};
	bool insert(const K& key)
	{
    
    
		return _ht.insert(key);
	}
	bool erase(const K& key)
	{
    
    
		return _ht.erase(key);
	}
	HashNode<K>* find(const K& key)
	{
    
    
		return _ht.Find(key);
	}
private:
	BucketTable<K, K, KeyOfT, HashFunc> _ht;
};

template<class K, class T,class HashFunc = HashFunc<K>>
class UnorderedMap
{
    
    
public:
	struct KeyOfT
	{
    
    
		const K& operator()(const pair<K,T>& key)
		{
    
    
			return key.first;
		}
	};
	bool insert(const pair<K,T>& data)
	{
    
    
		return _ht.insert(data);
	}
	bool erase(const K& key)
	{
    
    
		return _ht.erase(key);
	}
	HashNode<K>* find(const K& key)
	{
    
    
		return _ht.Find(key);
	}
private:
	BucketTable<K, pair<const K,T>, KeyOfT, HashFunc> _ht;
};

With these basic functions, we can use the following code to simply check whether the above code is implemented correctly, then the checking code here is as follows:

int main()
{
    
    
	UnorderedSet<int> set;
	int a[] = {
    
     18, 8, 7, 27, 57, 3, 38, 18 };
	for (auto e : a)
	{
    
    
		set.insert(e);
	}
	set.insert(17);
	set.insert(5);
	if (set.find(7)) {
    
     cout << "存在" << endl; }
	else {
    
     cout << "不存在" << endl; }
	set.erase(7);
	if (set.find(7)) {
    
     cout << "存在" << endl; }
	else {
    
     cout << "不存在" << endl; }
	return 0;
}

The running result of this code is as follows:
Insert image description here
there is no problem with the running result, then this shows that there is no problem with the code we implemented above, and then we can continue to implement the iterators of map and set.

Iterator implementation

Each node of the hash bucket is a linked list, so the traversal of the iterator starts from the first position of the vector, first finds the position where the first node is not empty, and then traverses the linked list from top to bottom. For each node, if after the linked list traversal is completed, it will continue to search for the next node in the vector that is not empty after the current position, then this is the implementation idea of ​​the iterator. If you want to implement the ++ of the iterator, you must first create a For the class of iterator,
first of all, the iterator must be a template, and the template parameters are four, then the code here is as follows:

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

};

First of all, the ++ function must record the HashNode node, so a node pointer must be created in this class. Secondly, the return value of the ++ function is the iterator itself, so you can use typedef to simplify the class here, because when ++ You need to use a vector container to find the position of the next linked list, so you have to pass the hash bucket class here. Because the vector container is difficult to pass, the hash bucket is passed here. Then the code here is as follows:

template<class K, class T, class KeyOfT, class Hash>
struct _HashIterator
{
    
    
	typedef HashNode<T> Node;
	typedef _HashIterator<K, T, KeyOfT, Hash> Self;
	typedef BucketTable<K, T, KeyOfT, Hash> Bt;
	Node* _node;
	Bt* _bt;
	Self& operatpr++()
	{
    
    
	}	
};

Then we will implement the operator overloading of ++. First, judge whether the next node of the current node is empty. If it is not empty, it means that there is still data below. We will change the pointer of the node to point to the next node. If If the next point of the node is empty, we have to find the location of a linked list. First, create a keyoft functor to find the key in the array, then use the hashfunc function to find the value of the data conversion, and finally touch the size of the container. You can determine the position of the current element, and then you can create a loop to find the next non-empty position, then the code here is as follows:

Self& operator++()
{
    
    
	if (_node->next)
	{
    
    
		//当前节点的下一个节点为空
		_node = _node->next;
	}
	else
	{
    
    
		KeyOfT kot;
		Hash hf;
		size_t hashi = hf(kot(_node->_data)) % _bt->_tables.size();
		++hashi;
		while (hashi < _bt->_tables.size())
		{
    
    
			if (_bt->_tables[hashi])
			{
    
    
				_node = _bt->_tables[hashi];
				break;
			}
			else
			{
    
    
				hashi++;
			}
		}

	}
}	

If the loop ends, there will be two situations here. The first is that the current element is the last element and then ++ will have no elements. The second situation is that the next element is found. For the first situation, you can use hash It is equal to the current _tables.size() to judge. If the first case is not true, it means that the current situation is the second case. Here we use the _node of the iterator as a null pointer to represent that we have reached the last position of the element , then the code here is as follows:

Self& operator++()
{
    
    
	if (_node->_next)
	{
    
    
		//当前节点的下一个节点不为空
		_node = _node->_next;
	}
	else
	{
    
    
		KeyOfT kot;
		Hash hf;
		size_t hashi = hf(kot(_node->_data)) % _bt->_tables.size();
		++hashi;
		while (hashi < _bt->_tables.size())
		{
    
    
			if (_bt->_tables[hashi])
			{
    
    
				_node = _bt->_tables[hashi];
				break;
			}
			else
			{
    
    
				hashi++;
			}
		}
		if (hashi == _bt->_tables.size())
		{
    
    
			_node = nullptr;
		}
	}
	return *this;
}

After completing this implementation, you can simply implement the operator overloading of operators * and ->. Then the code here is as follows:

T& operator *()
{
    
    
	return _node->_data;
}
T* operator ->()
{
    
    
	return &_node->_data;
}
bool operator !=(const Self& s) const
{
    
    
	return _node != s._node;
}

After these functions are implemented, the constructor of the iterator can be implemented. The constructor is to initialize the node pointer and the hash bucket pointer. Then the code here is as follows:

_HashIterator(Node* node,Bt* bt)
	:_node(node)
	,_bt(bt)
{
    
    }

After completing this function, we can return to the hash bucket class and then implement the begin function and end function corresponding to the iterator. The begin function here returns the address of the first linked list in the vector container that is not empty. Then here You can create a for loop to continuously search, then the code here is as follows:

typedef _HashIterator<K, T, KeyOfT, Hash> iterator;
iterator begin()
{
    
    
	for (int i = 0; i < _tables.size(); i++)
	{
    
    
		if (_tables[i])
		{
    
    
			return iterator(_tables[i], this);
		}
	}
}

The implementation principle of the end function is also similar, but we use nullptr to indicate that there are no elements, so the code here is implemented as follows:

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

But there is a problem with our implementation. After running the code, we can see that there are many errors reported: the
Insert image description here
reason is that the iterator we implemented here needs a hash bucket, but the implementation of the hash bucket needs an iterator. Therefore, there is a self-contradictory mutual reference here, so here we need to use the method of pre-declaring the hash bucket class to solve the problem. The code here is as follows:

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

template<class K, class T, class KeyOfT, class Hash>
struct _HashIterator
{
    
    
	//....
}

After implementing the above code, you can apply this iterator to the set

typedef typename BucketTable<K, K, KeyOfT, HashFunc>::iterator iterator;
iterator begin()
{
    
    
	return _ht.begin();
}
iterator end()
{
    
    
	return _ht.end();
}

After completing this, you can use the following code to test the correctness of the above:

void test2()
{
    
    
	UnorderedSet<int> us;
	us.insert(1);
	us.insert(2);
	us.insert(3);
	us.insert(4);
	us.insert(5);
	UnorderedSet<int>::iterator it1 = us.begin();
	while (it1 != us.end())
	{
    
    
		cout << *it1 << endl;
		it1++;
	}
}

Run this code and you will find that there seems to be a problem with the above implementation:
Insert image description here
the reason for this problem is that
Insert image description here
_tables here is a private member variable, and the iterator class cannot access it normally, so the solution here is to use friendly To solve it, the code here is as follows:

template<class K, class T, class KeyOfT,class Hash >
class BucketTable
{
    
    
	template<class K, class T, class KeyOfT, class Hash>
	friend struct _HashIterator;
	//....
}

After completing this, you can run the above code normally, and the results of running here are as follows: After
Insert image description here
implementing the iterator, you can modify the return value of the find function and the return value of the insert function. Insert returns pair, find What is returned is the iterator. After modification here, the overloading of square brackets can be realized. The overloading of square brackets is to first call the insert function, then receive its return value of pair type, and finally return the first and second element of the pair element. , which is Value, then the code here is as follows:

template<class K, class T,class HashFunc = HashFunc<K>>
class UnorderedMap
{
    
    
public:
	
	struct KeyOfT
	{
    
    
		const K& operator()(const pair<K,T>& key)
		{
    
    
			return key.first;
		}
	};
	typedef typename BucketTable<K, pair<const K,T>, KeyOfT, HashFunc>::iterator iterator;
	iterator begin()
	{
    
    
		return _ht.begin();
	}
	iterator end()
	{
    
    
		return _ht.end();
	}
	pair<iterator,bool> insert(const pair<K,T>& data)
	{
    
    
		return _ht.insert(data);
	}
	bool erase(const K& key)
	{
    
    
		return _ht.erase(key);
	}
	iterator find(const K& key)
	{
    
    
		return _ht.Find(key);
	}
	T& operator[](const K& key)
	{
    
    
		pair<iterator, bool> ret = _ht.insert(make_pair(key, T()));
		return ret.first->second;
	}
private:
	BucketTable<K, pair<const K,T>, KeyOfT, HashFunc> _ht;
};

You can use the following code to test it:

void test3()
{
    
    
	string arr[] = {
    
     "苹果", "西瓜", "香蕉", "草莓", "苹果", "西瓜", 
		"苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
		
	UnorderedMap<string, int> countMap;
	for (auto& e : arr)
	{
    
    
		countMap[e]++;
	}
	for (const auto& kv : countMap)
	{
    
    
		cout << kv.first << ":" << kv.second << endl;
	}
}

The running results of the code are as follows:
Insert image description here
The running results of the code are in line with our expectations, so let's take a look at how the const iterator is implemented.

const iterator

Eradicating the previous experience, we know that const iterators and ordinary iterators can be integrated by adding template parameters. Then first modify the template parameters of the iterator and modify the dereferences of the two operators, then You can get the following code:

template<class K, class T,class Ref,class Ptr, class KeyOfT, class Hash>
struct _HashIterator
{
    
    
	typedef HashNode<T> Node;
	typedef _HashIterator<K, T, Ref,Ptr,KeyOfT, Hash> Self;
	typedef BucketTable<K, T, KeyOfT, Hash> Bt;
		Node* _node;
	Bt* _bt;
	_HashIterator(Node* node,Bt* bt)
		:_node(node)
		,_bt(bt)
	{
    
    }
	Self& operator++()
	{
    
    //....return *this;}	
	Ref operator *()
	{
    
    return _node->_data;}
	Ptr operator ->()
	{
    
    return &_node->_data;}
	bool operator !=(const Self& s) const
	{
    
    return _node != s._node;}
};

Then modify the code in the hash bucket class, create a const iterator, and add the const version of the begin function and end function, then the code here is roughly as follows:

template<class K, class T, class KeyOfT,class Hash >
class BucketTable
{
    
    
public:
	typedef HashNode<T> Node;
	template<class K, class T, class Ref, class Ptr, class KeyOfT, class Hash>
	friend struct _HashIterator;
	BucketTable()
		:_n(0)
	{
    
    
		_tables.resize(__stl_next_prime(_tables.size()));
	}
	typedef _HashIterator<K, T, T&, T*, KeyOfT, Hash> iterator;
	typedef _HashIterator<K, T,const T&,const T* ,KeyOfT, Hash> const_iterator;
	iterator begin()
	{
    
    //...}
	iterator end()
	{
    
    //...}
	const_iterator begin() const
	{
    
    //...}
	const_iterator end() const 
	{
    
    //...}
	iterator Find(const K& key)
	{
    
    //...}
	bool erase(const K& key)
	{
    
    //...}
	~BucketTable()
	{
    
    //...}
	inline unsigned long __stl_next_prime(unsigned long n)
	{
    
    //...}
private:
	vector<Node*> _tables;
	size_t _n;
};

Then you can use the following code to test it:

void test4(const UnorderedSet<int>& us)
{
    
    
	UnorderedSet<int>::const_iterator it = us.begin();
	while (it != us.end())
	{
    
    
		cout << *it << endl;
		it++;
	}
	cout << endl;
}
int main()
{
    
    
	UnorderedSet<int> us;
	us.insert(1);
	us.insert(2);
	us.insert(3);
	us.insert(4);
	test4(us);
	return 0;
}

The result of running this code is as follows:
Insert image description here

We found that there was a problem with the above implementation method. The problem was in the constructor. We used the begin function to create a const iterator. Because us is modified by const, the begin called here is a const type begin.

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

The begin function here is to construct a const iterator by finding the first node that is not empty, because this is a const version, so the _tables[i] used here is also a const version, _tables is a vector container and is also a const version, Here, the return value of the square brackets is called to construct, and the square bracket overload of vector is also divided into two versions:
Insert image description here
so what is returned here is the const version, and the iterator is constructed using the normal version of the pointer. :

_HashIterator(Node* node,Bt* bt)
		:_node(node)
		,_bt(bt)
	{
    
    }

So the creation of the iterator object here fails. If you add const to the node pointer here, it still cannot solve the problem, and it will report the following error:

_HashIterator( const Node* node,const Bt* bt)
		:_node(node)
		,_bt(bt)
	{
    
    }

Insert image description here

Because the permissions here are amplified, problems arise. What if we say that _node also becomes a const attribute? For example:

const Node* _node;
const Bt* _bt;
_HashIterator(const Node* node,Bt* bt)
	:_node(node)
	,_bt(bt)
{
    
    }

If this is the case, can ordinary iterators still modify the contents? Isn't it possible? So the solution adopted here is to define const iterators and ordinary iterators separately, for example, the following code:

template<class K, class T, class KeyOfT, class Hash>
struct const_HashIterator
{
    
    
	typedef HashNode<T> Node;
	typedef const_HashIterator<K, T, KeyOfT, Hash> Self;
	typedef BucketTable<K, T, KeyOfT, Hash> Bt;
	const Node* _node;
	const Bt* _bt;
	const_HashIterator(const Node* node, const Bt* bt)
		:_node(node)
		, _bt(bt)
	{
    
    }
	Self& operator++()
	{
    
    }
	const T& operator *()
	{
    
    }
	const T* operator ->()
	{
    
    }
	bool operator !=(const Self& s) const
	{
    
    }
};

In this way, there will be no problems when we run the above code:
Insert image description here
So the content of this article is complete here, all the codes are as follows:

//哈希桶文件的代码
#pragma once
#include<iostream>
#include<vector>
using namespace std;
template< class T>
struct HashNode
{
    
    
	HashNode(const T& data)
		:_data(data)
		, _next(nullptr)
	{
    
    }
	T _data;
	HashNode<T>* _next;
};
template<class K>
struct HashFunc
{
    
    
	size_t operator()(const K& key)
	{
    
    
		return (size_t)key;
	}
};
template<>
struct HashFunc<string>
{
    
    
	size_t operator()(const string& s)
	{
    
    
		size_t res = 0;
		for (auto& ch : s)
		{
    
    
			res *= 131;
			res += ch;
		}
		return res;
	}
};
template<class K, class T, class KeyOfT, class Hash >
class BucketTable;//前置声明

template<class K, class T, class KeyOfT, class Hash>
struct const_HashIterator
{
    
    
	typedef HashNode<T> Node;
	typedef const_HashIterator<K, T, KeyOfT, Hash> Self;
	typedef BucketTable<K, T, KeyOfT, Hash> Bt;
	const Node* _node;
	const Bt* _bt;
	const_HashIterator(const Node* node, const Bt* bt)
		:_node(node)
		, _bt(bt)
	{
    
    }
	Self& operator++()
	{
    
    
		if (_node->_next)
		{
    
    
			//当前节点的下一个节点为空
			_node = _node->_next;
		}
		else
		{
    
    
			KeyOfT kot;
			Hash hf;
			size_t hashi = hf(kot(_node->_data)) % _bt->_tables.size();
			++hashi;
			while (hashi < _bt->_tables.size())
			{
    
    
				if (_bt->_tables[hashi])
				{
    
    
					_node = _bt->_tables[hashi];
					break;
				}
				else
				{
    
    
					hashi++;
				}
			}
			if (hashi == _bt->_tables.size())
			{
    
    
				_node = nullptr;
			}
		}
		return *this;
	}
	const T& operator *()
	{
    
    
		return _node->_data;
	}
	const T* operator ->()
	{
    
    
		return &_node->_data;
	}
	bool operator !=(const Self& s) const
	{
    
    
		return _node != s._node;
	}
};

template<class K, class T, class KeyOfT, class Hash>
struct _HashIterator
{
    
    
	typedef HashNode<T> Node;
	typedef _HashIterator<K, T,KeyOfT, Hash> Self;
	typedef BucketTable<K, T, KeyOfT, Hash> Bt;
	Node* _node;
	Bt* _bt;
	_HashIterator( Node* node,Bt* bt)
		:_node(node)
		,_bt(bt)
	{
    
    }
	Self& operator++()
	{
    
    
		if (_node->_next)
		{
    
    
			//当前节点的下一个节点为空
			_node = _node->_next;
		}
		else
		{
    
    
			KeyOfT kot;
			Hash hf;
			size_t hashi = hf(kot(_node->_data)) % _bt->_tables.size();
			++hashi;
			while (hashi < _bt->_tables.size())
			{
    
    
				if (_bt->_tables[hashi])
				{
    
    
					_node = _bt->_tables[hashi];
					break;
				}
				else
				{
    
    
					hashi++;
				}
			}
			if (hashi == _bt->_tables.size())
			{
    
    
				_node = nullptr;
			}
		}
		return *this;
	}	
	T& operator *()
	{
    
    
		return _node->_data;
	}
	T* operator ->()
	{
    
    
		return &_node->_data;
	}
	bool operator !=(const Self& s) const
	{
    
    
		return _node != s._node;
	}
};
template<class K, class T, class KeyOfT,class Hash >
class BucketTable
{
    
    
public:	
	typedef HashNode<T> Node;
	template<class K, class T, class KeyOfT, class Hash>
	friend struct _HashIterator;

	template<class K, class T, class KeyOfT, class Hash>
	friend struct const_HashIterator;
	BucketTable()
		:_n(0)
	{
    
    
		_tables.resize(__stl_next_prime(_tables.size()));
	}
	typedef _HashIterator<K, T, KeyOfT, Hash> iterator;
	typedef const_HashIterator<K, T,KeyOfT, Hash> const_iterator;
	iterator begin()
	{
    
    
		for (int i = 0; i < _tables.size(); i++)
		{
    
    
			if (_tables[i])
			{
    
    
				return iterator(_tables[i], this);
			}
		}
	}
	iterator end()
	{
    
    
		return iterator(nullptr, this);
	}
	const_iterator begin() const
	{
    
    
		for (int i = 0; i < _tables.size(); i++)
		{
    
    
			if (_tables[i])
			{
    
    
				return const_iterator(_tables[i], this);
			}
		}
	}
	const_iterator end() const 
	{
    
    
		const_iterator it1(nullptr, this);
		return it1;
	}
	iterator Find(const K& key)
	{
    
    
		Hash hf;
		size_t pos = hf(key) % _tables.size();
		KeyOfT kot;
		Node* cur = _tables[pos];
		while (cur)
		{
    
    
			if (kot(cur->_data) == key)
			{
    
    
				return iterator(cur,this);
			}
			else
			{
    
    
				cur = cur->_next;
			}
		}
		return iterator(nullptr,this);
	}
	pair<iterator,bool> insert(const T& data)
	{
    
    
		KeyOfT kot;
		iterator it = Find(kot(data));
		if (it!=end())
		{
    
    
			return make_pair(it,false);
		}
		if (_n / _tables.size() == 1)//平衡因子为1就更新
		{
    
    
			vector<Node*> newBH;
			newBH.resize(__stl_next_prime(_tables.size()));
			for (int i = 0; i < _tables.size(); i++)
			{
    
    
				Node* cur = _tables[i];
				while (cur)
				{
    
    
					Node* next = cur->_next;
					size_t pos = Hash()(kot(cur->_data));
					cur->_next = newBH[pos];
					newBH[pos] = cur;
					cur = next;
				}
				_tables[i] = nullptr;
			}
		}
		Hash hf;
		size_t pos = hf(kot(data)) % _tables.size();
		Node* newnode = new HashNode< T>(data);
		newnode->_next = _tables[pos];
		_tables[pos] = newnode;
		++_n;
		return make_pair(iterator(newnode,this),true) ;
	}
	bool erase(const K& key)
	{
    
    
		HashFunc<K> HF;
		KeyOfT kot;
		size_t pos = HF(key) % _tables.size();
		Node* cur = _tables[pos];
		Node* prev = cur;
		while (cur)
		{
    
    
			if (kot(cur->_data) == key)
			{
    
    
				if (cur == _tables[pos])
				{
    
    
					_tables[pos] = cur->_next;
				}
				else
				{
    
    
					prev->_next = cur->_next;
				}
				delete cur;
				_n--;
				return true;
			}
			else
			{
    
    
				prev = cur;
				cur = cur->_next;
			}
		}
		return false;
	}
	~BucketTable()
	{
    
    
		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;
		}
	}
	inline unsigned long __stl_next_prime(unsigned long n)
	{
    
    
		static const int __stl_num_primes = 28;
		static const unsigned long __stl_prime_list[__stl_num_primes] =
		{
    
    
			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 (int i = 0; i < __stl_num_primes; ++i)
		{
    
    
			if (__stl_prime_list[i] > n)
			{
    
    
				return __stl_prime_list[i];
			}
		}
		return __stl_prime_list[__stl_num_primes - 1];
	}


private:
	vector<Node*> _tables;
	size_t _n;
};
//封装的set的代码
#include"HashBucket.h"

template<class K,class HashFunc=HashFunc<K>>
class UnorderedSet
{
    
    
public:
	
	struct SetOfT
	{
    
    
		const K& operator()(const K& key)
		{
    
    
			return key;
		}
	};
	typedef typename BucketTable<K, K, SetOfT, HashFunc>::iterator iterator;
	typedef typename BucketTable<K, K, SetOfT, HashFunc>::const_iterator const_iterator;
	iterator begin()
	{
    
    
		return _ht.begin();
	}
	iterator end()
	{
    
    
		return _ht.end();
	}
	const_iterator begin() const 
	{
    
    
		return _ht.begin();
	}
	const_iterator end() const
	{
    
    
		return _ht.end();
	}
	pair<iterator,bool> insert(const K& key)
	{
    
    
		return _ht.insert(key);
	}
	bool erase(const K& key)
	{
    
    
		return _ht.erase(key);
	}
	iterator find(const K& key)
	{
    
    
		return _ht.Find(key);
	}
private:
	BucketTable<K, K, SetOfT, HashFunc> _ht;
};
//封装的map的代码
#include"HashBucket.h"
template<class K, class T,class HashFunc = HashFunc<K>>
class UnorderMap
{
    
    
public:
	
	struct MapOfT
	{
    
    
		const K& operator()(const pair<const K,T>& key)
		{
    
    
			return key.first;
		}
	};
	typedef typename BucketTable<K, pair<const K,T>, MapOfT, HashFunc>::iterator iterator;
	typedef typename BucketTable<K, K, MapOfT, HashFunc>::const_iterator const_iterator;
	iterator begin()
	{
    
    
		return _ht.begin();
	}
	iterator end()
	{
    
    
		return _ht.end();
	}
	const_iterator begin() const
	{
    
    
		return _ht.begin();
	}
	const_iterator end() const
	{
    
    
		return _ht.end();
	}
	pair<iterator,bool> insert(const pair< const K,T>& data)
	{
    
    
		return _ht.insert(data);
	}
	bool erase(const K& key)
	{
    
    
		return _ht.erase(key);
	}
	iterator find(const K& key)
	{
    
    
		return _ht.Find(key);
	}
	T& operator[](const K& key)
	{
    
    
		pair<iterator, bool> ret = _ht.insert(make_pair(key, T()));
		return ret.first->second;
	}
private:
	BucketTable<K, pair<const K,T>, MapOfT, HashFunc> _ht;
};

Guess you like

Origin blog.csdn.net/qq_68695298/article/details/131464716