[C++]20:unorderedset和unorderedmap结构和封装。

news2024/9/20 21:27:40

unorderedset和unorderedmap结构和封装

  • 一.哈希表:
    • 1.直接定址法:
    • 2.闭散列的开放定址法:
      • 1.基本结构:
      • 2.insert
      • 3.find
      • 4.erase
      • 5.补充:
      • 6.pair<k,v> k的数据类型:
    • 3.开散列的拉链法/哈希桶:
      • 1.基本结构:
      • 2.insert
        • 1.正常插入:
        • 2.考虑扩容
      • 3.find
      • 4.erase
      • 5.数据计算观察:
  • 二.unordered_set和unordered_map的封装:
    • 1.unordered_set
      • 1.基本结构:
      • 2.插入:
      • 3.迭代器:
      • 4.find
      • 5.整体代码:
    • 2.unorder_map
      • 1.基本结构:
      • 2.插入:
      • 3.迭代器:
      • 4.find
      • 5.operator[]重载:
      • 6.整体代码:
      • 7.补充代码:
    • 3.HashTable
      • 1.find查找:
      • 2.迭代器:
        • 1.基本结构:
        • 2.operator++
        • 3.整体代码:
      • 3.插入:
        • 1.bool返回的插入:
        • 2.重载operator[]实现的重载unordered_map独有:

一.哈希表:

1.直接定址法:

1.数据比较集中并且数据都比较小。
2.使用key值作为下标进行数据的存贮。
3.适用于数据比较小并且连续的情况。

字符串中第一个唯一字符

2.闭散列的开放定址法:

1.基本结构:

namespace oper_addres {
	
	enum state {
		Empty,
		Delete,
		Exist,
	};

	template<class k, class v>
	struct Hash_Node {
		Hash_Node(pair<k, v> x = pair<k,v>())
			:_date(x)
			,_state(Empty)
		{}

		pair<k, v> _date;
		state _state;
	};
	
	template<class k, class v>
	class Hash {
	public:
		Hash(size_t n = 10)
		{
			_hash.resize(n);
		}
	private:
		vector<Hash_Node<k, v>> _hash;
		size_t _num = 0;
	};
}

2.insert

1.不存在重复数据:除留余数法,1%10==1 下标1位置放置数值1,4%10=4 下标4位置放置数值4,以此类推。

2.存在重复数据->线性探测->7%10=7 ,下标7位置放置数值7,17%10=7 下标7位置有值就向后放置下标8就放置17,27%10=7下标7位置有值 下标8位置有值下标9放置,++然后取模找到可以放置值的位置结束。

3.如何确定这个位置的值情况?
提供枚举常量 :Empty Delete Exist

4.扩容+数值拷贝:每插入一个值都需要计算负载因子 = 当前插入的数据量/可以插入的size 当这个差值>=0.7就需要进行扩容,新建一个newhashtable大小为当前表的两倍,新的表使用insert插入原来哈希表数据最后交换两个哈希表的vector。

		bool insert(pair<k, v> x)
		{
			//扩容:
			size_t tmp = ((_num * 10) / _hash.size());

			if (tmp >= 7)
			{
				Hash<k, v>* newhash = new Hash<k, v>(_hash.size() * 2);
				for (auto e : _hash)
				{
					newhash->insert(e._date);
				}

				_hash.swap(newhash->_hash);
			}


			//1.正常插入:插入位置
			size_t indx = x.first % _hash.size();

			//2.进行插入:
			if ((_hash[indx]._state) != Empty || (_hash[indx]._state) == Delete)
			{
				//向后查找->线性探测:
				while (_hash[indx]._state == Exist)
				{
					indx++;
					indx %= _hash.size();
				}

				_hash[indx]._date = x;
				_hash[indx]._state = Exist;
				_num++;
				return true;
			}

			_hash[indx]._date = x;
			_hash[indx]._state = Exist;
			_num++;
			return true;
		}

在这里插入图片描述

3.find

1.find数据传参查找的数据并且返回数据的下标。
2.数据%hashtable.size(),对应下标位置就是这个值返回下标,如果不是线性探测直到空为止。数据不存在返回-1.

			int find(const k& fd)
		{
			size_t Hashi = fd % _hash.size();

			if (_hash[Hashi]._state == Exist && _hash[Hashi]._date.first == fd)
			{
				return Hashi;
			}
			else
			{
				while (_hash[Hashi]._date.first != fd && _hash[Hashi]._state != Empty)
				{
					Hashi++;
					Hashi %= _hash.size();
				}

				if (_hash[Hashi]._date.first == fd && _hash[Hashi]._state == Exist)
					return Hashi;
				else
					return -1;
			}
		}

4.erase

1.这个地方首先使用find找到对应的下标位置进行返回。
2.找到就修改这个位置的状态为Delete,并且返回true。


		bool erase(const k& fd)
		{
			int hashi = find(fd);
			if (hashi != -1)
			{
				_hash[hashi]._state = Delete;
				return true;
			}
			return false;
		}

5.补充:

size_t size()
		{
			return _num;
		}

		bool empty()
		{
			if (_num == 0)
				return true;
			return false;
		}

1.哈希冲突?
2.数据%hashtable.size()同一个位置的数据然后进行线性探测,线性探测的次数越多哈希冲突越多。哈希冲突越多,效率就越低。
3.当因子大于0.7就考虑进行扩容。

6.pair<k,v> k的数据类型:

1.数据类型的一个转换考虑如何变成下标可以识别的size_t类型。
2.实现仿函数,并且重载operator()
3.特殊类型可以使用类模板的特化。

template<class T>
	struct transition
	{
		size_t operator()(const T& x)
		{
			return x;
		}
	};

	//1.string -> string
	template<>
	struct transition<string>
	{
		size_t operator()(const string& x)
		{
			//1.可以计算string字符串的asia码的和并且每次*131降低哈希冲突:
			size_t sum = 0;
			for (auto& e : x)
			{
				sum += (e*131);
			}
			return sum;
		}
	};

在这里插入图片描述

3.开散列的拉链法/哈希桶:

1.开散列,首先对于key值计算出下标位置,具有相同下标位置的值放在同一个子集里面,每一个子集就是一个桶,每一个桶中的元素通过一个单链表连接在一起,每一个链表的头节点由vector保存

1.基本结构:

namespace Hash_bucket{
	template<class T>
	struct transition
	{
		size_t operator()(const T& x)
		{
			return x;
		}
	};

	//1.string -> string
	template<>
	struct transition<string>
	{
		size_t operator()(const string& x)
		{
			//1.可以计算string字符串的asia码的和并且每次*131降低哈希冲突:
			size_t sum = 0;
			for (auto& e : x)
			{
				sum += (e * 131);
			}
			return sum;
		}
	};

	template<class k, class v>
	struct Hash_Node {
		typedef Hash_Node Node;
		Hash_Node(pair<k, v> x = pair<k, v>())
			:_date(x)
			,_next(nullptr)
		{}

		pair<k, v> _date;
		Node* _next;
	};
	//, class trans = transition<k>
	template<class k, class v, class trans = transition<k>>
	class Hash {
	public:
		typedef Hash_Node<k, v> Node;
		Hash(size_t n = 10)
		{
			_hash.resize(n,nullptr);
			_num = 0;
		}
	private:
		vector<Node*> _hash;
		size_t _num;
	};
}

2.insert

1.正常插入:
bool insert(const pair<k, v>& x)
		{

			//1.正常插入:
			trans kot;
			size_t hashi = kot(x.first) % _hash.size();
			Node* newnode = new Node(x);

			//1-1:_hash[hashi]==nullptr 直接插入节点:
			if (_hash[hashi] == nullptr)
			{
				_hash[hashi] = newnode;
				_num++;

				return true;
			}
			//1-2:_hash[hashi]!=nullptr 进行单链表的头插:
			else
			{
				newnode->_next = _hash[hashi];
				_hash[hashi] = newnode;
				_num++;

				return true;
			}

			return false;
		}

在这里插入图片描述

2.考虑扩容

1,什么时候需要取进行扩容?
2.当我们的vector<Node*> _hash;每一个下标处都有非空节点就进行扩容。
3.扩容需要考虑原来链表中保存的节点并且考虑进行重新的插入数据。
4.节点数据不需要先delete后new,直接进行简单连接的转移。
5.开头使用find可以帮助我们判断要插入的这个节点之前存不存在。

		bool insert(const pair<k, v>& x)
		{
			if (find(x.first))
				return false;

			trans kot;
			//1.计算平衡因子:
			if (_num == _hash.size())
			{
				vector<Node*> newhash(_hash.size() * 2, nullptr);
				for(int i=0;i<_hash.size();i++)
				{
					Node* cur = _hash[i];
					while (cur)
					{
						Node* next = cur->_next;
						// 头插到新表
						size_t hashi = kot(cur->_date.first) % (_hash.size()*2);
						cur->_next = newhash[hashi];
						newhash[hashi] = cur;

						cur = next;
					}

					_hash[i] = nullptr;
				}
				_hash.swap(newhash);
			}
			else
			{
				//1.正常插入
				size_t hashi = kot(x.first) % _hash.size();
				Node* newnode = new Node(x);

				//1-1:_hash[hashi]==nullptr 直接插入节点:
				newnode->_next = _hash[hashi];
				_hash[hashi] = newnode;
				_num++;
				
				return true;
			}
			return false;
		}

3.find

1.%_hash.size()快速确定下标位置。
2.通过cur遍历单链表找到值相同的节点就返回。
3.找不到就返回空节点。


Node* find(const k& fd)
{
			trans kot;
			size_t i = kot(fd) % _hash.size();
			Node* cur = _hash[i];
			while (cur)
			{
				if (cur->_date.first == fd)
					return cur;
				cur = cur->_next;
			}

			return nullptr;
}

4.erase

1.%_hash.size()快速确定下标位置。
2.两个情况:
2-1:_hash[hashi]保存的就是需要删除的节点
2-1:需要删除的节点在单链表中。

bool erase(const k& fd)
		{
			trans kot;
			size_t i = kot(fd) % _hash.size();
			Node* cur = _hash[i];
			Node* prev = nullptr;
			while (cur)
			{
				if (cur->_date.first == fd)
				{
					//头节点就是需要删除的
					if (prev == nullptr)
					{
						_hash[i] = cur->_next;
					}
					//在单链表中的节点需要被删除:
					else
					{
						prev->_next = cur->_next;
						
					}
					return true;
				}
				prev = cur;
				cur = cur->_next;
			}
			return false;
		}

5.数据计算观察:

		void Some()
		{
			size_t bucketSize = 0;
			size_t maxBucketLen = 0;
			size_t sum = 0;
			double averageBucketLen = 0;

			for (size_t i = 0; i < _hash.size(); i++)
			{
				Node* cur = _hash[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("load factor:%lf\n", (double)_num / _hash.size());
			//表长度:
			printf("all bucketSize:%d\n",_hash.size());
			//桶的个数:
			printf("bucketSize:%d\n", bucketSize);
			//最长的桶的长度
			printf("maxBucketLen:%d\n", maxBucketLen);
			//平均桶长度
			printf("averageBucketLen:%lf\n\n", averageBucketLen);
		}

二.unordered_set和unordered_map的封装:

1.unordered_set

1.基本结构:

namespace sfpy {
	template<class k,class transition = transition<k>>
	class myunset {

	public:
		struct copy_set {
			const k& operator()(const k& x)
			{
				return x;
			}
		};
	private:
		Hash_bucket::Hash<k,k,copy_set,transition> _t;
	};
}

2.插入:

//1.插入:
		bool Insert(const k& x)
		{
			pair<iterator, bool> ret = _t.Insert(x);
			return ret.second;
		}

		bool insert(const k& x)
		{
			return _t.insert(x);
		}

3.迭代器:

typedef typename Hash_bucket::Hash<k, k, copy_set, transition>::_iterator iterator;

iterator begin()
		{
			return _t.find_begin();
		}

		iterator end()
		{
			return nullptr;
		}


4.find

//3.find()
		iterator _find(const k& x)
		{
			return _t.Find(x);
		}

5.整体代码:

namespace sfpy {
	template<class k,class transition = transition<k>>
	class myunset {

	public:
		struct copy_set {
			const k& operator()(const k& x)
			{
				return x;
			}
		};

		typedef typename Hash_bucket::Hash<k, k, copy_set, transition>::_iterator iterator;
		//1.插入:
		bool Insert(const k& x)
		{
			pair<iterator, bool> ret = _t.Insert(x);
			return ret.second;
		}

		bool insert(const k& x)
		{
			return _t.insert(x);
		}

		//2.迭代器:
		

		iterator begin()
		{
			return _t.find_begin();
		}

		iterator end()
		{
			return nullptr;
		}

		//3.find()
		iterator _find(const k& x)
		{
			return _t.Find(x);
		}
		

	private:
		Hash_bucket::Hash<k,k,copy_set,transition> _t;
	};
}

2.unorder_map

1.基本结构:

namespace sfpy {
	template<class k , class v , class transition = transition<k>>
	class myunmap {
	public:
		struct copy_map{
			const k& operator()(const pair<k,v>& x)
			{
				return x.first;
			}
		};
	private:
		Hash_bucket::Hash<k,pair<k,v>, copy_map , transition> _t;
	};
}

2.插入:

//1.插入:
		bool Insert(const pair<k, v> x)
		{
			pair<iterator, bool> ret = _t.Insert(x);
			return ret.second;
		}

		bool insert(const pair<k, v> x)
		{
			return _t.insert(x);
		}

3.迭代器:

typedef typename Hash_bucket::Hash<k, pair<k, v>, copy_map, transition>::_iterator iterator;

		//2.迭代器

		iterator begin()
		{
			return _t.find_begin();
		}

		iterator end()
		{
			return  _t.find_end();
		}

4.find

iterator _find(const k& x)
		{
			return _t.Find(x);
		}

5.operator[]重载:

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

6.整体代码:

namespace sfpy {
	template<class k , class v , class transition = transition<k>>
	class myunmap {
	public:
		struct copy_map{
			const k& operator()(const pair<k,v>& x)
			{
				return x.first;
			}
		};

		typedef typename Hash_bucket::Hash<k, pair<k, v>, copy_map, transition>::_iterator iterator;

		//1.插入:
		bool Insert(const pair<k, v> x)
		{
			pair<iterator, bool> ret = _t.Insert(x);
			return ret.second;
		}

		bool insert(const pair<k, v> x)
		{
			return _t.insert(x);
		}

		//2.迭代器

		iterator begin()
		{
			return _t.find_begin();
		}

		iterator end()
		{
			return  _t.find_end();
		}

		//3.find()

		iterator _find(const k& x)
		{
			return _t.Find(x);
		}

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


	private:
		Hash_bucket::Hash<k,pair<k,v>, copy_map , transition> _t;
	};
}

7.补充代码:

1.我们知道在unordered_set和unordered_map中key值是不可以被修改的。
2.我们上面的代码是可以修改key值就是一个比较离谱的事情。
3.封装unordered_map和unordered_set对key的类型进行加const。

namespace sfpy {
	template<class k , class v , class transition = transition<k>>
	class myunmap {
	public:
		struct copy_map{
			const k& operator()(const pair<const k,v>& x)
			{
				return x.first;
			}
		};

		typedef typename Hash_bucket::Hash<k, pair<const k, v>, copy_map, transition>::_iterator iterator;

		//1.插入:
		bool Insert(const pair<const k, v> x)
		{
			pair<iterator, bool> ret = _t.Insert(x);
			return ret.second;
		}

		bool insert(const pair<const k, v> x)
		{
			return _t.insert(x);
		}

		//2.迭代器

		iterator begin()
		{
			return _t.find_begin();
		}

		iterator end()
		{
			return  _t.find_end();
		}

		//3.find()

		iterator _find(const k& x)
		{
			return _t.Find(x);
		}

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


	private:
		Hash_bucket::Hash<k,pair<const k, v>, copy_map , transition> _t;
	};
}

3.HashTable

1.map和set去封装同一个哈希表。
2.模板:template<class k , class T, class copy, class trans>
3.copy类重载了operator()做键值的获取。
4.trans是一个类型转换,比如说string转化为一个size_t类型方便下标的使用。

1.find查找:

1.返回查找到的数据的节点或者空指针。
2.数据计算下表并且遍历单链表找到节点返回节点指针。
3.找不到节点就返回nullptr

Node* find(const k& fd)
		{
			trans up;
			copy kot;

			size_t i = up(fd) % _hash.size();
			Node* cur = _hash[i];
			while (cur)
			{
				if ((up(kot(cur->_date))) == up(fd))
					return cur;
				cur = cur->_next;
			}

			return nullptr;
		}

2.迭代器:

1.基本结构:

1.迭代器肯定需要封装数据的节点。
2.封装数据的节点够吗?不够!
3.只封装数据的节点重载++在一个单链表的数据还可以,但是进行链表的跳转就无法实现了。
4.考虑封装一个哈希表到迭代器中。
5.迭代器和哈希表会相互封装(上面的类找不到下面的)—>模板+声明放到上面的那个类的上面。

struct unorderediterator
	{
		typedef Hash_Node<T> Node;
		typedef Hash<k, T, copy, trans> HT;
		typedef unorderediterator<k, T, copy, trans> self;

		HT* _Hash;
		Node* _node;

		unorderediterator(HT* hash, Node* x)
			:_Hash(hash)
			, _node(x)
		{}
	};
2.operator++

1,情况一:当前节点的下一个不是空直接修改迭代器中节点的内容_node = cur->next
2.情况二:当前节点的下一个是空,求当前单链表所在节点的哈希下表使用节点访问数据进行计算,找到下标之后哈希表向后进行遍历。
2-1:找到一个不是空的就进行_node的修改。
2-2:找不到,表示哈希表一直向后进行遍历到结尾都是空指针。

//主要是要去找节点:
		self operator++()
		{
			trans kot;
			copy up;

			//1.情况一:当前节点有下一个节点:
			if (_node->_next != nullptr)
				_node = _node->_next;
			//2.哈希位置的跳转:
			else
			{
				size_t hashi = kot(up(_node->_date)) % _Hash->_hash.size();
				hashi++;

				while (hashi < _Hash->_hash.size())
				{
					if (_Hash->_hash[hashi])
					{
						_node = _Hash->_hash[hashi];
						break;
					}
					hashi++;
				}

				if (hashi == _Hash->_hash.size())
					_node = nullptr;
			}
			return *this;
		}
3.整体代码:

	//迭代器:
	template<class k, class T, class copy, class trans>
	struct unorderediterator
	{
		typedef Hash_Node<T> Node;
		typedef Hash<k, T, copy, trans> HT;
		typedef unorderediterator<k, T, copy, trans> self;

		HT* _Hash;
		Node* _node;

		unorderediterator(HT* hash, Node* x)
			:_Hash(hash)
			, _node(x)
		{}


		T& operator*()
		{
			return _node->_date;
		}

		T* operator->()
		{
			return &(_node->_date);
		}

		bool operator!=(const self& bitter)//self* bitter
		{
			//比较哈希表中的vector不是iterator?
			//迭代器==哈希+节点 比较迭代器的地址还是节点的地址?
			if (this->_node == bitter._node)
				return false;

			return true;
		}

		//主要是要去找节点:
		self operator++()
		{
			trans kot;
			copy up;

			//1.情况一:当前节点有下一个节点:
			if (_node->_next != nullptr)
				_node = _node->_next;
			//2.哈希位置的跳转:
			else
			{
				size_t hashi = kot(up(_node->_date)) % _Hash->_hash.size();
				hashi++;

				while (hashi < _Hash->_hash.size())
				{
					if (_Hash->_hash[hashi])
					{
						_node = _Hash->_hash[hashi];
						break;
					}
					hashi++;
				}

				if (hashi == _Hash->_hash.size())
					_node = nullptr;
			}
			return *this;
		}

	};

3.插入:

1.bool返回的插入:

1.正常插入:通过key计算下标值。
2.当前Hash[hashi]中已经有数据进行头插操作。
3.当前Hash[hashi]中没有有数据就修改hash[hashi]值。
4.负载因子到1就需要进行扩容操作,负载因子=单链表个数/hash.size()
5.创建一个新的大小为当前哈希表的两倍,遍历原来的链表有key求下标的方法重新进行原来数据的移动,节约了时间,结尾和_node进行哈希表的交换。

bool insert(const T& x)
		{

			copy kot;
			trans up;

			//1.调find函数去查一下当前要插入的数据是否已经存在
			if (find(kot(x)))
				return false;

			//1.计算平衡因子:
			if (_num == _hash.size())
			{
				vector<Node*> newhash(_hash.size() * 2, nullptr);
				for (int i = 0; i < _hash.size(); i++)
				{
					Node* cur = _hash[i];
					while (cur)
					{
						Node* next = cur->_next;
						// 头插到新表
						size_t hashi = up(kot(cur->_date)) % (_hash.size() * 2);
						cur->_next = newhash[hashi];
						newhash[hashi] = cur;

						cur = next;
					}

					_hash[i] = nullptr;
				}
				_hash.swap(newhash);
			}
			else
			{
				//1.正常插入
				size_t hashi = up(kot(x)) % _hash.size();
				Node* newnode = new Node(x);

				//1-1:_hash[hashi]==nullptr 直接插入节点:
				newnode->_next = _hash[hashi];
				_hash[hashi] = newnode;
				_num++;

				return true;
			}
			return false;
		}

		Node* find(const k& fd)
		{
			trans up;
			copy kot;

			size_t i = up(fd) % _hash.size();
			Node* cur = _hash[i];
			while (cur)
			{
				if ((up(kot(cur->_date))) == up(fd))
					return cur;
				cur = cur->_next;
			}

			return nullptr;
		}
2.重载operator[]实现的重载unordered_map独有:

1,重载operator[]一定需要pair<iterator,bool>的插入返回。
2.operator[]不存在就插入,存在就返回value的引用可以进行修改。
3.ret.second 为false表示已经插入过对应的key值。
4.ret.second 为true表示没有插入过对应的key值这一次刚刚插入数据。
5.优化了一个find的查找返回迭代器类型的数据方便pair<iterator,bool>返回。

pair <_iterator, bool> Insert(const T& x)
		{
			copy kot;
			trans up;

			//1.调find函数去查一下当前要插入的数据是否已经存在
			if (Find(kot(x))._node)
				return make_pair(Find(kot(x)),false);

			//1.计算平衡因子:
			if (_num == _hash.size())
			{
				vector<Node*> newhash(_hash.size() * 2, nullptr);
				for (int i = 0; i < _hash.size(); i++)
				{
					Node* cur = _hash[i];
					while (cur)
					{
						Node* next = cur->_next;
						// 头插到新表
						size_t hashi = up(kot(cur->_date)) % (_hash.size() * 2);
						cur->_next = newhash[hashi];
						newhash[hashi] = cur;

						cur = next;
					}

					_hash[i] = nullptr;
				}
				_hash.swap(newhash);
			}
			else
			{
				//1.正常插入
				size_t hashi = up(kot(x)) % _hash.size();
				Node* newnode = new Node(x);

				//1-1:_hash[hashi]==nullptr 直接插入节点:
				newnode->_next = _hash[hashi];
				_hash[hashi] = newnode;
				_num++;

				return make_pair(_iterator(this,newnode), true);
			}
			return make_pair(_iterator(this,nullptr),false);
		}
		_iterator Find(const k& fd)
		{
			trans up;
			copy kot;

			size_t i = up(fd) % _hash.size();
			Node* cur = _hash[i];
			while (cur)
			{
				if ((up(kot(cur->_date))) == up(fd))
					return _iterator(this, cur);
				cur = cur->_next;
			}

			return _iterator(this,nullptr);
		}
#pragma once

#include<iostream>
#include<string>
#include<vector>

using namespace std;

template<class T>
struct transition
{
	size_t operator()(const T& x)
	{
		return x;
	}
};

//1.string -> string
template<>
struct transition<string>
{
	size_t operator()(const string& x)
	{
		//1.可以计算string字符串的asia码的和并且每次*131降低哈希冲突:
		size_t sum = 0;
		for (auto& e : x)
		{
			sum += (e * 131);
		}
		return sum;
	}
};

namespace Hash_bucket
{
	template<class T>
	struct Hash_Node {
		typedef Hash_Node Node;
		Hash_Node(T x = T())
			:_date(x)
			,_next(nullptr)
		{}

		T _date;
		Node* _next;
	};

	//T() ---> int
	//T() ---> pair<int,int> pair类型:

	//哈希表和迭代器相互包含需要声明迭代器到哈希表的前面:
	//类模板的声明需要模板+struct/class + 类名:

	template<class k, class T, class copy, class trans>
	struct unorderediterator;

	template<class k , class T, class copy, class trans>
	class Hash {

		template<class K, class T, class copy, class Hash>
		friend struct unorderediterator;

	public:
		typedef Hash_Node<T> Node;
		typedef unorderediterator<k, T, copy, trans> _iterator;

		Hash(size_t n = 10)
		{
			_hash.resize(n,nullptr);
			_num = 0;
		}

		pair <_iterator, bool> Insert(const T& x)
		{
			copy kot;
			trans up;

			//1.调find函数去查一下当前要插入的数据是否已经存在
			if (Find(kot(x))._node)
				return make_pair(Find(kot(x)),false);

			//1.计算平衡因子:
			if (_num == _hash.size())
			{
				vector<Node*> newhash(_hash.size() * 2, nullptr);
				for (int i = 0; i < _hash.size(); i++)
				{
					Node* cur = _hash[i];
					while (cur)
					{
						Node* next = cur->_next;
						// 头插到新表
						size_t hashi = up(kot(cur->_date)) % (_hash.size() * 2);
						cur->_next = newhash[hashi];
						newhash[hashi] = cur;

						cur = next;
					}

					_hash[i] = nullptr;
				}
				_hash.swap(newhash);
			}
			else
			{
				//1.正常插入
				size_t hashi = up(kot(x)) % _hash.size();
				Node* newnode = new Node(x);

				//1-1:_hash[hashi]==nullptr 直接插入节点:
				newnode->_next = _hash[hashi];
				_hash[hashi] = newnode;
				_num++;

				return make_pair(_iterator(this,newnode), true);
			}
			return make_pair(_iterator(this,nullptr),false);
		}
		_iterator Find(const k& fd)
		{
			trans up;
			copy kot;

			size_t i = up(fd) % _hash.size();
			Node* cur = _hash[i];
			while (cur)
			{
				if ((up(kot(cur->_date))) == up(fd))
					return _iterator(this, cur);
				cur = cur->_next;
			}

			return _iterator(this,nullptr);
		}


		bool insert(const T& x)
		{

			copy kot;
			trans up;

			//1.调find函数去查一下当前要插入的数据是否已经存在
			if (find(kot(x)))
				return false;

			//1.计算平衡因子:
			if (_num == _hash.size())
			{
				vector<Node*> newhash(_hash.size() * 2, nullptr);
				for (int i = 0; i < _hash.size(); i++)
				{
					Node* cur = _hash[i];
					while (cur)
					{
						Node* next = cur->_next;
						// 头插到新表
						size_t hashi = up(kot(cur->_date)) % (_hash.size() * 2);
						cur->_next = newhash[hashi];
						newhash[hashi] = cur;

						cur = next;
					}

					_hash[i] = nullptr;
				}
				_hash.swap(newhash);
			}
			else
			{
				//1.正常插入
				size_t hashi = up(kot(x)) % _hash.size();
				Node* newnode = new Node(x);

				//1-1:_hash[hashi]==nullptr 直接插入节点:
				newnode->_next = _hash[hashi];
				_hash[hashi] = newnode;
				_num++;

				return true;
			}
			return false;
		}

		Node* find(const k& fd)
		{
			trans up;
			copy kot;

			size_t i = up(fd) % _hash.size();
			Node* cur = _hash[i];
			while (cur)
			{
				if ((up(kot(cur->_date))) == up(fd))
					return cur;
				cur = cur->_next;
			}

			return nullptr;
		}

		bool erase(const T& fd)
		{
			trans kot;
			copy up;

			size_t i = kot(up(fd)) % _hash.size();
			Node* cur = _hash[i];
			Node* prev = nullptr;
			while (cur)
			{
				if (up(cur->_date) == fd)
				{
					if (prev == nullptr)
					{
						_hash[i] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
						
					}
					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;

			for (size_t i = 0; i < _hash.size(); i++)
			{
				Node* cur = _hash[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("load factor:%lf\n", (double)_num / _hash.size());
			//表长度:
			printf("all bucketSize:%d\n",_hash.size());
			//桶的个数:
			printf("bucketSize:%d\n", bucketSize);
			//最长的桶的长度
			printf("maxBucketLen:%d\n", maxBucketLen);
			//平均桶长度
			printf("averageBucketLen:%lf\n\n", averageBucketLen);
		}

		//找开始的节点:
		_iterator find_begin()
		{
			for (int i = 0; i < _hash.size(); i++)
			{
				if (_hash[i])
				{
					return _iterator(this, _hash[i]);
				}
			}
			return  _iterator(this, nullptr);
		}

		_iterator find_end()
		{
			return  _iterator(this, nullptr);
		}

	private:
		//指针数组:
		vector<Node*> _hash;
		size_t _num;
	};


	//迭代器:
	template<class k, class T, class copy, class trans>
	struct unorderediterator
	{
		typedef Hash_Node<T> Node;
		typedef Hash<k, T, copy, trans> HT;
		typedef unorderediterator<k, T, copy, trans> self;

		HT* _Hash;
		Node* _node;

		unorderediterator(HT* hash, Node* x)
			:_Hash(hash)
			, _node(x)
		{}


		T& operator*()
		{
			return _node->_date;
		}

		T* operator->()
		{
			return &(_node->_date);
		}

		bool operator!=(const self& bitter)//self* bitter
		{
			//比较哈希表中的vector不是iterator?
			//迭代器==哈希+节点 比较迭代器的地址还是节点的地址?
			if (this->_node == bitter._node)
				return false;

			return true;
		}

		//主要是要去找节点:
		self operator++()
		{
			trans kot;
			copy up;

			//1.情况一:当前节点有下一个节点:
			if (_node->_next != nullptr)
				_node = _node->_next;
			//2.哈希位置的跳转:
			else
			{
				size_t hashi = kot(up(_node->_date)) % _Hash->_hash.size();
				hashi++;

				while (hashi < _Hash->_hash.size())
				{
					if (_Hash->_hash[hashi])
					{
						_node = _Hash->_hash[hashi];
						break;
					}
					hashi++;
				}

				if (hashi == _Hash->_hash.size())
					_node = nullptr;
			}
			return *this;
		}
	};
}

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

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

相关文章

Jackson 2.x 系列【3】解析器 JsonParser

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-seata-demo 文章目录 1. 前言2. 解析原理3. 案例演示3.1 创建 JsonParser3.2 解析3.3 读取3.4 测试 1. 前…

流畅的 Python 第二版(GPT 重译)(三)

第五章&#xff1a;数据类构建器 数据类就像孩子一样。它们作为一个起点是可以的&#xff0c;但要作为一个成熟的对象参与&#xff0c;它们需要承担一些责任。 马丁福勒和肯特贝克 Python 提供了几种构建简单类的方法&#xff0c;这些类只是一组字段&#xff0c;几乎没有额外功…

隐私计算实训营学习一:数据可信流通,从运维信任到技术信任

文章目录 一、数据可信流通二、数据可信流通的技术信任基础三、技术信任开启数据密态时代&#xff0c;保障广域数据可信流通 一、数据可信流通 可信数据流通体系&#xff1a;数据二十条第一次明确提出可信流通&#xff0c;建立数据来源可确认、使用范围可界定、流通过程可追溯…

【数据可视化】Echarts中的其它图表

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 前言2. 绘制散点图2.1 绘制基本散点图2.2 绘制两个序列的散点图2.3 绘制带涟漪特效的散点图 3. 绘制气泡图3.1 绘制标准气泡图3.2 绘制各国人均寿命与GDP气泡图3.3 绘制城市A、城市B、城市C三个城市空气污染指数气…

智慧公园:AI智能分析网关V4城市公园视频智能监管方案

一、背景分析 随着天气渐渐转暖&#xff0c;城市公园的花卉也逐渐盛开&#xff0c;春暖花开时节&#xff0c;前往公园赏花游玩的城市居民也渐渐多起来&#xff0c;因此安全问题也成为相关监管部门的重要管理任务之一。随着科技的不断进步&#xff0c;智能监控技术已经成为现代…

使用paho.mqtt.client实现MQTT Client连接EMQX Broker

目录 概述 1 认识paho.mqtt.client 2 实现MQTT Client 2.1 功能介绍 2.2 paho.mqtt.client库函数介绍 2.3 MQTT Client实现 2.3.1 创建项目 2.3.2 编写MQTT Client代码 2.3.3 Log工具源码 2.4 功能测试代码实现 2.4.1 功能介绍 2.4.2 代码实现 3 测试 3.1 EMQX上创…

回归预测 | Matlab基于SAO-LSTM雪消融算法优化长短期记忆神经网络的数据多输入单输出回归预测

回归预测 | Matlab基于SAO-LSTM雪消融算法优化长短期记忆神经网络的数据多输入单输出回归预测 目录 回归预测 | Matlab基于SAO-LSTM雪消融算法优化长短期记忆神经网络的数据多输入单输出回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab基于SAO-LSTM雪消融…

课时70:流程控制_for循环_嵌套循环

2.4.4 嵌套循环 学习目标 这一节&#xff0c;我们从 基础知识、简单实践、小结 三个方面来学习。 基础知识 简介 这里的嵌套实践&#xff0c;与选择语句的嵌套实践基本一致&#xff0c;只不过组合的方式发生了一些变化。常见的组合样式如下&#xff1a;for嵌套for语句for …

[STM32] Keil MDK 新建工程编译不通过(warning: #2803-D和Error: L6218E)解决方法备忘

按照野火的PDF教程的第4章&#xff1a;[野火]《RT-Thread 内核实现与应用开发实战—基于STM32》.pdf 新建 Keil MDK 工程&#xff0c;工程设置完成后点击编译按钮&#xff0c;编译不通过&#xff1a; RTE\Device\ARMCM3\startup_ARMCM3.c(75): warning: #2803-D: unrecognize…

nodejs社区垃圾分类管理平台的设计与实现python-flask-django-php

近些年来&#xff0c;随着科技的飞速发展&#xff0c;互联网的普及逐渐延伸到各行各业中&#xff0c;给人们生活带来了十分的便利&#xff0c;社区垃圾分类管理平台利用计算机网络实现信息化管理&#xff0c;使整个社区垃圾分类管理的发展和服务水平有显著提升。 语言&#xf…

Hash类型

2.3.Hash类型 Hash类型&#xff0c;也叫散列&#xff0c;其value是一个无序字典&#xff0c;类似于Java中的HashMap结构。 String结构是将对象序列化为JSON字符串后存储&#xff0c;当需要修改对象某个字段时很不方便&#xff1a; Hash结构可以将对象中的每个字段独立存储&am…

【Qt】使用Qt实现Web服务器(三):QtWebApp中HttpRequest和HttpResponse

1、HttpRequest 1.1 示例 1)在Demo1的Dump HTTP request示例 在浏览器中输入http://127.0.0.1:8080点击Dump HTTP request 2)切换到页面:http://127.0.0.1:8080/dump 该页面显示请求和响应的内容: Request: Method: GET Path: /dump Version: HTTP/1.1 Headers: accep…

图片编辑器中实现文件上传的三种方式和二进制流及文件头校验文件类型

背景 最近在 vue-design-editor 开源项目中实现 psd 等多种文件格式上传解析成模板过程中, 发现搞定设计文件上传没有使用 input 实现文件上传, 所以我研究了一下相关技术, 总结了以下三种文件上传方法 input 文件选择window.showOpenFilePicker 和 window.showDirectoryPicke…

树莓派夜视摄像头拍摄红外LED灯

NoIR相机是一种特殊类型的红外摄像头&#xff0c;其名称来源于"No Infrared"的缩写。与普通的彩色摄像头不同&#xff0c;NoIR相机具备红外摄影和低光条件下摄影的能力。 一般摄像头能够感知可见光&#xff0c;并用于普通摄影和视频拍摄。而NoIR相机则在设计上去除了…

【Hadoop】Hadoop 编译源码

目录 为什么要源码编译Hadoop 编译源码1前期工作准备2jar 包安装2.1安装 Maven2.2安装 ant2.3安装 glibc-headers 和 g2.4安装 make 和 cmake2.5安装 protobuf2.6安装 openssl 库2.7安装 ncurses-devel 库 3编译源码3.1解压源码到 /opt/ 目录3.2 进入到 hadoop 源码主目录 /opt…

Redis持久化笔记(3)

redis持久化&#xff1a;把内存的数据存放到磁盘&#xff0c;避免因为断电等导致数据丢失。 RDB&#xff08;Redis Database&#xff09; rdb就是在一定时间间隔内把当时的数据和状态保存为 .rdb文件放在磁盘中。 自动触发设置&#xff1a;在redis.conf 修改.rdb文件的保存位…

【Vue】三、使用ElementUI实现图片上传

目录 一、前端代码实现 二、后端代码实现 三、调试效果实现 一、前端代码实现 废话不多说直接上代码 <el-form-item prop"image" label"上传图片" v-model"form.image"><el-upload:action"http://localhost:8…

【prometheus-operator】k8s监控集群外redis

1、部署exporter GitHub - oliver006/redis_exporter: Prometheus Exporter for Redis Metrics. Supports Redis 2.x, 3.x, 4.x, 5.x, 6.x, and 7.x redis_exporter-v1.57.0.linux-386.tar.gz # 解压 tar -zxvf redis_exporter-v1.57.0.linux-386.tar.gz # 启动 nohup ./redi…

Go——指针和内存逃逸

区别于C/C中的指针&#xff0c;Go语言中的指针不能进行偏移和运算&#xff0c;是安全指针。 要搞明白Go语言中的指针概念需要先知道3个概念&#xff1a;指针地址&#xff0c;指针类型和指针取值。 一. Go语言的指针 Go语言中的函数传参都是值拷贝&#xff0c;当我们想修改某个…

# Django通过开关控制数据库参数(JS版)

目录 场景初始的视图层HTML部分JS代码视图层接受部分 场景 此时我的表单中有一排开关 数据库有一排状态 需求是要当开关开启时数据库state为1&#xff0c;关闭时为0 初始的视图层 将整个adv数据表返回给前端HTML def adv(request):adv_list Adv.objects.all()return rende…