数据结构哈希表--线性探测和哈希桶及unordered_set,unordered_map代码示范

Posted zhaocx111222333

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构哈希表--线性探测和哈希桶及unordered_set,unordered_map代码示范相关的知识,希望对你有一定的参考价值。

这里写目录标题

哈希桶

#include<iostream>
#include<vector>
using namespace std;
//开散列

//hash表封装了一个链表指针数组,和一个size(存放的元素个数)
//链表指针数组里面就是单链表的节点

//单链表节点
template<class V>
struct HashNode{
	V _val;
	HashNode<V>* _next;

	//再写一个构造函数,插入等操作需要在某位置新建节点
	HashNode(const V& val)
		:_val(val)//可能是K,也可能是pair<K,v>
		, _next(nullptr)
	{}

};


//在实现了hashtable后,需要实现迭代器
//哈希表的迭代器需要封装链表节点
//由于hashtable的具体结构不确定,我们还需要一个hashtable来辅助迭代器的实现
//这时就有一个问题,iterator需要hashtable,hashtable需要iterator,这时就需要前置声明和友元来解决
template<class K, class V, class KeyOfValue, class HashFun=HashFun<K>>
class HashTable;

//迭代器的实现:
template<class K, class V, class KeyOfValue, class HashFun = HashFun<K>>
struct Hashiterator{
	//成员:哈希表指针,节点指针
	typedef Hashiterator<K, V, KeyOfValue,  HashFun> Self;
	typedef HashTable<K, V, KeyOfValue,  HashFun> HT;
	typedef HashNode<V> Node;
	Node* _node;
	HT* _hptr;

	//迭代器的构造函数
	Hashiterator(Node* node, HT* hptr)
		:_node(node)
		, _hptr(hptr)
	{}

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

	V* operator->(){
		return &_node->_val;//返回??
	}

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

	//迭代器的++需要考虑2种情况
	//1.链表有next
	//2.没有next
	Self& operator++(){
		if (_node->_next){
			_node = _node->_next;
		}
		else{
			//查找下一个非空链表的头结点

			//先找出该节点的hash位置,再向后查找
			KeyOfValue kov;
			HashFun hfun;
			//这里想要使用哈希表指针指向的指针数组的私有成员_ht,需要在hashtable里友元迭代器类
			size_t idx = hfun(kov(_node->_val)) % _hptr->_ht.size();
			++idx;
			for (; idx < _hptr->_ht.size(); ++idx){//需要遍历后续链表头节点
				if (_hptr->_ht[idx]){//如果哈希表的vector对应的索引存在就找到
					_node = _hptr->_ht[idx];
					break;
				}
			}
			//没有找到的情况
			if (idx == _hptr->_ht.size()){//和vector的长度比,不可和实际元素个数比
				_node = nullptr;
			}
		}
		return *this;
	}

};


//接下来是哈希表的实现
//kov是用来获取节点val的K值的
template<class K,class V,class KeyOfValue,class HashFun>
class HashTable{

public:
	typedef HashNode<V> Node;
	typedef Hashiterator<K, V, KeyOfValue,  HashFun> iterator;

	//友元
	template<class K, class V, class KeyOfValue, class HashFun>
	friend struct Hashiterator;


	//构造函数(可以写个缺省值创建vector)
	HashTable(int n = 10)
		:_ht(n)
		, _size(0)
	{}

	//写出迭代器
	iterator begin(){
		for (size_t i = 0; i < _ht.size(); ++i){
			if (_ht[i]){
				return iterator(_ht[i], this);//谁调用迭代器就用那个哈希表创建迭代器
			}
		}
		return iterator(nullptr, this);
	}

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


	pair<iterator, bool> insert(const V& val){
	//bool insert(const V& val){
		//0.检查容量
		checkCapacity();
		//1.计算哈希位置

		//需要用k值查找,所以用kov
		KeyOfValue kov;
		HashFun hfun;
		int idx = hfun(kov(val)) % _ht.size();

		//2.检查是否存在

		Node* cur = _ht[idx];
		while (cur){
			if (kov(cur->_val) == kov(val)){//如果节点的val(的K)相等
				return make_pair(iterator(cur, this), false);
				//return false;
			}
			cur = cur->_next;
		}
		//3.插入指针数组(头插)
		cur = new Node(val);
		cur->_next = _ht[idx];
		_ht[idx] = cur;
		++_size;
		return make_pair(iterator(cur, this), true);
		//return true;

	}

	size_t GetNextPrime(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
		};
		size_t i = 0;
		for (; i < PRIMECOUNT; ++i)
		{
			if (primeList[i] > primeList[i])
				return primeList[i];
		}
		return primeList[i];
	}

	void checkCapacity(){
		if (_size == _ht.size()){//如果实际的数据和指针数组的长度相同需要扩容
			//计算容量大小
			int newc = _size == 0 ? 10 : 2 * _size;
			//开辟空间
			vector<Node*> newht(newc);

			KeyOfValue kov;
			HashFun hfun;
			//遍历旧表插入新表
			for (size_t i = 0; i < _ht.size(); ++i){
			//for用来遍历指针数组的链表头节点
				Node* cur = _ht[i];
			//while遍历链表
				while (cur){
					//需要记录下一个位置,因为我们是修改指针的指向,下一个不存会丢失
					Node* next = cur->_next;
					//计算新的表的hash位置
					int idx = hfun(kov(cur->_val)) % newht.size();

					//头插
					cur->_next = newht[idx];
					newht[idx] = cur;

					cur=next;
				}

				//需要将旧表置空
				_ht[i] = nullptr;
			}

			//交换表,旧表就跟着vector的析构函数销毁了
			swap(_ht, newht);
		}
	}
		 

	Node* Find(const K& key){
		KeyOfValue kov;
		HashFun hfun;
		int idx = key % _ht.size();

		//2.检查是否存在

		Node* cur = _ht[idx];
		while (cur){
			if hfun((kov(cur->_val)) == key){//如果节点的val(的K)相等
				return cur;
			}
			cur = cur->_next;
		}
		return nullptr;
	}


	bool Erase(const K& key){
		KeyOfValue kov;
		HashFun hfun;
		int idx = key % _ht.size();
		Node* cur = _ht[idx];
		if (!cur){
			return false;
		}
		else if (cur->_next == nullptr){
			delete cur;
			_ht[idx] = nullptr;
			return true;
		}
		else{
			Node* prev = cur;
			cur = cur->_next;
			while (cur){
				if (hfun(kov(cur->_val)) == key){
					Node* tmp = cur;
					cur = cur->_next;
					prev->_next = tmp->_next;
					return true;
				}
			}

		}
		return false;
	}
		//X开散列删除
		/*Node* prev = nullptr;
		
		Node* pret = nullptr;
		while (cur){
			
			if (kov(cur->_val) == key ){//如果节点的val(的K)相等
				//Node* next = cur->_next;
				//prev = cur;
				if (cur == _ht[idx])
					_ht[idx] = cur->_next;
				else
					prev->_next = cur->_next;
				delete cur;
				--_size;
			}
			cur = cur->_next;
			
		}
		return nullptr;*/

	



private:
	vector<Node*> _ht;
	int _size;


};

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

};


//将字符串转数字的哈希函数
struct Hashfunstring
{
	size_t operator()(const string& key){
		size_t hash = 0;
		for (const auto& ch : key){
			hash = hash * 131 + ch;
		}
		return hash;
	}
};

//也可以这样写
template<>
struct Hashfun<string>
{
	size_t operator()(const string& key){
		size_t hash = 0;
		for (const auto& ch : key){
			hash = hash * 131 + ch;
		}
		return hash;
	}
};



//自定义类型的哈希函数示例:
class A{
public:
	int _a;
	string _b;
	double _d;
	//自定义类型需要手写==运算符重载
	bool operator==(const A& a){
		return _a == a._a && _b == a._b && _d == a._d;
	}
};

struct HashfunA{
	size_t operator()(const A& a){
		return a._a ;
	}

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

map和set






//因为我的hashfun给了缺省,so这里可以不写
//哈希表底层的map实现
//map底层的哈希表的指针数组放的是,成员为pair的单链表指针
template <class K,class V>
class Map{
	//因为哈希表是三个泛型,第三个为获取K的仿函数
	struct MapKeyOfValue
	{
		//仅需要重载()运算符即可获取K值
		const K& operator()(const pair<K, V>& val){
			return val.first;
		}
	};

public:
	typedef typename HashTable<K, pair<K, V>, MapKeyOfValue>::iterator iterator;

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

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

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

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

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

private:
	HashTable<K, pair<K, V>, MapKeyOfValue> _ht;

};


template <class K>
class Set{
	//因为哈希表是三个泛型,第三个为获取K的仿函数
	struct SetKeyOfValue
	{
		//仅需要重载()运算符即可获取K值
		const K& operator()(const K& val){
			return val;
		}
	};

public:
	typedef typename HashTable<K, K, SetKeyOfValue>::iterator iterator;

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

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

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


	bool earse(const K& val){
		return _ht.Erase(val);
	}

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

};

void test(){
	/*Set<int> s;
	s.insert(1);
	s.insert(11);
	s.insert(12);
	s.insert(13);
	s.insert(44);
	s.insert(5);
	s.insert(7);
	s.insert(8);
	s.insert(2);
	s.insert(3);
	s.earse(1);
	s.earse(11);
	s.earse(12);
	s.earse(100);*/
	Map<int, int> s;
	s.insert(make_pair(1, 1));
	s[1] = 5;
	s[5] = 10;
	s[15] = 15;
	s[4] = 4;
	s.earse(1);
	//cout << s.find(2) << endl;
	for (auto& e : s){
		cout << e.first << " " << e.second << endl;
	}
}
int main(){
	test();
	system("pause");
	return 0;
}

线性探测

#include<vector>
#include<iostream>
using namespace std;
//闭散列主要是线性探测
//每一个节点是一种标志位和一个值
//哈希表里面是一个节点的vector和有效元素个数
enum STATE
{
	EXIST,
	DELETE,
	EMPTY
};


//我们让节点里存放kv键值对
template<class K, class V>
struct HashNode
{
	pair<K, V> _kv;
	STATE _state = EMPTY;
};


//哈希表的实现:
template<class K, class V>
class HashTable
{
public:
	typedef HashNode<K,以上是关于数据结构哈希表--线性探测和哈希桶及unordered_set,unordered_map代码示范的主要内容,如果未能解决你的问题,请参考以下文章

C++数据结构——哈希表

C++数据结构——哈希表

C++数据结构——哈希表

C++数据结构——哈希表

手撕STLunordered_setunordered_map(用哈希表封装)

哈希表底层探索