Map与Set的模拟实现

✨前言✨

📘 博客主页:to Keep博客主页
🙆欢迎关注,👍点赞,📝留言评论
⏳首发时间:2024年5月10日
📨 博主码云地址:博主码云地址
📕参考书籍:《C++ Primer》《C++编程规范》
📢编程练习:牛客网+力扣网
**由于博主目前也是处于一个学习的状态,如有讲的不对的地方,请一定联系我予以改正!!!


全文的完整代码

1 认识map与set

序列式容器:像vector,list底层是线性的数据结构,它们属于STL中的序列式容器。
关联式容器:像map与set底层是红黑树,属于非线性的结构,属于关联式容器

map与set都是通过关键字Key比较,通过红黑树的规则进行存储的,是有序的!但是不同的是,set是去重的集合,里面只有Key,而map也是去重的集合,但是里面存储的K-V结构!那么在map中是如何存储K-V值的呢?实际上,map是用pair<k,v>类型来存储K-V结构的:
在这里插入图片描述
key就是pair<k,v>类型中的first,value就是second!而我们可以着重来理解一下map重载[]这方法
在这里插入图片描述
在这里插入图片描述
通过以上两张图片,我们就可以知道,重载[]是需要你在[]内填入相应的key值就会返回对应的value值,如何取到对应的value呢?我们可以看到,该方法是复用了insert方法,insert方法是怎么样的呢?
在这里插入图片描述

我们可以看到insert是插入pair<k,v>类型的,返回的是一个pair<iterator,bool>类型的,结合上面重载[]的返回值,我们就可以知道,我们取出pair<iterator,bool>对象中的first也就是iterator,对迭代器进行解引用就是pair<k,v>对象,然后取second就是value了!

2 改造红黑树

下面让我们看看set与map底层的源码是怎么样的,set的源码如图所示:
在这里插入图片描述
map的源码如图所示:
在这里插入图片描述
底层的红黑树是以k,v结构,为了复用这段代码,从上图中我们可以发现,set是传入了两个key值,而map是传入一个key值一个pair类型的值,既然这样,我们是否可以只传入第二个value值呢?我们红黑树就写一个v结构的?显然是不可以的,比如我们在查找时或者需要进行比较,是根据key值来进行查找与比较的,这样set与map就保持了一致!有了上述想法,在之前的红黑树的基础上,改造如下:

#include <assert.h>
enum Colour
{
	RED,
	BLACK
};

template <class T>
struct RBTreeNode
{	
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	Colour _col;
	T _data;

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

};
//Comapre是一个仿函数,用来取出key值
template <class K, class T,class Compare>
class RBTree
{
public:
	typedef RBTreeNode<T> Node;

public:
	bool Insert(const T& data)
	{
		//1 寻找要插入的位置
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return true;
		}
		Node* parent = _root;
		Node* cur = _root;
		Compare com;

		while (cur)
		{
			if (com(cur->_data) > com(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (com(cur->_data) < com(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
			if (cur)
				cur->_parent = parent;
		}

		cur = new Node(data);
		if (com(data) > com(parent->_data))
		{
			parent->_right = cur;
		}
		else {
			parent->_left = cur;
		}
		cur->_parent = parent;
		Node* newnode = cur;
		//开始改变颜色
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;

			//parent节点在祖父节点的左边
			if (parent == grandfather->_left)
			{
				//第一种情况,需要变色处理
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)
				{
					uncle->_col = BLACK;
					parent->_col = BLACK;
					grandfather->_col = RED;
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					//第二种情况 uncle节点为空或者是黑色节点
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						RotateL(parent);
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					//由于根节点变成了黑色,那就不用再往上判断了
					break;
				}
			}
			else
			{
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)
				{
					uncle->_col = BLACK;
					parent->_col = BLACK;
					grandfather->_col = RED;
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					//第二种情况 uncle节点为空或者是黑色节点
					if (cur == parent->_left)
					{
						RotateR(parent);
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					//由于根节点变成了黑色,那就不用再往上判断了
					break;
				}
			}
		}
		//将根节点的颜色变成黑色就行了
		_root->_col = BLACK;

		return true;

	}

	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		subR->_left = parent;
		parent->_right = subRL;

		Node* ppnode = parent->_parent;

		if (subRL)
			subRL->_parent = parent;
		parent->_parent = subR;

		if (parent == _root)
		{
			_root = subR;
		}
		else if (parent == ppnode->_left)
		{
			ppnode->_left = subR;
		}
		else if (parent == ppnode->_right)
		{
			ppnode->_right = subR;
		}
		subR->_parent = ppnode;
	}

	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		subL->_right = parent;
		parent->_left = subLR;

		Node* ppnode = parent->_parent;

		parent->_parent = subL;
		if (subLR)
			subLR->_parent = parent;
		if (parent == _root)
		{
			_root = subL;
		}
		else if (parent == ppnode->_left)
		{
			ppnode->_left = subL;
		}
		else if (parent == ppnode->_right)
		{
			ppnode->_right = subL;
		}

		subL->_parent = ppnode;
	}
}

3 迭代器的实现

这里的迭代器与链表哪里的迭代器一样,需要将节点指针进行封装,自己重载++与–运算符,因为自然的++与–运算符不符合我们的预期!
实现如下所示:

template<class T,class Ptr,class Ptr1>
struct RBIterator {

	typedef RBTreeNode<T> Node;
	typedef RBIterator<T,Ptr,Ptr1> self;
	
	RBIterator(Node* node)
		:_node(node)
	{}

	self& operator++()
	{
		 
		Node* curRight = _node->_right;

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

	self& operator--()
	{
		Node* curLeft = _node->_left;
		if (curLeft)
		{
			while (curLeft->_right)
			{
				curLeft = curLeft->_right;
			}
			_node = curLeft;
		}
		else
		{
			Node* cur = _node;
			Node* parent = _node->_parent;

			while (parent && parent->_left == cur)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;

	}

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

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

	bool operator==(const self& it)
	{
		return _node == it._node;
	}

	bool operator!=(const self& it)
	{
		return _node != it._node;
	}


private:
	Node* _node;
};

这里就重点提一下++的实现:

1️⃣假设当前节点就是根节点,要对下一个节点进行访问,那么就需要我们找到右子树中的最左边的节点
2️⃣然后在判断cur与parent的位置关系,如果parent的左边为cur,那么此时的parent就是下一个要访问的节点,如果cur在parent的右边时,cur往上走,parent也会往上走,一直可能走到parent为空的情况

同理–这个运算符就与++是相反的!

4 实现map与set

map的实现

template<class K,class V>
class Map {
	//仿函数 内部类取first数据
	struct MapCompare {
		const K& operator()(const pair<const K, V>& data)
		{
			return data.first;
		}
	};
public:
	typedef typename  RBTree<K,pair<const K,V>,MapCompare>::iterator iterator;
	typedef typename RBTree<K, pair<const K,V>, MapCompare>::const_itertaor const_iterator;
private:
	RBTree<K, pair<const K, V>, MapCompare> _t;

public:
	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(pair<K,V> kv)
	{
		pair<iterator, bool> it = _t.Insert(kv);
		return it;
	}

};

set的实现

template<class K>
class Set {
	//虚函数
	struct SetCompare {
		const K& operator()(const K& data)
		{
			return data;
		}
	};
	typedef typename RBTree < K, const K, SetCompare>::iterator iterator;
	typedef typename RBTree < K, const K, SetCompare>::const_itertaor const_iterator;
public:

	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 K& key)
	{
		pair<iterator, bool> it = _t.Insert(key);
		return it;
	}
private:
	RBTree<K,const K,SetCompare> _t;
};

  • 31
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

to Keep

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

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

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

打赏作者

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

抵扣说明:

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

余额充值