详解c++---哈希封装

news2024/11/15 13:25:51

目录标题

  • 哈希桶的代码
  • 哈希桶的修改
  • 迭代器的实现
  • const迭代器

哈希桶的代码

通过前面的学习大家应该能够很容易的写出下面的代码:

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

哈希桶的修改

首先我们这里得使用哈希桶来封装出来set和map,根据前面红黑树的经验我们知道虽然set容器内部只存放一种数据,但是在封装的时候依然得使用两个模板参数来对数据进行封装,因为insert函数的参数得是value,而find函数的参数是key,所以为了保证封装时候的统一性这里就采用两个参数来表示哈希桶中的内部数据,对于set容器节点模板的两个参数都是key,对于map容器节点模板的两个参数则是key和value,那么描述节点类的代码就如下:

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

既然节点类发生了修改,那么哈希桶的insert函数也得做出修改将原来参数的pair类型转换成为模板的第二个类型T,又因为第二个参数可能存储的是pair也可能存储的是key,但是find函数和insert函数之前一直都是用pair的第一个参数来进行比较,所以这里得添加一个仿函数参数来获取容器内部数据的key值,并且insert函数,find函数,erase函数都是通过创建仿函数对象来进行数据的比较,所以这里得一一做出更改,将之前手动通过pair获取first数据的方式修改成为通过仿函数来获取key,比如原来是这样的:cur->_kv.first,那么修改之后就应该变成了下面这样:kot(cur->_data)将上面哈希桶的全部的这样情况都进行修改就完成了第一步,接下来就实现unorderedmap和unorderedset的类的实现,首先这两个类都是基于哈希桶来实现的,所以这两个类中都一个哈希桶的私有变量, 其次我们使用哈希表的时候可以传递不同的仿函数,所以这里的类也得添加一个仿函数的模板参数,又因为哈希桶需要一个仿函数来获取数据中的key,所以在外层的set和map容器得创建一个仿函数并且传递给内部的哈希桶,那么这里的代码就如下:

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

将基础的框架实现之后就可以完成内部的find函数,insert函数,erase函数,那么这里的实现原理就是通过调用哈希桶内部的函数来进行实现,那么这里的代码就如下:

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

有了这些基础的函数我们就可以用下面的代码来简单的检查一下上面的代码有没有实现正确,那么这里的检查代码就如下:

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

这段代码的运行结果如下:
在这里插入图片描述
运行的结果没有问题,那么这就说明我们上面实现的代码没有问题,接下来就可以继续实现map和set的迭代器。

迭代器的实现

哈希桶的每个节点都是一个链表,所以迭代器的遍历就是先从vector的第一个位置开始,先找到第一个节点不为空的位置,然后从上往下依次遍历链表中的每个节点,如果链表遍历完成之后就从当前位置完后继续寻找下一个vector中不为空的节点,那么这就是迭代器的实现思路,那么要想实现迭代器的++首先就得创建一个迭代器的类,
首先迭代器肯定得是一个模板,并且模板参数是四个,那么这里的代码就如下:

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

};

首先++函数肯定要记录HashNode节点,所以在该类里面就得创建一个节点指针,其次++函数的返回值是迭代器本身,所以这里可以用typedef简化一下该类,因为在++的时候需要使用vector容器来查找下一个链表的位置,所以这里就得把哈希桶类也传递过来,因为vector容器比较难传递所以这里就传递哈希桶,那么这里的代码就如下:

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++()
	{
	}	
};

然后就来实现++的操作符重载,首先判断当前节点的下一个节点是否为空,如果不为空的话就说明下面还存在数据,我们就改变节点指针的指向,让其指向下一个节点,如果该节点的下一个指向为空的话,我们就得查找一个链表所在的位置,首先创建keyoft的仿函数找到数组中的key,然后通过hashfunc函数找到该数据转换的值,最后摸上容器的大小最终就可以确定当前元素所在的位置,然后就可以创建一个循环找到下一个不为空的位置,那么这里的代码就如下:

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

	}
}	

如果循环结束了这里会出现两种情况,第一种就是当前元素是最后一个元素再++就没有元素了,第二个情况就是找到了下一个元素,对于第一种情况就可以用hashi是否等于当前_tables.size()来进行判断,如果第一种情况不成立就说明当前情况是第二种情况,我们这里用迭代器的_node为空指针来代表当前已经来到了元素的最后一个位置,那么这里的代码就如下:

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

这些函数实现完之后就可以来实现迭代器的构造函数,构造函数就是将节点指针和哈希桶指针初始化一下就可以了,那么这里的代码就如下:

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

将这个函数完成之后我们就可以回到哈希桶的类然后接着实现迭代器对应的begin函数和end函数,这里的begin函数返回的就是vector容器中第一个链表不为空的地址,那么这里就可以创建一个for循环来不断地进行查找,那么这里地代码就如下:

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

end函数实现地原理也相似,但是我们使用nullptr来表示没有元素了,所以这里地代码实现如下:

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

但是我们这么实现存在一个问题,将代码运行一下便可以看到当前报出来了很多的错误:
在这里插入图片描述
原因是因为我们这里的实现的迭代器需要哈希桶,但是哈希桶的实现有需要迭代器,所以这里出现了相互引用自相矛盾了,所以这里就需要使用将哈希桶类前置声明的方式来解决问题,这里的代码如下:

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

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

将上面的代码实现之后就可以将这个迭代器套用到set里面

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

将这个完成之后就可以用下面的代码来测试一下上面的正确性:

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

将这段代码运行一下就会发现上面的实现好像出了问题:
在这里插入图片描述
出现这个问题的原因就是
在这里插入图片描述
这里的_tables是一个私有的成员变量,迭代器类无法正常的访问,所以这里的解决方法就是使用友元来进行解决,那么这里的代码就如下:

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

将这个完成之后就可以正常地运行上面地代码,那么这里运行地结果就如下:
在这里插入图片描述
将迭代器实现之后就可以将find函数地返回值和insert函数的返回值修改一下,insert返回的是pair,find返回的是iterator,这里修改之后就可以实现方括号的重载,方括号重载就是先调用insert函数,然后接收他的pair类型的返回值,最后返回pair元素的第一个的第二个元素,也就是Value,那么这里的代码就如下:

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

这里可以使用下面的代码来进行一下测试:

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

代码的运行结果如下:
在这里插入图片描述
代码的运行结果符合我们的预期,那么接下来我们就来看看const迭代器是如何实现的。

const迭代器

根绝前面的经验我们知道可以通过添加模板参数的形式来将const迭代器和普通迭代器融合在一起,那么首先将迭代器的模板参数进行修改,并且将两个操作符的解引用进行修改,就可以得到下面的代码:

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

然后就对哈希桶类中的代码进行修改,创建一个const迭代器,增加const版本的begin函数和end函数,那么这里的代码大致如下:

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

然后就可以用下面的代码来进行一下测试:

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

这段代码的运行结果如下:
在这里插入图片描述

我们发现上面的这种实现方法出现了问题,出问题的地方就在构造函数,我们使用begin函数来创建const迭代器,因为us被const所修饰所以这里调用的begin是const类型的begin

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

这里的begin函数是通过找到第一个不为空的节点来构造const迭代器,因为这里是const版本,所以这里使用的_tables[i]也是const版本,_tables是一个vector容器并且还是const版本,这里调用方括号的返回值来进行构造,而vector的方括号重载也分为两个版本:
在这里插入图片描述
所以这里返回的就是const版本的,而迭代器在构造的时候是使用普通版本的指针来进行构造:

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

所以这里的迭代器对象创建就失败了,如果给这里的node指针添加const的话依然是无法解决问题的,他会报出下面的错误:

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

在这里插入图片描述

因为这里的权限被放大了,所以就出现问题,那如果我们讲_node也变成const属性呢?比如说下面这样:

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

如果这样的话那普通迭代器还能对里面的内容进行修改吗?是不是就不行了啊,所以这里采用的解决方法就是const迭代器和普通迭代器分开定义,比如说下面的代码:

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

这样我们再运行上面的代码就不会出现问题:
在这里插入图片描述
那么到这里本篇文章的内容就全部完成了,所有的代码如下:

//哈希桶文件的代码
#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;
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年互联网行业研究报告

第一章 行业概况 互联网行业是一个广泛的领域&#xff0c;包括所有利用互联网技术进行商业活动的企业和组织。这个行业的核心是互联网&#xff0c;一个全球性的网络&#xff0c;连接着数以亿计的计算设备和用户&#xff0c;使他们可以共享信息、资源和服务。 互联网行业包括网…

apache 安装配置 基础篇(-)

download 地址 apache下载 ApacheHaus是免安装的&#xff0c; 然后解压上面的文件&#xff0c;把里面 因apache 默认端口是80&#xff0c;如果这个端口被占用&#xff0c;apache服务是启动不起来的 netstat -ano|findstr 80 apache 修改端口号 创建apache服务 在apa…

ESP32-H2 固件烧录需满足的硬件环境整理

ESP32-H2 默认通过 UART0 &#xff08;即 TXD&#xff08;GPIO24&#xff09;和 RXD&#xff08;GPIO23&#xff09;&#xff09;下载固件。 Windows 下可使用 Flash download tool 工具来下载编译后的 bin 文件&#xff1b; 运行 flash_download_tool.exe 的文件 选择开发…

SkyEye处理器仿真系列:龙芯2K1000处理器

​SkyEye简介&#xff1a; 天目全数字实时仿真软件SkyEye作为基于可视化建模的硬件行为级仿真平台&#xff0c;能够为嵌入式软件提供虚拟化运行环境&#xff0c;开发、测试人员可在该虚拟运行环境上进行软件开发、软件测试和软件验证活动。小到芯片&#xff0c;大到系统&#…

win10 DBeaver (升级)下载、安装、彻底卸载

DBeaver &#xff08;升级&#xff09;下载及安装 一、DBeaver 下载二、安装三、DBeaver 的基本使用 - mysql连接四、DBeaver 彻底卸载 DBeaver是一种通用数据库管理工具&#xff0c;适用于需要以专业方式使用数据的每个人&#xff1b;适用于开发人员&#xff0c;数据库管理员&…

苹果笔买原装的还是随便买?便宜好用的手写笔推荐

自从ipad和其他的平板电脑都搭配上了电容笔以后&#xff0c;电容笔很好地取代了我们的手指&#xff0c;书写的效率就大大提升了&#xff0c;但由于苹果原装电容笔的价格不够人性化&#xff0c;一直高居不下给普通人带来了很大的负担&#xff0c;特别是对于学生们来说&#xff0…

QT DAY1

做一个窗口界面 #include "mainwindow.h" #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow) {ui->setupUi(this);//设置窗口标题、图标this->setWindowTitle("Fly_Chat")…

6、Flume安装部署

按照采集通道规划&#xff0c;需在hadoop102&#xff0c;hadoop103&#xff0c;hadoop104三台节点分别部署一个Flume。可参照以下步骤先在hadoop102安装&#xff0c;然后再进行分发。 1、Flume入门 1.1、 Flume安装部署 1.1.1、 安装地址 &#xff08;1&#xff09; Flume官…

全网最牛,Web自动化测试Selenium八大元素定位实战(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 安装Selenium和下…

BFT 最前线|OpenAI暂时下线ChatGPT”浏览“功能;Stability AI CEO:5年内,人类程序员将不复存在

原创 | 文 BFT机器人 AI视界 TECHNOLOGY NEWS 01 Open AI暂时下线ChatGPT“浏览”功能 日前OpenAI方面宣布&#xff0c;面向ChatGPT Plus用户的"浏览"功能会在某些情况下出现故障&#xff0c;因此已于7月3日暂时禁用了这一功能。该功能是为了提高ChatGPT的搜索体验…

威胁检测和取证日志分析

在网络中&#xff0c;威胁是指可能影响其平稳运行的恶意元素。因此&#xff0c;对于任何希望搁置任何财政损失或生产力下降机会的组织来说&#xff0c;威胁检测都是必要的。为了先发制人地阻止来自各种来源的任何此类攻击&#xff0c;需要有效的威胁检测情报。 威胁检测可以是…

mmap函数

参考 https://blog.csdn.net/bhniunan/article/details/104105153void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);参数 addr&#xff1a;出参&#xff0c; 指定映射的起始地址&#xff0c;通常设为NULL&#xff0c;由内核来分配 len&#x…

网络编程3——TCP Socket实现的客户端服务器通信完整代码(详细注释帮你快速理解)

文章目录 前言一、理论准备Socket套接字是什么TCP协议的特点 二、TCP 流套接字提供的APIServerSocket APISocket API 三、代码实现请求响应式 客户端服务器服务器客户端疑惑解答为什么服务器进程需要手动指定端口号而客户端进程不需要为什么客户端中的服务器IP与端口号是"…

Mysql架构篇--Mysql 主从同步方案

文章目录 前言一、传统的主从复制&#xff1a;1 原理&#xff1a;2 缺点&#xff1a; 二、半同步复制&#xff08;Semi-Synchronous Replication&#xff09;&#xff1a;三、组复制&#xff1a;1 原理&#xff1a;2 实现&#xff1a;2.1 myql 实例安装&#xff1a;2.1 myql 实…

量子近似优化算法(QAOA)入门(1):从量子绝热算法(QAA)角度的直观理解

文章目录 前言&#xff1a;量子计算的本质是测量一、基于量子逻辑电路的常用算法1.NISQ&#xff1a;Noisy Intermediate-Scale Quantum&#xff08;含噪声中等规模量子&#xff09; 二、量子绝热算法&#xff08;QAA&#xff1a;Quantum Adiabatic Algorithm&#xff09;1.QAA的…

【KingFusion】用KingFusion3.6创建一个客户端工程的步骤

哈喽&#xff0c;大家好&#xff0c;我是雷工&#xff01; 今天学习用KingFusion3.6创建一个客户端工程&#xff0c;以下记录创建过程。 客户端组件作为KingFusion3.6的数据展示功能模块&#xff0c;其主要功能是通过组态组态式配置以及丰富的图表元素、动画连接等多样的展示形…

ROS:TF坐标变换

目录 一、TF坐标变换背景二、概念三、静态坐标变换3.1概念3.2实际用例3.2.1分析3.2.2流程3.2.3C实现 一、TF坐标变换背景 机器人系统上&#xff0c;有多个传感器&#xff0c;如激光雷达、摄像头等&#xff0c;有的传感器是可以感知机器人周边的物体方位(或者称之为:坐标&#…

《LORA: LOW-RANK ADAPTATION OF LARGE LANGUAGE MODELS》论文笔记

引言 全量参数微调在LLM背景下由“不方便”演变为“不可行|高昂成本”&#xff0c;基于“收敛的模型参数可以压缩到低维空间”的假设&#xff1a; the learned over-parametrized models in fact reside on a low intrinsic dimension. 作者提出LORA&#xff08;Low Rank Adap…

远程关闭或重新启动计算机

远程关机只是从远程位置关闭计算机的过程。主要领域是组织在没有知识的情况下失去收入将是电力费用。员工倾向于在周末打开他们的系统。不必要的电力消耗也会影响我们的环境。在这种情况下&#xff0c;系统管理员可以在周末和非工作时间安排自动系统关闭&#xff0c;或者在必要…

Valve 签约开源 Linux 图形驱动开发者

导读据外媒 phoronix 报道&#xff0c;Valve 最近聘用了著名开源 Linux 图形驱动开发者 Alyssa Rosenzweig&#xff0c;以改进开源 Linux 图形驱动程序堆栈&#xff0c;增强 Linux 游戏生态系统。 Alyssa Rosenzweig 多年来在 Panfrost 开源、逆向工程 Arm Mali 图形驱动程序方…