C++unordered_map/set容器实现


一、改造哈希表

由于哈希桶解决冲突的方式更加有效且简单,所以这里底层哈希表采用拉链法。

1.1 哈希表的数据结构

为了让unordered系列容器使用同一个哈希表,需要修改哈希结点的定义,变得更加抽象

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

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

此外还要增加一个仿函数获得不同类型的key值。所以现在的哈希表的定义为:

template<class K, class V, class KeyOfT, class HashFunc >
class HashTable
{
	typedef HashNode<K, V> Node;
private:
	vector<Node*> _tables;
	size_t _n = 0; // 有效数字个数
}

1.2 哈希表的迭代器

哈希表的迭代器怎么设计呢?
观察unordered_map的函数可以知道,它的迭代器是一个单向的,也就是只有 ++ 。哈希表的迭代器需要从某一个位置开始遍历它所在的桶元素,然后遍历后面的桶,为了方便,可以将哈希表对象的传给迭代器,迭代器就能根据当前的key值确定位置,然后开始往后遍历。

迭代器的结构如下

// 迭代器里面会用到,所以提前声明
template<class K, class T, class KeyOfT, class HashFunc >
class HashTable;

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

	Node* _node;
	HashTable<K, T, KeyOfT, HashFunc>* _pht; // 存放哈希表的地址

	HTableIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
		:_node(node), _pht(pht)
	{}
};
  • 迭代器寻找后一个

这里需要使用哈希表的私有元素,所以可以将迭代器声明为哈希表的友元类,或者创建get方法。

Self& operator++()
{
	// 如果当前桶下面还有数据,直接输出
	// 否则寻找下一个不为空的桶
	if (_node->_next != nullptr)
	{
		_node = _node->_next;
	}
	else
	{
		KeyOfT kot;
		HashFunc hf;
		// 重新映射,找到下标
		size_t index = hf(kot(_node->_data)) % _pht->_tables.size();
		++index;
		while (index < _pht->_tables.size())
		{
			if (_pht->_tables[index] != nullptr)
			{
				break;
			}
			else
			{
				++index;
			}
		}

		// 表后面都找完了没有找到,返回空
		if (index == _pht->_tables.size())
		{
			_node = nullptr;
		}
		else
		{
			_node = _pht->_tables[index];
		}
	}
	return *this;
}
  • 其他运算符重载
Ref operator*()
{
	return _node->_data;
}
Ptr operator->()
{
	return &_node->_data;
}
bool operator!=(const Self& s)
{
	return _node != s._node;
}
bool operator==(const Self& s)
{
	return _node == s._node;
}

1.3 哈希表迭代器相关函数

一些typedef

	// 将迭代器声明为友元类这样就能访问哈希表的私有元素
	template<class K, class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
	friend struct HTableIterator;

	typedef HashNode<T> Node;
	typedef HTableIterator <K, T, T&, T*, KeyOfT, HashFunc> iterator;
	typedef HashTable<K, T, KeyOfT, HashFunc> Self;

1.3.1 类的默认函数

// C++ 11 ,显示指定编译器去生成默认构造函数
HashTable() = default;
// 拷贝构造函数
HashTable(const Self & s)
{
	_tables.resize(s._tables.size());
	for (int i = 0; i < s._tables.size(); i++)
	{
		Node* cur = s._tables[i];
		while (cur)
		{
			Node* copy = new Node(cur->_data);
			copy->_next = _tables[i];
			_tables[i] = copy;
			++_n;
			cur = cur->_next;
		}
	}
}
// 赋值运算重载
Self& operator=(Self copy)
{
	_tables.swap(copy._tables);
	_n = copy._n;
	return *this;
}

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

1.3.2 迭代器相关函数

iterator begin()
{
	// 寻找第一个有元素的桶
	for (int i = 0; i < _tables.size(); i++)
	{
		if (_tables[i] != nullptr)
		{
			return iterator(_tables[i], this);
		}
	}

	// 没有
	return end();
}
iterator end()
{
	return iterator(nullptr, *this);
}

同时也要改变Find的返回值类型为迭代器
插入函数的返回值类型为一个元组pair<iterator, bool> 方便上层实现[]重载

二、封装unordered_map

这下封装就很简单了,只需要调用哈希表的函数就好了

#pragma once

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

		iterator end()
		{
			return _ht.end();
		}
		
		V& operator[](const K& key)
		{
			auto res = _ht.Insert(make_pair(key, V()));
			return res.first->second;
		}

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

三、封装unordered_set

#pragma once

namespace sqin
{
	template<class K, class HashFunc = Hash<K>>
	class unordered_set
	{
		struct SetOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	private:
		HashTable<K, K, SetOfT, HashFunc> _ht;
	public:
		typedef typename HashTable<K, K, SetOfT, HashFunc>::iterator iterator;
		iterator begin()
		{
			return _ht.begin();
		}

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

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

	};
	void test_unordered_set()
	{

		unordered_set<int> us;
		us.insert(5);
		us.insert(15);
		us.insert(35);
		us.insert(8);
		us.insert(24);
		us.insert(18);
		unordered_set<int>::iterator it = us.begin();
		while (it != us.end())
		{
			cout << *it << " ";
			++it;
		}
		cout<<endl;

		unordered_set<int> copyus(us);
		for (auto e : copyus)
		{
			cout << e << " ";
		}

		unordered_set<string> uss;
		uss.insert("aaa");
		uss.insert("bbb");
		for (auto e : uss)
		{
			cout << e << " ";
		}
		unordered_set<int> us2;
		for (size_t i = 0; i < 100; ++i)
		{
			us2.insert(i);
		}

		for (auto e : us2)
		{
			cout << e << " ";
		}
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
回答: 在C++中,map、unordered_mapset和unordered_set都是STL(标准模板库)中的容器。它们都用于存储一组数据,并提供了不同的功能和性能特点。 map是一个有序的关联容器,它使用红黑树实现,可以根据键值进行快速查找。map中的元素按照键值的大小进行排序,并且每个键值只能出现一次。\[1\]unordered_map是一个无序的关联容器,它使用哈希表实现,可以根据键值进行快速查找。unordered_map中的元素没有特定的顺序,并且每个键值只能出现一次。\[2\] set是一个有序的容器,它使用红黑树实现,可以存储不重复的元素。set中的元素按照值的大小进行排序,并且每个值只能出现一次。\[3\]unordered_set是一个无序的容器,它使用哈希表实现,可以存储不重复的元素。unordered_set中的元素没有特定的顺序,并且每个值只能出现一次。 在使用这些容器时,可以使用insert()函数插入元素,使用find()函数查找元素,使用erase()函数删除元素。此外,map和unordered_map还提供了count()函数来计算特定键值的出现次数。 总结来说,map和unordered_map适用于需要根据键值进行快速查找的场景,set和unordered_set适用于需要存储不重复元素的场景。具体选择哪个容器取决于你的需求和性能要求。 #### 引用[.reference_title] - *1* *3* [C++map,unordered_map,set和unordered_set的用法和区别](https://blog.csdn.net/bryant_zhang/article/details/111600209)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [初识C++之unordered_map与unordered_set](https://blog.csdn.net/Masquerena114514/article/details/129938734)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

s_persist

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

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

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

打赏作者

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

抵扣说明:

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

余额充值