【C++】map和set封装

这篇具有很好参考价值的文章主要介绍了【C++】map和set封装。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

> 作者:დ旧言~
> 座右铭:松树千年终是朽,槿花一日自为荣。

> 目标:手撕哈希表的闭散列和开散列

> 毒鸡汤:学习,学习,再学习 ! 学,然后知不足。

> 专栏选自:C嘎嘎进阶

> 望小伙伴们点赞👍收藏✨加关注哟💕💕

【C++】map和set封装,C嘎嘎进阶,c++

🌟前言  

map和set的知识我们学习了,模拟实现我们已经实现了AVL树和红黑树。熟练知识点为map和set的使用提供了前提,手撕AVL树和红黑树让我了解到map和set的底层如何实现,有了知识点和两树的模拟铺垫,那我们该如何封装map和set呢?

⭐主体

学习map和set封装咱们按照下面的图解:

【C++】map和set封装,C嘎嘎进阶,c++

🌙map/set底层原理

Map和Set底层是用红黑树封装的,而红黑树结构是KV结构。

RBTree是通过传入的Value的值来判断类型,也就是一棵泛型的RBTree,通过不同的实例化,实现出了Map和Set,对于map:传key,对于set:传pair,如图:

【C++】map和set封装,C嘎嘎进阶,c++

简化后的map源码:

【C++】map和set封装,C嘎嘎进阶,c++

简化后的set源码:

【C++】map和set封装,C嘎嘎进阶,c++


我们查看源码后,因此在封装我们map和set时,让我们的红黑树增加一个模板参数T来识别map和set,代码如下:(此处的代码是修改了红黑树)

template<class K, class T>
class RBTree

分析原理:

如果是set容器,那么它传入底层红黑树的模板参数就是Key和Key:

template<class K>
class set
{
  private:
    RBTree<K,K> _t;
};

如果是map容器,传入底层红黑树的模板参数就是Key和Key和value的键值对:

class map
{
private:
    RBTree<K, pair<const K,V>> _t;
};

问题拓展:

通过上面,我们可以知道,对于set和map的区别:我们只要通过第二个模板参数就能进行区分那是不是第一个模板参数就没有意义了呢?

  • 对于insert(const Value&v)来说,需要放入存入的值,确实是这个样子的,插入的值是value,对于set就是key,对于map就是pair。
  • 但是对于find(const Key&key)来说,查找的参数不是value,找的不是pair而是Key,对于map容器来说就不行了。

🌙map/set定义

【C++】map和set封装,C嘎嘎进阶,c++

map和set封装都用头文件来,代码如下:

set:

template<class K>
class set
{
  private:
    RBTree<K,K> _t;
};

map:

class map
{
private:
    RBTree<K, pair<const K,V>> _t;
};

 🌙map/set仿函数

问题阐述:

插入的时候data的大小如何去进行比较:我们并不知道是什么类型是key,还是pair的比较,而我们刚开始kv结构就直接用kv.first去比较了。

  • 对于set是Key,可以比较
  • 对于map是pair,那我们要取其中的first来比较,但是pair的大小并不是直接按照first去进行比较的,而我们只需要按照first去进行比较

问题分析:

由于底层的红黑树不知道传的是map还是set容器,当需要进行两个结点键值的比较时,底层红黑树传入的仿函数来获取键值Key,进行两个结点键值的比较:这个时候我们就需要仿函数了,如果是set那就是用于返回T当中的键值Key,如果是map那就是用于返回pair的first:

注意事项:

仿函数/函数对象也是类,是一个类对象。仿函数要重载operator()。

代码实现:

map:

namespace lyk
{
	template<class K, class V>
	class map
	{
		struct MapkeyOfT // 仿函数
		{
			const K& operator()(const pair<const K, V>& kv)
			{
				return kv.first;
			}
		};
}

set:

namespace lyk
{
	template <class K>
	class set  // 仿函数,重载operator()
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}

		};
}

图示:

【C++】map和set封装,C嘎嘎进阶,c++

我们有了仿函数时,查找函数就可以用仿函数来替换比较部分

KeyOfT kot;//仿函数对象
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
	if (kot(cur->_data)<kot(data))
	{
		parent = cur;
		cur = cur->_right;
	}
	else if (kot(cur->_data)>kot(data))
	{
		parent = cur;
		cur = cur->_left;
	}
	else
	{
		return false;
	}
}

🌙map/set插入

接下来 map/set 的插入直接套用红黑树的即可,代码如下:

//map的插入,插入pair
bool insert(const pair<K, V>& kv)
{
	return _t.Insert(kv);
}
 
//set的插入,插入key
bool insert(const K& key)
{
	return _t.Insert(key);
}

🌙map/set迭代器

💫迭代器的定义

template<class T,class Ref,class Ptr>
struct __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T,Ref,Ptr> Self;
	typedef __RBTreeIterator<T, T&, T*> iterator;
	Node* _node;

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

	//普通迭代器的时候,它是拷贝构造
	//const迭代器的时候,它是构造,支持用普通迭代器构造const迭代器
	__RBTreeIterator(const iterator& s)
		:_node(s._node)
	{}
}

 💫解引用操作

作用:返回对应结点数据的引用

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

 💫成员访问操作符

作用:返回结点数据的引用

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

 💫!=、==

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

💫begin() 与 end()

begin():返回中序(左、根、右)第一个结点的正向迭代器,即最左节点,返回的是最左节点,直接找最左节点即可

end():返回中序(左、根、右)最后一个结点下一个位置的正向迭代器,这里直接用空指针

template<class K, class T,class KeyOfT>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef __RBTreeIterator<T> iterator;

	iterator begin()
	{
		Node* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}
		return iterator(left);
	}

	iterator end()
	{
		return iterator(nullptr);
	}
}

💫迭代器的++

一个结点的正向迭代器进行++操作后,根据红黑树中序(左、根、右)找到当前结点的下一个结点,中序的第一个节点是最左,迭代器的++怎么去找:

  • 如果节点的右子树不为空++就是找右子树的最左节点
  • 如果节点的右子树为空++就是找祖先(孩子是父亲的左的那个祖先)
	Self& operator++()
	{
		if (_node->_right)
		{
			Node* min = _node->_right;
			while (min->_left)
			{
				min = min->_left;
			}
			_node = min;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

💫迭代器的--

对于–,如果是根,–就是左子树,找到左子树最大的那一个(最右节点)

  • 如果节点的左子树不为空,--找左子树最右的节点
  • 如果节点的左子树为空,--找祖先(孩子是父亲的右的祖先)
Self& operator--()
	{
		if (_node->_left)
		{
			Node* max = _node->_left;
			while (max->_right)
			{
				max = max->_right;
			}
			_node = max;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent&&cur==parent->_left)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

🌙源代码及其测试

💫map代码

#include"RBTree.h"

namespace lyk
{
	template<class K, class V>
	class map
	{
		struct MapkeyOfT // 仿函数
		{
			const K& operator()(const pair<const K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		//typename:没有实例化的模板,区分不了是静态变量还是类型,typename告诉编译器是类型
		typedef typename RBTree<K, pair<const K, V>, MapkeyOfT>::iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, MapkeyOfT>::const_iterator
			const_iterator;

		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}

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

		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));
			return ret.first->second;
		}
	private:
		RBTree<K, pair<const K, V>, MapkeyOfT> _t;
	};

	void test_map1() // 测试代码
	{
		map<int, int> m;
		int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
		for (auto e : a)
		{
			m.insert(make_pair(e, e));
		}

		map<int, int>::iterator it = m.begin();
		while (it != m.end())
		{
			//it->first += 100;
			it->second += 100;

			cout << it->first << ":" << it->second << endl;
			++it;
		}
		cout << endl;
	}
}

💫set代码

#include"RBTree.h"

namespace lyk
{
	template <class K>
	class set  // 仿函数,重载operator()
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}

		};
	
	public:
		//typename:没有实例化的模板,区分不了是静态变量还是类型,typename告诉编译器是类型
		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;//key不可以修改
		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;

		iterator begin() const
		{
			return _t.begin();
		}

		iterator end() const
		{
			return _t.end();
		}

		pair<iterator, bool> insert(const K& key)
		{
			//底层红黑树的iterator是普通迭代器
			pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> ret = _t.Insert(key);
			return pair<iterator, bool>(ret.first, ret.second);//用普通迭代器构造const迭代器
		}

	private:
		RBTree<K, K, SetKeyOfT> _t;
	
	};

	void test_set1() // 测试代码
	{
		set<int> s;
		int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
		for (auto e : a)
		{
			s.insert(e);
		}

		set<int>::iterator it = s.begin();
		while (it != s.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}
}

💫红黑树代码

#pragma once

#include <iostream>
#include <assert.h>
#include <time.h>
using namespace std;
enum Color
{
	RED,
	BLACK,
};
template<class T>
struct RBTreeNode
{
	T _data;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;

	Color _col;
	RBTreeNode(const T& data)
		:_data(data)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(RED)
	{}
};

template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, Ref, Ptr> Self;
	typedef __RBTreeIterator<T, T&, T*> iterator;
	Node* _node;

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


	//普通迭代器的时候,它是拷贝构造
	//const迭代器的时候,它是构造,支持用普通迭代器构造const迭代器
	__RBTreeIterator(const iterator& s)
		:_node(s._node)
	{}

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

	Self& operator++()
	{
		if (_node->_right)
		{
			Node* min = _node->_right;
			while (min->_left)
			{
				min = min->_left;
			}
			_node = min;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	Self& operator--()
	{
		if (_node->_left)
		{
			Node* max = _node->_left;
			while (max->_right)
			{
				max = max->_right;
			}
			_node = max;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}

	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 RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef __RBTreeIterator<T, T&, T*> iterator;
	typedef __RBTreeIterator<T, const T&, const T*> const_iterator;
	const_iterator begin() const
	{
		Node* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}
		return const_iterator(left);

	}
	const_iterator end() const
	{
		return const_iterator(nullptr);
	}

	iterator begin()
	{
		Node* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}
		return iterator(left);

	}
	iterator end()
	{
		return iterator(nullptr);
	}
	pair<iterator, bool> Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root), true);
		}
		KeyOfT kot;
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return make_pair(iterator(cur), false);
			}
		}
		cur = new Node(data);
		Node* newnode = cur;
		cur->_col = RED;
		if (kot(parent->_data) < kot(data))
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_left = cur;
			cur->_parent = parent;
		}

		while (parent && parent->_col == RED)
		{
			Node* grandfater = parent->_parent;
			if (parent == grandfater->_left)
			{
				Node* uncle = grandfater->_right;
				//情况一:u存在且为红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfater->_col = RED;
					//向上调整
					cur = grandfater;
					parent = cur->_parent;
				}
				else
				{
					//情况2
					if (cur == parent->_left)
					{
						RotateR(grandfater);
						parent->_col = BLACK;
						grandfater->_col = RED;
					}
					//情况3
					else
					{
						//       g
						//  p
						//    c 
						RotateL(parent);
						RotateR(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}
					break;
				}
			}
			else//parent==grandfater->_right
			{
				Node* uncle = grandfater->_left;
				//情况1:u存在且为红色
				if (uncle && uncle->_col == RED)
				{
					uncle->_col = parent->_col = BLACK;
					grandfater->_col = RED;
					//向上调整
					cur = grandfater;
					parent = cur->_parent;
				}
				else
				{
					//情况2:u不存在/u存在为黑色
					//g
					//    p
					//        c
					if (cur == parent->_right)
					{
						RotateL(grandfater);
						grandfater->_col = RED;
						parent->_col = BLACK;
					}
					//情况3
					//     g
					 //         p
					 //      c
					else
					{
						RotateR(parent);
						RotateL(grandfater);
						cur->_col = BLACK;
						grandfater->_col = RED;
					}
					break;
				}

			}
		}
		//根变黑
		_root->_col = BLACK;
		return make_pair(iterator(newnode), true);
	}

	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;
		Node* ppNode = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;
		if (ppNode == nullptr)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}
			subR->_parent = ppNode;
		}
	}

	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;
		Node* ppNode = parent->_parent;
		parent->_parent = subL;
		subL->_right = parent;
		if (ppNode == nullptr)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}
			subL->_parent = ppNode;
		}
	}


	void InOrder()
	{
		_InOrder(_root);
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;
		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}


	bool Check(Node* root, int blackNum, int ref)
	{
		if (root == nullptr)
		{
			//cout << blackNum << endl;
			if (blackNum != ref)
			{
				cout << "违反规则:本条路径的黑色结点的数量根最左路径不相等" << endl;
				return false;
			}
			return true;
		}
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "违反规则:出现连续的红色结点" << endl;
			return false;
		}
		if (root->_col == BLACK)
		{
			++blackNum;
		}
		return Check(root->_left, blackNum, ref)
			&& Check(root->_right, blackNum, ref);
	}

	bool IsBalance()
	{
		if (_root == nullptr)
		{
			return true;
		}

		if (_root->_col != BLACK)
		{
			return false;
		}
		int ref = 0;
		Node* left = _root;
		while (left)
		{
			if (left->_col == BLACK)
			{
				++ref;
			}
			left = left->_left;
		}
		return Check(_root, 0, ref);
	}
private:
	Node* _root = nullptr;
};

💫测试

测试set:

【C++】map和set封装,C嘎嘎进阶,c++

测试map:

【C++】map和set封装,C嘎嘎进阶,c++

🌟结束语

       今天内容就到这里啦,时间过得很快,大家沉下心来好好学习,会有一定的收获的,大家多多坚持,嘻嘻,成功路上注定孤独,因为坚持的人不多。那请大家举起自己的小手给博主一键三连,有你们的支持是我最大的动力💞💞💞,回见。

【C++】map和set封装,C嘎嘎进阶,c++文章来源地址https://www.toymoban.com/news/detail-856692.html

到了这里,关于【C++】map和set封装的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 用红黑树封装map&set【C++】

    目录 前言 一,定义 二,完善setmap比较  三,迭代器实现 operator++ operator-- operator[] 四,发现问题 解决 修改一: set封装层面 修改二:正向迭代器修改 下期预告: 哈希!! 结语 我们在上篇文章红黑树——插入底层实现【C++】面试重灾区!!-CSDN博客 在上篇文章我们实现了红

    2024年02月06日
    浏览(32)
  • C++ 改造红黑树,封装map和set

    set是Key模型的红黑树 map是Key-Value模型的红黑树 我们之前已经把红黑树实现并测试过了 这是Key-Value模型的红黑树 RBTree.h 要想用红黑树来封装set和map,我们首先想到的是在搞一份Key模型的红黑树, 然后用Key模型红黑树来封装set,Key-Value模型红黑树封装map 但是STL库中set和map的设计却

    2024年03月17日
    浏览(40)
  • 【C++】map与set容器——红黑树底层封装

    💭STL中,容器大概可分为两种类型——序列式容器和关联式容器。在前面的系列文章中,我们已经介绍了诸多序列式容器,如:vector、list、stack、queue等,它们以序列的形式存储数据。 💭而关联式容器也是一种非常重要的容器。标准的STL关联式容器分为set(集合)和map(映

    2023年04月11日
    浏览(27)
  • C++ | 红黑树以及map与set的封装

    目录 前言 一、红黑树 1、红黑树的基本概念 2、红黑树相关特性 3、红黑树结点的定义 4、红黑树的查找 5、红黑树的插入 6、二叉树的拷贝构造与析构 7、红黑树的检测 8、红黑树总结 二、map与set的封装 1、红黑树的结点 2、红黑树迭代器 3、set的封装 4、map的封装 三、源码仓库

    2024年02月14日
    浏览(29)
  • C++【一棵红黑树封装 set 和 map】

    ✨个人主页: 北 海 🎉所属专栏: C++修行之路 🎃操作环境: Visual Studio 2019 版本 16.11.17 红黑树的基本情况我们已经在上一篇文章中学习过了,本文主要研究的是红黑树的实际应用:封装实现 set 和 map ,看看如何通过一棵红黑树满足两个不同的数据结构;在正式封装之前,

    2024年02月11日
    浏览(27)
  • 【C++】使用红黑树进行封装map和set

    🌇个人主页:平凡的小苏 📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风翻盘 。 🛸 C++专栏 : C++内功修炼基地 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真

    2024年02月07日
    浏览(30)
  • 【C++】用红黑树迭代器封装map和set

    封装有点难 - . - 文章目录 前言 一、红黑树原先代码的修改 二、红黑树迭代器的实现 总结 因为我们要将红黑树封装让map和set使用,所以我们要在原来的基础上将红黑树代码进行修改,最主要的是修改模板参数,下面我们直接进入正题: 首先我们拿出STL中的源代码,看看大佬

    2024年02月06日
    浏览(32)
  • 【C++进阶】map和set( 万字详解)—— 上篇

    🎇C++学习历程:进阶 博客主页: 一起去看日落吗 持续分享博主的C++学习历程 博主的能力有限,出现错误希望大家不吝赐教 分享给大家一句我很喜欢的话: 也许你现在做的事情,暂时看不到成果,但不要忘记,树🌿成长之前也要扎根,也要在漫长的时光🌞中沉淀养分。静

    2024年01月17日
    浏览(34)
  • [C++]哈希表实现,unordered_map\set封装

    目录 ​​​​​​​ 前言: 1 哈希 1.1 为什么有哈希 1.2 哈希结构 1.3 哈希冲突  2 闭散列 2.1 闭散列结点结构和位置状态表示 2.2 哈希类结构 2.3 插入 2.4 查找 2.5 删除 3 开散列 3.1 哈希表结点结构 3.2 哈希表结构 3.3 插入 3.4 查找、删除 3.5 迭代器实现 4 map和set的封装 4.1 map的封

    2024年02月08日
    浏览(33)
  • 【C++】用一棵红黑树同时封装出map和set

    苦厄难夺凌云志,不死终有出头日。 1. 在源码里面,对于map和set的实现,底层是用同一棵红黑树封装出来的,并不是用了两棵红黑树,一个红黑树结点存key,一个红黑树结点存key,value的键值对,这样的方式太不符合大佬的水准了,实际上在红黑树底层中用了一个模板参数Va

    2023年04月13日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包