模拟实现哈希桶,并封装哈希桶模拟实现map

哈希桶结构:一个数组,每个元素都是一个单链表结点(开散列)(闭散列只用数组)
map:用仿函数和哈希函数来封装哈希桶。仿函数是为了计算出元素的值(为什么要仿函数计算:后序可以封装set,所以无论是map还是set,存的值都是K-V结构,set存了K-K,也就是两个一样的值),哈希函数用了模板特化,为了能够更方便的计算出string类型的值的哈希位置。

源码:

哈希结点

//哈希结点:实质是一个单链表
template<class V>
struct HashNode
{
	V _data;
	HashNode<V>* _next;

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

哈希迭代器

//前置声明
template<class K, class V, class KeyOfValue,class HashFun>
class HashBucket;
//迭代器
//K、V是为了兼容set和map
//KeyOfValue是个仿函数,让set和map封装,然后通过仿函数求出set和map里存的值
//HashFun是哈希函数,是为了区别计算string的值和普通数字的值,从而计算存放位置
template<class K, class V, class KeyOfValue, class HashFun>
struct HashIterator
{
	typedef HashNode<V> Node;
	typedef Node* pNode;
	typedef HashIterator<K, V, KeyOfValue, HashFun> Self;
	typedef typename HashBucket<K, V, KeyOfValue, HashFun> HSbucket;

	pNode _node;
	HSbucket* _hb;

	HashIterator(pNode node, HSbucket* hb)
		:_node(node)
		, _hb(hb)
	{

	}

	V& operator*()
	{
		return _node->_data;
	}
	V* operator->()
	{
		return &_node->_data;
	}
	bool operator!=(const Self& it)
	{
		return _node != it._node;
	}
	Self& operator++()
	{
		//如果链表结点的下一个还有值,则下一个结点就是++后的值
		if (_node->_next)
		{
			_node = _node->_next;
		}
		else//否则就由数组往后找,直到找到数组有值的位置
		{
			KeyOfValue kov;
			HashFun hf;
			int table_size = _hb->_table.size();
			//先通过哈希函数和KeyOfValue计算出当前位置
			int index = hf(kov(_node->_data)) % table_size;
			//然后遍历数组去查找数组的值
			++index;
			for (; index < table_size; ++index)
			{
				//如果不是空就是找到了
				if (_hb->_table[index] != nullptr)
				{
					_node = _hb->_table[index];
					break;
				}
			}
			//如果走到最后还没有找到,就是没有了,就是end()
			if (index == table_size)
			{
				_node = nullptr;
			}
		}
		return *this;
	}
};

哈希桶

template<class K, class V, class KeyOfValue, class HashFun>
class HashBucket
{
public:
	//声明为友元是为了让自己定义的迭代器访问到自己的private里的table
	friend HashIterator<K, V, KeyOfValue,HashFun>;
	typedef HashNode<V> Node;
	typedef Node* pNode;
	typedef typename HashIterator<K, V, KeyOfValue,HashFun> iterator;
private:
	vector<pNode> _table;
	size_t _size;
public:
	iterator begin()
	{
		//begin就是数组里第一个有值的元素
		for (size_t i = 0; i < _table.size(); ++i)
		{
			if (_table[i])
			{
				return iterator(_table[i], this);
			}
		}
		return iterator(nullptr, this);
	}
	iterator end()
	{
		//end就是值为空的迭代器
		return iterator(nullptr, this);
	}
	HashBucket()
		:_size(0)
	{
		_table.resize(5, nullptr);
	}
	pair<iterator,bool> Insert(const V& data)
	{
		//检查容量
		ChekckCapacity();
		//先确定数组下标
		KeyOfValue kov;
		HashFun hf;
		int index = hf(kov(data)) % _table.size();
		//插入
		pNode cur = _table[index];
		//判断是否已经有这个元素,直到cur为空
		while (cur)
		{
			if (hf(kov(cur->_data)) == hf(kov(data)))
			{
				return make_pair(iterator(cur, this), false);
			}
			cur = cur->_next;
		}
		//创建新节点
		cur = new Node(data);
		//让新节点指向这个链表的头,头插
		cur->_next = _table[index];
		//更新数组的元素
		_table[index] = cur;
		++_size;
		return make_pair(iterator(cur, this), true);
	}
	void ChekckCapacity()
	{
		//负载因子可以是1
		if (_size / _table.size() < 1)
		{
			return;
		}
		KeyOfValue kov;
		HashFun hf;
		vector<pNode> new_table(_size * 2);
		for (int i = 0; i < _table.size(); ++i)
		{
			pNode cur = _table[i];
			while (cur)
			{
				//先保存后一个节点
				pNode next = cur->_next;
				//确定这个数据在新表的位置
				int index = hf(kov(cur->_data)) % new_table.size();
				//头插
				cur->_next = new_table[index];
				//更新数组元素
				new_table[index] = cur;
				//继续遍历
				cur = next;
			}
			_table[i] = nullptr;
		}
		_table.swap(new_table);
	}
};

哈希函数

//为了得到普通数字的数值
template<class K>
struct HashFunction
{
	int operator()(const K& key)
	{
		return key;
	}
};
//这里是模板特化,编译器会先去找有特化的仿函数,这里是为了计算string的值,得到string需要存放的位置
template<>
struct HashFunction<string>
{
	int operator()(const string& key)
	{
		int hash = 0;
		for (const auto& ch : key)
		{
			//这里的值的计算方式不唯一,有很多种方法
			hash = hash * 31 + ch;
		}
		return hash;
	}
};

map

//模拟unordered_map
template<class K, class V, class HashFun = HashFunction<K>>
class MyUnorderedMap
{
	//这个是为了计算出map键值对里的K的仿函数
	struct MapKeyOfValue
	{
	//如果是set则只要传入一个K,返回一个K即可
		const K& operator()(const pair<K, V>& data)
		{
			return data.first;
		}
	};

private:
	//底层实际就是通过哈希函数和仿函数来封装了哈希桶
	HashBucket<K, pair<K, V>, MapKeyOfValue, HashFun> _hb;
public:
	typedef typename HashBucket<K, pair<K, V>, MapKeyOfValue, HashFun>::iterator iterator;
	pair<iterator, bool> Insert(const pair<K, V>& data)
	{
		return _hb.Insert(data);
	}
	iterator begin()
	{
		return _hb.begin();
	}
	iterator end()
	{
		return _hb.end();
	}
	V& operator[](const K& key)
	{
		//这个函数的原理是:
		//如果访问的值已经存在,返回已存在的元素的引用
		//如果不存在,一个值:key-默认的V()的值,返回插入元素的引用
		//ret的second是插入成功或者失败,first是访问的那个值所在的键值对
		pair<iterator, bool> ret = _hb.Insert(make_pair(key, V()));
		//所以first->second就是键值对里的value的引用,返回后用户可以修改这个value
		return ret.first->second;
	}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值