c++编程(25)——unordered_map模拟实现

欢迎来到博主的专栏:c++编程

博主ID:代码小豪

文章目录

unordered_map是STL中的关联式容器之一,与常规的map有两点不同
(1)unordered是无序的意思,之所以取这名字,是因为与常规map迭代器前进的方向是有序的,而unordered_map的迭代器前进方向是无序的。

cpp 复制代码
void test1()
{
	int a[] = { 11,25,21,1,6,9,16 };
	unordered_map<int, int> op;
	map<int, int> m;
	for (auto e : a)
	{
		op.insert(make_pair(e, e));
		m.insert(make_pair(e, e));
	}
	for (auto e : m)//遍历map容器
	{
		cout << e.first << ' ';//1 6 9 11 16 21 25
	}
	cout << endl;
	for (auto e : op)//遍历unordered_map
	{
		cout << e.first << ' ';//11 9 1 25 21 6 16
	}
}

(2)之所以造成这种差异,是因为unordered_map的底层用的是哈希表,而map的底层是红黑树。这是底层而造成的差异。

unorder_map的底层

关于哈希表的介绍,博主放在了数据结构专栏中,因此不再赘述。

由于STL标准库中的set和map共用一个hashtable底层(SGI版stl),因此博主将hashtable做了些许修改:

cpp 复制代码
template<class key,class T,class keyofT>
class hash_tab
{
public:
	typedef T value_type;
	typedef hash_Node<value_type> Node;
	hash_tab()
	{
		_tab.resize(10);
	}

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

private:
	vector<Node*> _tab;
	size_t _n;
	keyofT kot;
};

由于map和set的数据不一样,set的数据是key类型的,而map的数据是pair<key,value>型的,因此需要一个key值提取器------keyofT。关于keyofT怎么用,博主在会在后续文章提到。

insert

unordered_map类被博主设计成这样,其成员为底层哈希表.

cpp 复制代码
template<class key,class value>
class unordered_map
{
public:
	typedef pair<key, value> value_type;

	template<class key,class value>
	class unordered_mapkeyofT
	{
		const key& operator()(const value_type& data)const
		{
			return data.first;
		}
	};

private:
	myhash::hash_tab<key, value_type, unordered_mapkeyofT<key, value>> _hashtab;//底层哈希表
	};

如果使用了底层哈希表,那么insert可以直接复用_hashtab的insert函数。

cpp 复制代码
pair<iterator, bool> insert(const value_type& data)
{
	return _hashtab.insert(data);
}

而unordered_map的erase,find操作都可以这样做,因此模拟实现unordered_map最重要的一步是设计出hashtab使用的迭代器。

迭代器

关于哈希的迭代器,博主是这样构思的,迭代器有两个成员,分别是指向当前哈希表的指针,以及一个指向该表的有效数据的指针。

cpp 复制代码
template<class key,class T,class ref,class ptr,class keyofT>
struct hash_iterator
{
	typedef hash_Node<T> Node;
	typedef hash_tab<key, T, keyofT> hash;
	typedef hash_iterator self;

	hash_iterfator(Node* node,hash* tab)
	{
		_node = node;
		_tab = tab;
	}


	keyofT kot;
	Node* _node;//指向节点
	hash* _tab;//指向当前哈希表
};

begin()函数返回指向哈希表第一个有效数据的节点

cpp 复制代码
Iterator begin()
{
	Node* cur = nullptr;
	for (int i = 0; i < _tab.size(); i++)
	{
		cur = _tab[i];
		if (cur != nullptr)
			return Iterator(cur, this);
	}
	return Iterator(cur, this);
}

end函数则返回nullptr作为结束标志。

cpp 复制代码
Iterator end()
{
	return Iterator(nullptr, this);
}

最后加上const版本的begin和end。

cpp 复制代码
const_Iterator begin() const
{
	Node* cur = nullptr;
	for (int i = 0; i < _tab.size(); i++)
	{
		cur = _tab[i];
		if (cur != nullptr)
			return Iterator(cur, this);
	}
	return Iterator(cur, this);
}

const_Iterator end()const
{
	return Iterator(nullptr, this);
}

成员访问函数

迭代器的成员访问函数也就是operator*和operator->.这很简单。

cpp 复制代码
ref operator*()
{
	return _node->_data;
}

ptr operator->()
{
	return &operator*();
}

operator++

operator++让迭代器前进到下一个有效数据。做法如下:

(1)有限前进到同一个桶中的下一个有效数据的节点

(2)如果桶中不存在有效节点,就去找下一个有数据的哈希桶。

代码如下:

cpp 复制代码
self operator++()
{
	if (_node->_next != nullptr)
	{
		_node = _node->_next;
	}
	else
	{
		size_t hashnum = kot(_node->_data) % _tab->_tab.size();
		hashnum++;
		while (hashnum < _tab->_tab.size())
		{
			Node* cur = _tab->_tab[hashnum];
			if (cur != nullptr)
			{
				_node = cur;
				return *this;
			}
			hashnum++;
		}
		_node = nullptr;//如果没找到,就返回end()标志
		return *this;
	}
}

operator--

operator--的操作如下:

(1)首先找到当前节点的映射地址

(2)判断该节点是否是该桶的第一个节点

若为否,则迭代器指向该节点的上一个节点

若为真,则迭代器指向上一个桶的最后一个节点

代码如下:

cpp 复制代码
	self operator--()
	{
		int hashnum = kot(_node) % _tab->_tab.size();
		Node* cur = _tab->_tab[hashnum];

		if (cur != _node)
		{
			while (cur->_next != _node)
			{
				cur = cur->_next;
			}
			_node = cur;
			return *this;
		}
		else//cur==_node
		{
			for (int i = hashnum-1; i >= 0; i--)
			{
				cur = _tab->_tab[i];
				if (cur != nullptr)
				{
					while (cur->_next != nullptr)
					{
						cur = cur->_next;
					}
					_node = cur;
					return *this;
				}
			}
		}
	}
相关推荐
斐夷所非9 分钟前
C++ 继承、多态与类型转换 | 函数重载 / 隐藏 / 覆盖实现与基派生类指针转换
c++
gfdhy33 分钟前
【C++实战】多态版商品库存管理系统:从设计到实现,吃透面向对象核心
开发语言·数据库·c++·microsoft·毕业设计·毕设
清酒难咽1 小时前
算法案例之分治法
c++·经验分享·算法
小屁猪qAq1 小时前
强符号和弱符号及应用场景
c++·弱符号·链接·编译
头发还没掉光光1 小时前
HTTP协议从基础到实战全解析
linux·服务器·网络·c++·网络协议·http
jojo_zjx2 小时前
GESP 24年12月2级 数位和
c++
自由的好好干活2 小时前
PCI9x5x驱动移植支持PCI9054在win7下使用3
c++·驱动开发
WBluuue4 小时前
数据结构与算法:dp优化——优化尝试和状态设计
c++·算法·leetcode·动态规划
睡不醒的kun5 小时前
定长滑动窗口-基础篇(2)
数据结构·c++·算法·leetcode·职场和发展·滑动窗口·定长滑动窗口
小王努力学编程5 小时前
LangChain——AI应用开发框架(核心组件1)
linux·服务器·前端·数据库·c++·人工智能·langchain