_cpp利用哈希封装实现unordered_map和unordered_set

文章目录

  • 0. 前言
  • 1. 改造哈希表
    • 1.1 哈希表节点的定义
    • 1.2 哈希表中的迭代器
      • 1.2.1 模拟实现后置加加的方法
      • 1.2.2 哈希表迭代器代码
    • 1.3 仿函数
    • 1.4 哈希表整体改造完成后的代码
  • 2. 封装实现unordered_map
  • 3. 封装实现unordered_set
  • 4. 测试案例
  • 5. 测试结果

0. 前言

1. 改造哈希表

链接:c++哈希(哈希表开散列实现的哈希表)
上面那篇文章,我们了解了哈希表的底层结构并模拟实现哈希表数据插入和删除。

这篇文章我们利用哈希表装实现unordered_map和unordered_set。

1.1 哈希表节点的定义

  • 因为我们要利用一颗树封装实现unordered_map和unordered_set,所以我们不能写死。

  • 怎么做?那看下面代码。

	template<class T>
	struct HashNode
	{
		//pair _kv;
		T _data;	//数据
		HashNode<T>* _next;

		HashNode(const T& data)
			:_data(data)
			,_next(nullptr)
		{}
	};

  • 我们把模板template改为template; 而T代表k or pair。这样就实现了泛型。

  • 看到这里uu们就很疑惑,那怎么区别那?我们就要借助仿函数(KeyOfT )-> 支持取出T对象中key的值。

1.2 哈希表中的迭代器

  • 迭代器大致框架跟利用红黑树封装map和set相同;区别我们上一篇文章实现的哈希表每一个位置是一个桶(单链表);我们只用模拟实现后置加加操作即可。

1.2.1 模拟实现后置加加的方法

  • 思路:加加分为俩种
      1. 当前桶位置找下一个节点。
      1. 找下一个哈希桶起始位置。
		typedef __HashIterator<K, T, Hash, KeyOfT> Self;
		Self& operator++()
		{
			if (_node->_next)
			{
				// 当前桶中迭代
				_node = _node->_next;
			}
			else
			{
				// 找下一个桶
				Hash hash;
				KeyOfT kot;
				size_t i = hash(kot(_node->_data)) % _pht->_tables.size();
				++i;

				for (; i < _pht->_tables.size(); ++i)
				{
					if (_pht->_tables[i])
					{
						_node = _pht->_tables[i];
						break;
					}
				}

				// 说明后面没有有数据的桶了
				if (i == _pht->_tables.size())
				{
					_node = nullptr;
				}
			}

1.2.2 哈希表迭代器代码

  • 注意:我们实现迭代器还要访问原来哈希表类的成员(私有成员需要把迭代器变为哈希表的友元,后面代码会写)
// 前置声明
	template<class K, class T, class Hash, class KeyOfT>
	class HashTable;

	template<class K, class T, class Hash, class KeyOfT>
	struct __HashIterator
	{
		typedef HashNode<T> Node;
		typedef HashTable<K, T, Hash, KeyOfT> HT;
		typedef __HashIterator<K, T, Hash, KeyOfT> Self;

		Node* _node;
		HT* _pht;
		__HashIterator(Node* node, HT* pht)
			:_node(node)
			, _pht(pht)
		{}

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

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

		Self& operator++()
		{
			if (_node->_next)
			{
				// 当前桶中迭代
				_node = _node->_next;
			}
			else
			{
				// 找下一个桶
				Hash hash;
				KeyOfT kot;
				size_t i = hash(kot(_node->_data)) % _pht->_tables.size();
				++i;

				for (; i < _pht->_tables.size(); ++i)
				{
					if (_pht->_tables[i])
					{
						_node = _pht->_tables[i];
						break;
					}
				}

				// 说明后面没有有数据的桶了
				if (i == _pht->_tables.size())
				{
					_node = nullptr;
				}
			}

			return *this;
		}

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

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

	};

1.3 仿函数

  • 除了上章比较用的仿函数Hash = HashFunc;此章我们还需要一个区分unordered_map和unordered_set的仿函数。
  • 因为map是pair; kv型原来的直接比较并插入就失效了,我们就需要借助仿函数来提取map中pair的first值;来进行比较。
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
		struct MapKeyOfT
		{
			const K& operator()(const K& val)
			{
				return val;
			}
		};

实现过程就在上图代码里了。

1.4 哈希表整体改造完成后的代码

namespace HashBucket
{
	template<class T>
	struct HashNode
	{
		//pair _kv;
		T _data;	//数据
		HashNode<T>* _next;

		HashNode(const T& data)
			:_data(data)
			,_next(nullptr)
		{}
	};

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

	template<class K, class T, class Hash, class KeyOfT>
	struct __HashIterator
	{
		typedef HashNode<T> Node;
		typedef HashTable<K, T, Hash, KeyOfT> HT;
		typedef __HashIterator<K, T, Hash, KeyOfT> Self;

		Node* _node;
		HT* _pht;
		__HashIterator(Node* node, HT* pht)
			:_node(node)
			, _pht(pht)
		{}

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

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

		Self& operator++()
		{
			if (_node->_next)
			{
				// 当前桶中迭代
				_node = _node->_next;
			}
			else
			{
				// 找下一个桶
				Hash hash;
				KeyOfT kot;
				size_t i = hash(kot(_node->_data)) % _pht->_tables.size();
				++i;

				for (; i < _pht->_tables.size(); ++i)
				{
					if (_pht->_tables[i])
					{
						_node = _pht->_tables[i];
						break;
					}
				}

				// 说明后面没有有数据的桶了
				if (i == _pht->_tables.size())
				{
					_node = nullptr;
				}
			}

			return *this;
		}

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

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

	};

	template<class K, class T, class Hash, class KeyOfT>
	class HashTable
	{
		typedef HashNode<T> Node;
		template<class K, class T, class Hash, class KeyOfT>
		friend struct __HashIterator;
	public:
		typedef __HashIterator<K, T, Hash, KeyOfT> iterator;
		iterator begin()
		{
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				if (_tables[i])
				{
					return iterator(_tables[i], this);
				}
			}

			return end();
		}

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



		~HashTable()
		{
			for (size_t i = 0; i < _tables.size(); i++)
			{
				Node* cur = _tables[i];
				while (cur)
				{
					Node* next = cur->_next;

					//头删
					delete cur;
					cur = next;
				}
				_tables[i] = nullptr;
			}
		}

		inline size_t __stl_next_prime(size_t n)
		{
			static const size_t __stl_num_primes = 28;
			static const size_t __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 (size_t i = 0; i < __stl_num_primes; ++i)
			{
				if (__stl_prime_list[i] > n)
				{
					return __stl_prime_list[i];
				}
			}

			return -1;
		}


		pair<iterator, bool> Insert(const T& data)
		{
			KeyOfT kot;
			Hash hash;
			//去重
			iterator ret = Find(kot(data));
			if (ret != end())
			{
				return make_pair(ret, false);
			}

			//负载因子到了就扩容
			if (_tables.size() == _size)
			{
				//size_t newSize = _tables.size() == 0 ? 10 : 2 * _tables.size();
				vector<Node*> newTables;
					//newTables.resize(newSize, nullptr);
					newTables.resize(__stl_next_prime(_tables.size()), nullptr);
				// 旧表中节点移动映射新表
				for (size_t i = 0; i < _tables.size(); i++)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;

						size_t hashi = hash(kot(cur->_data)) % newTables.size();
						//头插
						cur->_next = newTables[hashi];
						newTables[hashi] = cur;

						cur = next;
					}
					_tables[i] = nullptr;
				}
				_tables.swap(newTables);
			}

			size_t hashi = hash(kot(data)) % _tables.size();
			//头插
			Node * newNode = new Node(data);
			newNode->_next = _tables[hashi];
			_tables[hashi] = newNode;
			++_size;

			return make_pair(iterator(newNode, this), false);
		}

		iterator Find(const K& key)
		{
			if (Empty())
			{
				return end();
			}

			KeyOfT kot;
			Hash hash;
			size_t hashi = hash(key) % _tables.size();
			Node* cur = _tables[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					//找到了
					return iterator(cur, this);
				}
				cur = cur->_next;
			}
			//未找到
			return end();
		}

		bool Empty() const
		{
			return _size == 0;
		}

		bool Erase(const K& key)
		{
			if (Empty())
			{
				return false;
			}
			Hash hash;
			int hashi = hash(key) % _tables.size();
			Node* cur = _tables[hashi];
			Node* prev = nullptr;
			while (cur)
			{

				if (key == kot(cur->_data))
				{
					if (prev)   //中间删
					{
						prev->_next = cur->_next;
					}
					else      //头删
					{
						_tables[hashi] = cur->_next;
					}
					delete cur;
					--_size;
					return true;
				}
				prev = cur;
				cur = cur->_next;
			}

			//未找到
			return false;
		}

		size_t Size()
		{
			return _size;
		}

		// 表的长度
		size_t TablesSize()
		{
			return _tables.size();
		}

		// 链桶的个数
		size_t BucketNum()
		{
			size_t num = 0;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				if (_tables[i])
				{
					++num;
				}
			}

			return num;
		}

		//最长桶的链长
		size_t MaxBucketLenth()
		{
			size_t maxLen = 0;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
				size_t len = 0;
				Node* cur = _tables[i];
				while (cur)
				{
					++len;
					cur = cur->_next;
				}

				//if (len > 0)
					//printf("[%d]号桶长度:%d\n", i, len);

				if (len > maxLen)
				{
					maxLen = len;
				}
			}

			return maxLen;
		}
	private:
		vector<Node*> _tables;
		size_t _size = 0;		// 存储有效数据个数
	};

}

2. 封装实现unordered_map

  • 注意:typename的使用–我们要取取类里面的类型而不是变量(例如:类的静态变量)需要说明。
#pragma once
#include "HashTable.h"

namespace Ding
{
	template<class K, class V, class Hash = HashFunc<K>>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename  HashBucket::HashTable<K, pair<K, V>, Hash, MapKeyOfT> ::iterator iterator;

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

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

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

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = _ht.Insert(make_pair(key, V()));
			return ret.first->second;
		}
	private:
		HashBucket::HashTable<K, pair<K, V>, Hash, MapKeyOfT> _ht;
	};

3. 封装实现unordered_set

  • 注意:typename的使用–我们要取取类里面的类型而不是变量(例如:类的静态变量)需要说明。
#pragma once

#include "HashTable.h"

namespace Ding
{
	template<class K, class Hash = HashFunc<K>>
	class unordered_set
	{
		struct MapKeyOfT
		{
			const K& operator()(const K& val)
			{
				return val;
			}
		};
	public:
		typedef typename  HashBucket::HashTable<K, K, Hash, MapKeyOfT> ::iterator iterator;

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

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

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

	private:
		HashBucket::HashTable<K, K, Hash, MapKeyOfT> _ht;
	};

4. 测试案例

/unordered_map/

	void test_map()
	{
		unordered_map<string, string> dict;
		dict.insert(make_pair("sort", "排序"));
		dict.insert(make_pair("right", "右边"));
		dict.insert(make_pair("left", "左边"));

		unordered_map<string, string>::iterator it = dict.begin();
		while (it != dict.end())
		{
			cout << it->first << ":" << it->second << endl;
			++it;
		}
		cout << endl;

		unordered_map<string, int> countMap;
		string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
		for (auto e : arr)
		{
			countMap[e]++;
		}

		for (auto& kv : countMap)
		{
			cout << kv.first << ":" << kv.second << endl;
		}
	}
}

/unordered_set/

	void test_set()
	{
		unordered_set<int> s;
		s.insert(2);
		s.insert(3);
		s.insert(1);
		s.insert(2);
		s.insert(5);

		unordered_set<int>::iterator it = s.begin();
		//auto it = s.begin();
		while (it != s.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}
}

5. 测试结果

_cpp利用哈希封装实现unordered_map和unordered_set_第1张图片

你可能感兴趣的:(数据结构与算法,c++,哈希算法,数据结构,散列表)