C++-封装unordered

本期我们来封装实现unordered系列,需要前置知识,没有看过哈希的建议先看看哈希,而且哈希的代码都在这里面,一会要用到

C++-哈希Hash-CSDN博客

目录

代码实现

迭代器

const迭代器

全部代码


代码实现

 首先我们要把V改为T,因为我们不知道他是k结构还是kv结构

//unordered_set.h
namespace bai {
	template<class K>
	class unordered_set
	{
	private:
		hash_bucket::HashTable<K, K> _ht;
	};
}
namespace bai {
	template<class K,class V>
	class unordered_map
	{
	private:
		hash_bucket::HashTable<K,pair<K,V>> _ht;
	};
}

然后我们把框架搭好

我们对照着看一下,如果是set,这里就是key,data就是key,如果是map,这里就是pair,data就是pair

接下来还要修改insert,find等等,这里就因为不知道是pair还是k的原因,所以我们需要再次来写KeyOfT

        struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

		struct MapKeyOfT
		{
			const K& operator()(const pair<K,V>& kv)
			{
				return kv.first;
			}
		};

写完后别忘记加上 

bool Insert(const T& data)
		{
			KeyOfT kot;
			if (Find(kot(data)))
			{
				return false;
			}
			HashFunc hf;
			//负载因子到1时扩容
			if (_n == _table.size())
			{
				size_t newSize = _table.size() * 2;
				vector<Node*> newTable;
				newTable.resize(newSize, nullptr);
				//遍历旧表,把节点迁下来挂到新表
				for (size_t i = 0; i < _table.size(); i++)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->_next;
						//头插到新表
						size_t hashi = hf(kot(cur->_data)) % newSize;
						cur->_next = newTable[hashi];
						newTable[hashi] = cur;
						cur = next;
					}
					_table[i] = nullptr;
				}
				_table.swap(newTable);
			}
			size_t hashi = hf(kot(data)) % _table.size();
			//头插
			Node* newnode = new Node(data);
			newnode->_next = _table[hashi];
			_table[hashi] = newnode;
			++_n;
			return true;
		}

		Node* Find(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return cur;
				}
				cur = cur->_next;
			}
			return nullptr;
		}

		bool Erase(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* prev = nullptr;
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr)
					{
						//头删
						_table[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}
					delete cur;
					return true;
				}
				cur = cur->_next;
			}
		}

接着我们要修改insert,find和erase的代码,下面我们来看看修改了哪里,我们按顺序看

大概就是这些地方,具体的还要看上面的代码

我们简单测试一下,没有问题,下面我们来封装迭代器

迭代器

要实现迭代器,我们要想想,当前桶走完了,如何到下一个桶呢?也就是2号位的桶完了后如何到6号位

template<class T>
	struct HTiterator
	{
		typedef HashNode<T> Node;
		Node* _node;

		HTiterator(Node* node)
			:_node(node)
		{}

		operator++()
		{
			if (_node->_next)
			{
				_node = _node->next;
			}
			else
			{

			}
		}
	};

我们先把框架写好,我们来看operator++,如果当前桶没有走完,就让他到下一个位置即可

如果我们知道当前位置是几号桶可以解决吗?也不行,因为我们还需要哈希表的对象,大家想一想,他的底层是一个指针数组,每一个指针又指向一个单链表,我们现在是需要在指针数组里移动

    template<class K, class T, class KeyOfT, class HashFunc>
	struct HTiterator
	{
		typedef HashNode<T> Node;
		typedef HTiterator<K, T, KeyOfT, HashFunc> Self;

		Node* _node;
		HashTable<K, T, KeyOfT, HashFunc>* _pht;
		HTiterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			,_pht(pht)
		{}

		Self& operator++()
		{
			if (_node->_next)
			{
				//当前桶还没完
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				//从下一个位置开始查找下一个不为空的桶
				++hashi;
				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					else
					{
						++hashi;
					}
				}
				_node = nullptr;
			}
			return *this;
		}
	};

所以我们需要把整个哈希表传过来,传一个pht的指针,而且这里和之前一样,不一定是可以直接取模,需要再套一层hashfunc,此时我们就可以计算出当前是第几个桶

接着我们开始找下一个不为空的桶,我们先对hashi++,从下一个位置开始寻找,如果当前桶不为空,我们就break,否则++hashi,循环结束后是有两种情况的,一种是可以找到,我们要让it指向下一个桶,另一种是我们已经找完了整个表,已经走到结尾了,我们让nullptr充当end的位置

另外这里可能会有人有疑问,为什么这里没有加typename? 只有不能区分静态变量和类型时才需要加

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

		T* operator->()
		{
			return &_node->_data;
		}        
        bool operator!=(const Self& s)
		{
			return _node != s._node;
		}
        bool operator==(const Self& s)
		{
			return _node == s._node;
		}

我们顺手再把这几个也写一下

        typedef HTiterator<K, T, KeyOfT, HashFunc> iterator;
		iterator begin()
		{
			//找第一个桶
			for (size_t i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				if (cur)
				{
					return iterator(cur,this);
				}
			}
			return iterator(nullptr, this);
		}
		iterator end()
		{
			return iterator(nullptr, this);
		}

接着我们在HashTable里加上begin和end,我们构造迭代器时第一个传cur,找不到传nullptr,第二个传this即可,this就是哈希表的指针

下面我们再套一层,在unordered里封装一层

注意,这里就需要加typename了,我们之前封装map和set时也是一样的

下面我们要开始解决各种问题了 

这里是一个相互依赖的问题

首先我们在哈希表里调用迭代器 ,迭代器在前,没有问题

但是我们在迭代器里又用了哈希表,这里谁在前谁在后都不行,不过还好这里用的是哈希表的指针,如果是对象就不好解决了

这里我们就要用到前置声明了,告诉编译器哈希表我们是有定义的,只不过可能在后面,先不要着急

 

此时问题就解决了 

我们调用一下迭代器,再次编译,出现了这样的错误,我们仔细看可以发现,这里是私有的问题

这里在类外边是不能访问私有的 ,所以这里可以写一个gettable,或者使用友元

是迭代器要访问哈希表,所以迭代器是哈希表的友元,类模板的友元需要带上模板参数 

此时我们的迭代器就可以跑起来了 

接下来我们需要解决可以修改的问题,也就是要实现const迭代器了

const迭代器

和之前一样,要实现const迭代器我们需要传T*和T&

于是我们修改迭代器的模板参数和self 

operator*和箭头的返回参数也要修改

友元声明也需要修改 

        const_iterator begin() const
		{
			//找第一个桶
			for (size_t i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				if (cur)
				{
					return const_iterator(cur, this);
				}
			}
			return const_iterator(nullptr, this);
		}
		const_iterator end() const
		{
			return const_iterator(nullptr, this);
		}

然后要在HashTable里加上begin和end

接着我们对外层的set封装,无论是iterator还是const_iterator,它们其实都是const_iterator,而且只提供了const版本的begin和end,和库里面是一样的,这里的begin和end的返回值无论是const_iterator还是iterator都是可以的,因为普通对象是可以调用const成员函数的

此时我们的第一个问题就解决了

接着我们编译,发现这里编译不通过,不仅仅是这里,上面也是编译不通过的,这里报错是无构造函数可以接受源类型

我们来看,这里的const修饰的谁?

修饰的是*this

所以这里this的类型应该是这样的

我们画出来就是这样的,这里权限放大,是传不过去的 ,我们之前写的const迭代器没有遇到这样的问题,因为以前的const迭代器是不需要传哈希表的,而只需要传一个节点的指针过去即可

我们来看这种方法,我们重载了一个构造函数,并且直接把_pht改为了const

 对于原来的构造函数,pht是const变为非const,是权限的缩小

那此时我们不要第一个构造,只要第二个可以吗?

也是可以的,普通迭代器传过来只有const,权限缩小

我们来看库里面是怎么解决的,首先是普通的迭代器 

这里有一个

 库里面写了两个迭代器,用两个迭代器类来实现

这里还都写成const了,相当于单独写了一个类来解决

我们的方法和库里面的方法,这两种方法都可以解决

map的话和以前一样,改为const K

 修改这些地方

 这样就解决了

下面我们来修改返回值

先修改一下find的返回值

然后是insert的 

然后我们把map和set封装的insert也改一下,然后就是set这里的问题了

这里看起来是这样的

但是我们看这里

 但是它们是不同区域的代码

 set不允许修改key,所以选择用const迭代器替代普通迭代器

所以这里的两个pair是不一样的,pair是一个类模板,实例化为两个不一样的类,是不一样的类型

也就是说上面的pair是这样的,所以会报错

map为什么没报错?

 因为map的iterator就是iterator

注意,set这里的问题和权限缩小之类无关,只有指针和引用才有权限缩小的概念,这里是类模板实例化不同的模板参数,导致它们是不同的类型

我们之前在封装map和set就讲过了,这是库里面list的实现办法,不太清楚的各位可以去我往期的map和set实现里再看一看,我把链接放在下面

map和set模拟实现-CSDN博客

下面这个圈主的函数,如果是普通迭代器,这里就是拷贝构造,是const迭代器时,是构造函数,是支持普通迭代器转换成const迭代器的构造,这是一个有双重意义的函数

我们来用这个举例子,我们想用aa1构造aa3

我们要加这样一个构造,但是这里报错了一个私有

原因是上面这里是A<T,Ref>

这里是A<T,T&>

当这里Ref也是T&时是同一个类,当Ref是const时就不是同一个类了

我们看为什么库里面不报错呢?

因为库里面是struct

我们去掉private就好了,这些都是细节,大家需要注意

现在回到我们的哈希表里

我们也加上这样一个构造

接着我们再次编译,发现了几个错误

首先是我们把这里改为it(这里之前是写错了)

然后是find返回时不能返回nullptr,这里返回end(),接着编译就可以通过了

我们上次在这里写的和这里是不一样的

这里我们是先接收pair,再用pair去调用它的构造

这样写是没问题的,这里相当于把两个参数提取了出来,单独调用pair构造,这里的ret.first是一个普通迭代器,在pair的初始化列表里,初始化列表对自定义类型会调用构造

而这里屏蔽的(我们之前写的),是编译器的不严格检查,如果检查的严格一点,是不通过的

首先这里调用pair的构造

我们上面的a<int>和a<const int>都不是同一个类型,这里也是一样的

所以我们把iterator取出来

 然后直接调用它的构造,就会走它的初始化列表,初始化列表对于自定义类型就会调用它的构造

虽然这里两种方法都可以编译通过,但是我们还是建议使用方法2,不然换一个编译器可能就不通过了 

此时我们的问题就解决了

我们上面那么多事情,都是因为它 

insert要返回pair,就引发了普通迭代器转换const迭代器的问题,为什么insert要返回pair呢?

因为我们要实现operator[ ] ,这里都是相关联的

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

为什么我们这里接收时可以用iterator?

因为map的iterator就是iterator,和set不一样

此时我们就可以使用[ ] 了,和库里面一样

全部代码

#include"Hash.h"
//unordered_set.h
namespace bai {
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;

		const_iterator begin() const
		{
			return _ht.begin();
		}
		const_iterator end() const
		{
			return _ht.end();
		}
		pair<const_iterator, bool> insert(const K& key)
		{
			//return _ht.Insert(key);
			pair<typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> ret = _ht.Insert(key);
			return pair<const_iterator, bool>(ret.first, ret.second);
		}
		
	private:
		hash_bucket::HashTable<K, K, SetKeyOfT> _ht;
	};
}
#include"Hash.h"
//unordered_map.h
namespace bai {
	template<class K,class V>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<const K,V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::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<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:
		hash_bucket::HashTable<K, pair<const K,V>, MapKeyOfT> _ht;
	};
}
#pragma once
#include<vector>
#include<iostream>
using namespace std;
//Hash.h
template<class K>
struct DefaultHashFunc
{
	size_t operator()(const K& key)
	{
		return (size_t)key;
	}
};

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

namespace open_address {
	enum STATE //状态
	{
		EXIST,
		EMPTY,
		DELETE
	};

	template<class K, class V>
	struct HashData
	{
		pair<K, V> _kv;
		STATE _state = EMPTY;
	};

	//struct stringHashFunc
	//{
	//	size_t operator()(const string& str)
	//	{
	//		return str[0];
	//	}
	//};

	template<class K, class V, class HashFunc = DefaultHashFunc<K>>
	class HashTable
	{
	public:
		HashTable()
		{
			_table.resize(10);
		}
		bool Insert(const pair<K, V>& kv)
		{
			if(Find(kv.first))
			{
				return false;
			}
			//扩容
			//if ((double)_n / _table.size() >= 0.7)
			if (_n * 10 / _table.size() >= 7)
			{
				size_t newSize = _table.size() * 2;
				//重新映射
				HashTable<K, V, HashFunc> newHT;
				newHT._table.resize(newSize);
				for (size_t i = 0; i < _table.size(); i++)
				{
					if (_table[i]._state == EXIST)
					{
						newHT.Insert(_table[i]._kv);
					}
				}
				_table.swap(newHT._table);
			}
			//线性探测
			HashFunc hf;
			size_t hashi = hf(kv.first) % _table.size();
			while (_table[hashi]._state == EXIST)
			{
				++hashi;
				hashi %= _table.size();//防止越界,这样可以回到数组开头
			}
			_table[hashi]._kv = kv;
			_table[hashi]._state = EXIST;
			++_n;
			return true;
		}
		HashData<const K, V>* Find(const K& key)
		{
			//线性探测
			HashFunc hf;
			size_t hashi = hf(key) % _table.size();
			while (_table[hashi]._state != EMPTY)
			{
				if (_table[hashi]._state == EXIST
					&& _table[hashi]._kv.first == key)
				{
					return (HashData<const K, V>*) & _table[hashi];
				}
				++hashi;
				hashi %= _table.size();
			}
			return nullptr;
		}
		bool Erase(const K& key)
		{
			HashData<const K, V>* ret = Find(key);
			if (ret)
			{
				ret->_state = DELETE;
				--_n;
				return true;
			}
			return false;
		}
	private:
		vector<HashData<K, V>> _table;
		size_t _n; //存储有效数据的个数
	};
}

namespace hash_bucket
{
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next;
		HashNode(const T& data)
			:_data(data)
			,_next(nullptr)
		{}
	};

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

	template<class K, class T, class Ptr, class Ref,class KeyOfT, class HashFunc>
	struct HTiterator
	{
		typedef HashNode<T> Node;
		typedef HTiterator<K, T, Ptr, Ref, KeyOfT, HashFunc> Self;
		typedef HTiterator<K, T, T*, T&, KeyOfT, HashFunc> Iterator;

		Node* _node;
		const HashTable<K, T, KeyOfT, HashFunc>* _pht;

		/*HTiterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			,_pht(pht)
		{}*/

		HTiterator(Node* node,const HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			, _pht(pht)
		{}

		//普通迭代器时是拷贝构造
		//cosnt迭代器时是构造
		HTiterator(const Iterator& it)
			:_node(it._node)
			, _pht(it._pht)
		{}

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

		Ptr operator->()
		{
			return &_node->_data;
		}
		Self& operator++()
		{
			if (_node->_next)
			{
				//当前桶还没完
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				//从下一个位置开始查找下一个不为空的桶
				++hashi;
				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					else
					{
						++hashi;
					}
				}
				_node = nullptr;
			}
			return *this;
		}

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

	//set -> hash_bucket::HashTable<K, K> _ht;
	//map -> hash_bucket::HashTable<K, pair<K,V>> _ht;
	template<class K,class T,class KeyOfT, class HashFunc = DefaultHashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;

		//友元声明
		template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc >
		friend struct HTiterator;
	public:
		typedef HTiterator<K, T,T*,T&, KeyOfT, HashFunc> iterator;
		typedef	HTiterator<K, T,const T*,const T&, KeyOfT, HashFunc> const_iterator;
		
		iterator begin()
		{
			//找第一个桶
			for (size_t i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				if (cur)
				{
					return iterator(cur,this);
				}
			}
			return iterator(nullptr, this);
		}
		iterator end()
		{
			return iterator(nullptr, this);
		}

		const_iterator begin() const
		{
			//找第一个桶
			for (size_t i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				if (cur)
				{
					return const_iterator(cur, this);
				}
			}
			return const_iterator(nullptr, this);
		}
		const_iterator end() const
		{
			return const_iterator(nullptr, this);
		}

		HashTable()
		{
			_table.resize(10,nullptr);
		}

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

		pair<iterator,bool> Insert(const T& data)
		{
			KeyOfT kot;
			iterator it = Find(kot(data));
			if (it!=end())
			{
				return make_pair(it,false);
			}
			HashFunc hf;
			//负载因子到1时扩容
			if (_n == _table.size())
			{
				size_t newSize = _table.size() * 2;
				vector<Node*> newTable;
				newTable.resize(newSize, nullptr);
				//遍历旧表,把节点迁下来挂到新表
				for (size_t i = 0; i < _table.size(); i++)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->_next;
						//头插到新表
						size_t hashi = hf(kot(cur->_data)) % newSize;
						cur->_next = newTable[hashi];
						newTable[hashi] = cur;
						cur = next;
					}
					_table[i] = nullptr;
				}
				_table.swap(newTable);
			}
			size_t hashi = hf(kot(data)) % _table.size();
			//头插
			Node* newnode = new Node(data);
			newnode->_next = _table[hashi];
			_table[hashi] = newnode;
			++_n;
			return make_pair(iterator(newnode,this),true);
		}

		iterator Find(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur,this);
				}
				cur = cur->_next;
			}
			return end();
		}

		bool Erase(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* prev = nullptr;
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev == nullptr)
					{
						//头删
						_table[hashi] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}
					delete cur;
					return true;
				}
				prev = cur;
				cur = cur->_next;
			}
			--_n;
			return false;
		}

		void Print()
		{
			for (size_t i = 0; i < _table.size(); i++)
			{
				printf("[%d]->", i);
				Node* cur = _table[i];
				while (cur)
				{
					cout << cur->_kv.first << ":" <<cur->_kv.second <<"->";
					cur = cur->_next;
				}
				printf("NULL\n");
			}
			cout << endl;
		}
	private:
		vector<Node*> _table; //指针数组
		size_t _n = 0; //有效数据
	};
}

以上即为本期全部内容,希望大家可以有所收获

如有错误,还请指正

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值