unordered_set和unordered_map模拟实现

unordered_set和unordered_map模拟实现

KV模型的哈希表代码

namespace bucket_hash
{
	template<class K, class V>
	struct HashNode
	{
		pair<K, V> _kv;
		HashNode<K, V>* _next;

		HashNode(const pair<K, V>& kv)
			:_kv(kv)
			, _next(nullptr)
		{}
	};

	size_t GetNextPrime(size_t prime)
	{
		const int PRIMECOUNT = 28;
		static 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] > prime)
				return primeList[i];
		}

		return primeList[i];
	}

	template<class K, class V, class Hash = HashFunc<K>>
	class HashTable
	{
		typedef HashNode<K, V> Node;
	public:

		// 拷贝 和 赋值 需要自己实现桶的拷贝

		~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;
			}
			_n = 0;
		}

		bool Erase(const K& key)
		{
			if (_tables.size() == 0)
			{
				return false;
			}

			Hash hf;
			// 素数
			size_t index = hf(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[index];
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					// 1、cur是头结点
					// 2、非头节点
					if (prev == nullptr)
					{
						_tables[index] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}

					delete cur;
					--_n;

					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}

			return false;
		}

		Node* Find(const K& key)
		{
			if (_tables.size() == 0)
			{
				return nullptr;
			}

			Hash hf;
			size_t index = hf(key) % _tables.size();
			Node* cur = _tables[index];
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					return cur;
				}
				else
				{
					cur = cur->_next;
				}
			}

			return nullptr;
		}

		bool Insert(const pair<K, V>& kv)
		{
			Hash hf;

			//当负载因子到1时,进行扩容
			if (_n == _tables.size())
			{
				//size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				size_t newSize = GetNextPrime(_tables.size());

				//HashTable<K, V> newHT;
				vector<Node*> newtables;
				newtables.resize(newSize, nullptr);
				for (size_t i = 0; i < _tables.size(); ++i)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;

						size_t index = hf(cur->_kv.first) % newSize;
						cur->_next = newtables[index];
						newtables[index] = cur;

						cur = next;
					}
					_tables[i] = nullptr;
				}

				newtables.swap(_tables);
			}

			size_t index = hf(kv.first) % _tables.size();
			Node* cur = _tables[index];
			while (cur)
			{
				if (cur->_kv.first == kv.first)
				{
					return false;
				}
				else
				{
					cur = cur->_next;
				}
			}

			Node* newnode = new Node(kv);
			newnode->_next = _tables[index];
			_tables[index] = newnode;

			++_n;

			return true;
		}

	private:
		vector<Node*> _tables;
		size_t _n = 0; // 存储多少有效数据
	};
}

我们以KV模型的哈希表进行改造实现unordered_set和unordered_map:

哈希表的改造

模板参数的改造

template<class K, class T, class KeyOfT, class Hash = HashFunc<T> >
class HashBucket;

K:关键码类型
T: 不同容器T的类型不同,如果是unordered_map,T代表一个键值对,如果是unordered_set,T为 K
KeyOfT: 在哈希表中需要取到value,因为T的类型不同,通过value取key的方式就不同,详细见unordered_map/set的实现
Hash: 哈希函数仿函数对象类型,哈希函数使用除留余数法,如果是Key为string类型,需要将Key转换为整形数字才能取模

哈希表节点结构

template<class T>
struct HashNode
{
    T _data;
    HashNode<T>* _next;

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

如果是unordered_map,T代表一个键值对,如果是unordered_set,T为 K

哈希表迭代器模拟实现

哈希表的迭代器我们应该怎么实现呢?

哈希表的迭代器也是对节点指针进行了封装,我们想一想++操作怎么实现呢?一个桶遍历完了如何跳转到下一个桶?为了实现桶的跳转,我们在迭代器中还需要一个哈希表的指针

operator++模拟实现

当下一个节点不为空时,++后的节点就在当前桶,返回即可,当下一个节点为空时,我们需要找下一个桶,首先通过当前节点计算找到当前节点所在桶的位置index,计算出后,++index即找到了下一个桶,当下一个桶存在时,如果下一个桶里面有数据(即不为空),则将第一个数据给当前节点,就实现了++,否则继续找下一个桶,当循环出来时,有可能是找到++后的节点了,也有可能说明走完了后面没有桶了,所以循环出来需要判断是不是没有桶了,没有桶则返回nullptr

Self operator++()
{
    if (_node->_next) // 在当前桶迭代
    {
        _node = _node->_next;
    }
    else // 找下一个桶
    {
        KeyOfT kot;
        const K& key = kot(_node->_data);
        Hash hf;
        size_t index = hf(key) % _ht->_tables.size();
        ++index;
        _node = nullptr;
        while (index < _ht->_tables.size())
        {
            if (_ht->_tables[index])
            {
                _node = _ht->_tables[index];
                break;
            }
            else
            {
                ++index;
            }
        }

        // 后面没有桶了
        if (index == _ht->_tables.size())
        {
            _node = nullptr;
        }
    }

    return *this;
}
operator*的模拟实现

返回节点的数据的引用即可

T& operator*()
{
    return _node->_data;
}
operator->的模拟实现

返回节点数据的地址即可

T* operator->()
{
    return &_node->_data;
}
operator==和operator!=的模拟实现
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 KeyOfT,class Hash>
class HashTable;
template<class K, class T, class Hash, class KeyOfT>
struct HTIterator
{
    typedef HashNode<T> Node;
    typedef HashTable<K, T, Hash, KeyOfT> HT;
    typedef HTIterator<K, T, Hash, KeyOfT> Self;

    Node* _node;
    HT* _ht;

    HTIterator(Node* node, HT* ht)
        :_node(node)
            , _ht(ht)
   {}

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

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


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

    Self operator++()
    {
        if (_node->_next) // 在当前桶迭代
        {
            _node = _node->_next;
        }
        else // 找下一个桶
        {
            KeyOfT kot;
            const K& key = kot(_node->_data);
            Hash hf;
            size_t index = hf(key) % _ht->_tables.size();
            ++index;
            _node = nullptr;
            while (index < _ht->_tables.size())
            {
                if (_ht->_tables[index])
                {
                    _node = _ht->_tables[index];
                    break;
                }
                else
                {
                    ++index;
                }
            }

            // 后面没有桶了
            if (index == _ht->_tables.size())
            {
                _node = nullptr;
            }
        }

        return *this;
    }
};

然后在哈希表中实现迭代器的相关函数:

template<class K, class T,  class KeyOfT, class Hash = HashFunc<K>>
class HashTable
{
	typedef HashNode<T> Node;
	friend struct HTIterator<K, T, Hash, KeyOfT>;//将迭代器设置成友元,让迭代器访问哈希表的私有
	public:
    typedef HTIterator<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);
    }
private:
    vector<Node*> _table;
    size_t _n = 0;
}

哈希表的最终代码

namespace bucket_hash
{
	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next;

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

	size_t GetNextPrime(size_t prime)
	{
		const int PRIMECOUNT = 28;
		static 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] > prime)
				return primeList[i];
		}

		return primeList[i];
	}

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

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

		Node* _node;
		HT* _ht;

		HTIterator(Node* node, HT* ht)
			:_node(node)
			, _ht(ht)
		{}

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

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


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

		Self operator++()
		{
			if (_node->_next) // 在当前桶迭代
			{
				_node = _node->_next;
			}
			else // 找下一个桶
			{
				KeyOfT kot;
				const K& key = kot(_node->_data);
				Hash hf;
				size_t index = hf(key) % _ht->_tables.size();
				++index;
				_node = nullptr;
				while (index < _ht->_tables.size())
				{
					if (_ht->_tables[index])
					{
						_node = _ht->_tables[index];
						break;
					}
					else
					{
						++index;
					}
				}

				// 后面没有桶了
				if (index == _ht->_tables.size())
				{
					_node = nullptr;
				}
			}

			return *this;
		}
	};

	template<class K, class T,  class KeyOfT, class Hash = HashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;

		//template<class K, class T, class Hash, class KeyOfT>
		friend struct HTIterator<K, T, Hash, KeyOfT>;
	public:
		typedef HTIterator<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;
			}

			_n;
		}

		bool Erase(const K& key)
		{
			if (_tables.size() == 0)
			{
				return false;
			}

			Hash hf;
			KeyOfT kot;

			// 素数
			size_t index = hf(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[index];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					// 1、cur是头结点
					// 2、非头节点
					if (prev == nullptr)
					{
						_tables[index] = cur->_next;
					}
					else
					{
						prev->_next = cur->_next;
					}

					delete cur;
					--_n;

					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}

			return false;
		}

		Node* Find(const K& key)
		{
			if (_tables.size() == 0)
			{
				return nullptr;
			}

			Hash hf;
			KeyOfT kot;
			size_t index = hf(key) % _tables.size();
			Node* cur = _tables[index];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return cur;
				}
				else
				{
					cur = cur->_next;
				}
			}

			return nullptr;
		}

		bool Insert(const T& data)
		{
			Hash hf;
			KeyOfT kot;

			//当负载因子到1时,进行扩容
			if (_n == _tables.size())
			{
				//size_t newSize = _tables.size() == 0 ? 10 : _tables.size() * 2;
				size_t newSize = GetNextPrime(_tables.size());

				//HashTable<K, V> newHT;
				vector<Node*> newtables;
				newtables.resize(newSize, nullptr);
				for (size_t i = 0; i < _tables.size(); ++i)
				{
					Node* cur = _tables[i];
					while (cur)
					{
						Node* next = cur->_next;

						const K& key = kot(cur->_data);
						size_t index = hf(key) % newSize;

						cur->_next = newtables[index];
						newtables[index] = cur;

						cur = next;
					}
					_tables[i] = nullptr;
				}

				newtables.swap(_tables);
			}

			const K& key = kot(data);
			size_t index = hf(key) % _tables.size();
			Node* cur = _tables[index];
			while (cur)
			{
				if (kot(cur->_data) == kot(data))
				{
					return false;
				}
				else
				{
					cur = cur->_next;
				}
			}

			Node* newnode = new Node(data);
			newnode->_next = _tables[index];
			_tables[index] = newnode;

			++_n;

			return true;
		}

	private:
		vector<Node*> _tables;
		size_t _n = 0; // 存储多少有效数据
	};
}

unordered_set的模拟实现

实现unordered_set只需要调用底层哈希表对应的接口即可:

#pragma once
#include "HashTable.h"
namespace Z
{
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key) const
			{
				return key;
			}
		};
	public:
		typedef typename bucket_hash::HashTable<K, K,SetKeyOfT>::iterator iterator;
		//没有实例化,没办法去哈希表中去找iterator,typename这就是告诉编译器这就是个类型,等实例化后再去找
		iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}
		//插入
		pair<iterator,bool> insert(const pair<const K, V>& kv)
		{
			return _ht.Insert(kv);
		}
        //删除
        bool erase(const K& key)
        {
            return _ht.Erase(key);
        }
        //查找
        iterator find(const K& key)
        {
            return _ht.Find(key);
        }
	private:
		bucket_hash::HashTable<K, K, SetKeyOfT> _ht;
	};

unordered_map的模拟实现

实现unordered_map只需要调用底层哈希表对应的接口即可,和unordered_set不一样的是它需要实现[]运算符重载:

#pragma once
#include "HashTable.h"
namespace bit
{
	template<class K, class V>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<const K, V>& kv) const
			{
				return kv.first;
			}
		};
	public:
         iterator begin()
		{
			return _ht.begin();
		}

		iterator end()
		{
			return _ht.end();
		}
        //插入
		pair<iterator,bool> insert(const pair<const K, V>& kv)
		{
			return _ht.Insert(kv);
		}
        //[]运算符重载
         V& operator[](const K& key)
         {
             pair<iterator,bool> ret = insert(make_pair(key,V()));
             iterator it = ret.first;
             return it->second;
         }
        //删除
        bool erase(const K& key)
        {
            return _ht.Erase(key);
        }
        //查找
        iterator find(const K& key)
        {
            return _ht.Find(key);
        }
	private:
		bucket_hash::HashTable<K, pair<const K, V>, MapKeyOfT> _ht;
	};
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小赵小赵福星高照~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值