C ++ using the hash realization map, set Detailed

**

Because I have written before that the electronic notes on everything I learned as a summary, so basically the text, had wanted to send the whole form of blog and found the whole hair into a blog takes time, and has been issued blog quality is not very good, and by sending blog learned also become less, so be prepared to issue notes first, then follow them into the form of a blog, at least for two days to change a blog, I feel okay can be summarized my first concern, the follow-up will be made into blog form, the content will be more perfect

**
achieve map and set the hash version:
they are used to achieve the hash bucket to do, logic is not difficult mainly structure
with an array of lanyards table, for example, first of all it as a container, a hash bucket to achieve four containers, so is the template to do, because most of the parameters map type objects have pair, set only keep a type of data, so the design template, at least, to pass two template parameters, because pair takes two objects, but because only one set, the set of templates that two parameters are the same, because it may be passed in a string, the string to use the hash function to calculate a hash value, then, so should be divided, because only two cases, one is the number one is a string, so we use a specified template to do to make a type of automatic selection, so here we need the third argument, but the third we do not need to pass a parameter directly give it a default hash function for this type of structure, then give it send it into our first type, if it is a string, because we use a template specialization, To automatically call a string type of template function, in the hash table to have a function to calculate a hash value, then the argument is that we passed in the third parameter, which is the structure of the hash function, then we by instantiating the object, call the function to be calculated (I do not feel good pass directly to the cause of its hash value calculation, so need to add a parameter in the hash table, and there must be a constructor to initialize, the set and the map must write such a function, since it should get, and the same way, might as well do in the hash table,
when the underlying hash table to mass participation, not only to pass that just three parameters, but also to add a parameter is set as a type of data is map data type pair, so during storage, when the hash value is calculated, with only one, it is of course set a unique value, and However, it is a first pair of values, so a structure with a bracket do overload operator, so that different types of comparative pass in different ways, so that you can achieve a different hash table Device
Then because the container is, we access its elements, you have to use on behalf of the sending device, so we have to realize a manual feed generations, a generation of the transmission is not - operation since our data originally disorder, so no need to use - in operation, providing only a ++ operation to achieve this is definitely a ++ operator overloading, because our ++ need to access the next element, only to send a node-generation device is unable to complete the task , it is necessary that table pointer also pass over, so the generation of feed to pass at least two parameters, and the method is particularly ++, get the this node, the first determination, if it is present next, if there is directly back to the node just fine, if not, will begin to calculate its position in this table, and after calculate, because judges have already passed, it is no next node, the data in this unit have all gone over, so it is next to a cell to see if the next cell is present, the head node returns put out, if there is to continue to the next single In looking for, if the last cell of the array are found not found, null is returned, the other operator overloading, and are almost red-black tree
and the hash table is to provide interfaces begin and end, although the generation of reactor feed can, but we must have a hash table objects in the map, it is easy to write in the hash table, another map and a [] operation, the specific practices and red-black tree in the same way

#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <time.h>
using namespace std;
//哈希桶

template <class V>
struct HashNode
{
	V _data;
	HashNode<V>* _next;
	HashNode(const V& data)
		:_data(data)
		, _next(nullptr)
	{}
};
template <class K, class V, class KeyOfValue, class HashFun> //这里做前置声明的的原因是送代器要用到哈希表
class HashTable;                                             //并且哈希表也要用到送代器,因为编译器是从上往下执行的
                                                             //如果我们要用到下边的代码,它就不知道是什么,所以要有一个前置声明
template <class K, class V, class KeyOfValue, class HashFun>
struct __HIerator
{
	typedef HashNode<V> Node;
	typedef Node* pNode;
	typedef __HIerator<K, V, KeyOfValue, HashFun> Self;
	typedef HashTable<K, V, KeyOfValue,HashFun> HashT;
	HashT* _ht;
	pNode _node;

	__HIerator(pNode node, HashT* ht)
		:_node(node)
		,_ht(ht)
	{}

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

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

	bool operator != (const Self& it)
	{
		return _node != it._node;
	}
	Self& operator ++ ()
	{
		if (_node->_next)
		{
			_node = _node->_next;
		}
		else
		{
			KeyOfValue kov;
			size_t index = _ht->HashIdx(kov(_node->_data), _ht->_table.size());

			++index;
			for (; index < _ht->_table.size(); ++index)
			{
				if (_ht->_table[index])
				{
					_node = _ht->_table[index];
					break;
				}
			}
			if (index == _ht->_table.size())
			{
				_node = nullptr;
			}
		}
		return *this;
	}
};
template <class K, class V, class KeyOfValue, class HashFun>
class HashTable
{
public:
	template <class K, class V, class KeyOfValue, class HashFun>
	friend struct __HIerator;

	typedef HashNode<V> Node;
	typedef Node* pNode;
	KeyOfValue kov;
	typedef __HIerator<K, V, KeyOfValue, HashFun> iterator;
	
	iterator end()
	{
		return iterator(nullptr, this);
	}

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

	pair<iterator, bool> Insert(const V& data)
	{
		CheckCapacity();
		size_t index = HashIdx(kov(data), _table.size());
		pNode cur = _table[index];

		while (cur)
		{
			if (kov(cur->_data) == kov(data))
			{
				return make_pair(iterator(cur, this), false);
			}
			cur = cur->_next;
		}
		cur = new Node(data);//直接进行头插
		cur->_next = _table[index];
		_table[index] = cur;

		++_size;
		return make_pair(iterator(cur, this), true);
	}
	size_t getPrime(size_t prime)
	{
		const int PRIMECOUNT = 28;
		const size_t primeList[PRIMECOUNT] =
		{
			53ul, 97ul, 193ul, 389ul, 769ul,
			1543ul, 3079ul, 6151ul, 12289ul, 24593ul,
			49157ul, 98317ul, 196613ul, 393241ul, 786433ul,
			1572869ul, 3145739ul, 6291469ul, 12582917ul, 25165843ul,
			50331653ul, 100663319ul, 201326611ul, 402653189ul, 805306457ul,
			1610612741ul, 3221225473ul, 4294967291ul
		};

		for (size_t i = 0; i < PRIMECOUNT; ++i)
		{
			if (primeList[i] > prime)
			{
				return primeList[i];
			}
		}
		return primeList[PRIMECOUNT - 1];
	}
	//要进行扩容,是因为原来数组的长度不够用了
	//但是如果长度改变了,原来数据所处的位置的也就不同了,所以必须重新再搞一个数组
	//弄好后,就是数据的迁移了,怎么找
	//从原来数组的第一个非空的的位置开始找
	//先把数据拿出来计算一下新的位置,找到位置
	//因为我们要将cur这个结点,放到新数组的一个位置,所以cur的next必须改变
	//因为原来发生了哈希冲突,但重新计算的长度不一样 ,所以不一定会发生哈希冲突
	//所以我们让它的next指向它自己
	//但是我们还要计算下一个cur的位置也就是原来cur的next的那个结点,但是我们提前改变了next
	//因为如果先把cur的next给到cur那就找不到原来的cur了,就不能改变新数组里面cur所指向的位置
	//因为我们必须改变新数组cur所指向的位置,所以我们要提前保存一下最原始cur的next的节点
	void CheckCapacity()
	{
		if (_size == _table.size())
		{
			size_t newC = getPrime(_table.size());
			vector<pNode> newT;
			newT.resize(newC);
			for (size_t i = 0; i < _table.size(); ++i)
			{
				pNode cur = _table[i];//cur是一个结构体指针
				while (cur)
				{
					pNode next = cur->_next;

					size_t index = HashIdx(kov(cur->_data), newT.size());
					cur->_next = newT[index];//这样搬动,省去创建新节点的开销
					newT[index] = cur;       //因为本来就有这个节点,所以直接可以把它的值拿来用
					                         //因为是个链表,所以我们可以把它链好后,直接覆盖,这样
					cur = next;              //既不用浪费空间申请节点,也能重新排好位置
				}                            //这就相当于一个搬运的过程,因为每个节点都是独立的,只是通过一层链接关系来进行相互关联
				_table[i] = nullptr;         //它们并不是连续的,只是有指针的存在,让它们看起来是连续的
			}                                //所以我们只需要链表的头,就能按顺序访问
			_table.swap(newT);               //所以到最后我们复制过来的只是链表头,其它都是改变链接顺序
		}                                    //所以最后我们在销毁旧链表时,只是将所有的链表头的地址给清空了,并不是将节点给释放了
	}                                        //所以我们不用担心,我们的数据是从旧链表中来了,一但清空这边也就用不了了   
                                               
	size_t HashIdx(const K& key, size_t sz) //用来处理是字符串的情况
	{
		HashFun hfun;
		return hfun(key) % sz;
	}
private:
	size_t _size = 0;
	vector<pNode> _table;
};
#include "哈希桶.hpp"
//模板特化

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

template <>
struct HFun<string>
{
	size_t operator () (const string& str)
	{
			size_t hash = 0;
			for (const auto& e : str)
			{
				hash = hash * 131 + e;
			}
			return hash;
	}
};


template <class K, class V,class HashFun = HFun<K>>
class UnorderedMap
{
	struct MapKeyOfValue
	{
		const K& operator () (const pair<K, V>& data)
		{
			return data.first;
		}
	};
public:
	typedef typename HashTable<K, pair<K, V>, MapKeyOfValue, HashFun>::iterator iterator;
	V& operator [] (const K& key)
	{
		pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
		return ret.first->second;
	}

	pair<iterator, bool> Insert(const pair<K, V>& data)
	{
		return _ht.Insert(data);
	}
	iterator begin()
	{
		return _ht.begin();
	}
	iterator end()
	{
		return _ht.end();
	}
private:
	HashTable<K, pair<K, V>, MapKeyOfValue, HashFun> _ht;
};

void TestUnordered()
{
	//UnorderedMap<int, int> Umap;
	//srand(time(nullptr));
	//int n;
	//cin >> n;
	//for (size_t i = 0; i < n; i++)
	//{
	//	Umap.Insert(make_pair(rand() % 50, i));
	//}

	//Umap[100] = 100;
	//Umap[200] = 200;
	//Umap[300] = 300;

	//UnorderedMap<int, int>::iterator uit = Umap.begin();
	//while (uit != Umap.end())
	//{
	//	cout << uit->first << "--->" << uit->second << endl;
	//	++uit;
	//}
	UnorderedMap<string, string> uMap;
	uMap.Insert(make_pair("123", "123"));
	uMap.Insert(make_pair("12", "123"));
	uMap.Insert(make_pair("13", "123"));
	uMap.Insert(make_pair("3", "123"));
	uMap.Insert(make_pair("133", "123"));
	uMap.Insert(make_pair("153", "123"));
	UnorderedMap<string, string>::iterator uit = uMap.begin();
	while (uit != uMap.end())
	{
		cout << uit->first << "--->" << uit->second << endl;
		++uit;
	}
	
}

template <class K, class HashFun = HFun<K>>
class UnorderedSet
{
	struct SetKeyOfValue
	{
		const K& operator () (const K& data)
		{
			return data;
		}
	};
public:
	typedef typename HashTable<K, K, SetKeyOfValue, HashFun >::iterator iterator;
	pair<iterator, bool> Insert(const K& data)
	{
		return _ht.Insert(data);
	}
	iterator begin()
	{
		return _ht.begin();
	}
	iterator end()
	{
		return _ht.end();
	}

private:
	HashTable<K, K, SetKeyOfValue, HashFun> _ht;
};

void testSet()
{
	UnorderedSet<string> uMap;
	srand(time(nullptr));
	//int n;
	//cin >> n;
	//for (size_t i = 0; i < n; i++)
	//{
	//	uSet.Insert(rand() % 50));
	//}
	uMap.Insert("123");
	uMap.Insert("12");
	uMap.Insert("13");
	uMap.Insert("3");
	uMap.Insert("133");
	uMap.Insert("153");
	UnorderedSet<string>::iterator sit = uMap.begin();
	while (sit != uMap.end())
	{
		cout << *sit << endl;
		++sit;
	}


}

int main()
{
	//TestUnordered();
	testSet();
	return 0;
}
Published 90 original articles · won praise 3 · views 10000 +

Guess you like

Origin blog.csdn.net/ZhangaZhaoLong/article/details/104718232