C++:map和set容器

"我们都无可奈何地选择了,自己的人生。也无可奈何地出演自己拿到的剧本"


(1) set;

①set是什么?

set是一种管理数据的容器,其底层是由红黑树来实现的。

 

②set的性质:

1.set是按照一定序列存储的容器;

2.set中的元素(value)是唯一的;

3.......

 

与set相对的就是multiset

很显然,这个容器可以存放相同的数.


 

(2)map;

①map介绍:

map也是一种关联式容器。和set不同的地方是,map会多一个修饰key的参数value

在map中,这两个值 会被封装到一个pair中,本质上pair 就是一个结构体

 

②map的性质:

1.和set一样 底层用红黑树实现

2.只能存一个key值。但可以存同一value

3.map支持[]访问

 

使用map的时候,需要注意的是 他底层去重载的[].

 [],返回的是pair中value的值 , 并可以对其进行修改。

与map相对的也有mutltimap:

 


(3)map/set的底层封装:

重新搭建BRTree框架;

我们在实现红黑树的时候,对于 节点存储的模板为K,V。

怎么能让底层,去同时封装 不同存储类型的容器?!

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

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

所以我么你相应对 红黑树的节点 再次泛化。因为它并不知道存储的是什么。

template<class K,class T,class KeyOFT>
struct RBTree
{
  typedef RBTreeNode<T> Node;
	RBTree()
		:_root(nullptr)
	{}
  
   Node* _root;
}

我们不妨给红黑树的模板增加一个参数。让T 去决定 节点存什么!

既然T 是未知的,不同的T有不同的比较方法

KeyOFT----->就去弥补 实现不同T数据 的比较。(实现的仿函数)

 


 

set/map框架:4

template<class K,class T>
class map
{
public:
	struct MapOFT
	{
	   	const K& operator()(const pair<K, V>& kv)
		{
			return kv.first;
		}
	};
private:
	RBTree<K, pair<const K, V>, MapOFT> _m;
};


template<class K>
class set
{
public:
	struct SetOFT
	{
		K& operator()(const K& key)
		{
			return key;
		}
	};
private:
	RBTree<K, K, SetOFT> _s;
};

三者关系整理成图就成了这样。 这种设计极为巧妙。

BRtree迭代器封装; 

template<class T,class Ref,class Ptr>  //这里的迭代器封装和 list 极为相似 所谓的红黑树迭代器 就是指的 红黑树的节点
struct _RBTree_iterator
{
	typedef RBTreeNode<T> Node;
	typedef _RBTree_iterator<T, Ref,Ptr> Self;

	_RBTree_iterator(Node* node)
		:_node(node)
	{}
	Node* _node;

	//opertor*
	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;
	}

	//难点;
	//set/map走的是一个 中序	
	Self& operator++()
	{
		//此时_node 一定是最左节点
		if (_node->_right)
		{
			 //此时如果不为空
			//证明右边还需要访问;
			Node* left = _node->_right;
			while (left->_left)
			{
				left = left->_left;
			}

			//此时找到 不为空的 右边的 第一个左边给 _node
			_node = left;
		}
		else
		{
			//由node出发 
			//找到 右不为空的 父节点
			Node* cur = _node;
			Node* parent = cur->_parent;

			while (parent && cur == parent->_right)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}

			//下一次进来 就是去访问 _node 右根
			_node = parent;
 		}
          return *this;
	}

	//右子树 根 左子树
	Self& operator--()
	{
		if (_node->_left)
		{
			Node* right = _node->_left;
			while (right->_right)
			{
				right = right->_right;
			}

			_node = right;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;

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

operator++; 

 

 operator--;

 

 

RBTree封装迭代器+set/map使用:
 

template<class K,class T>
class map
{
public:
	struct MapOFT
	{
	   	const K& operator()(const pair<K, V>& kv)
		{
			return kv.first;
		}
	};
     //去红黑树 里找到 被封装好的 迭代器
	//此时要添加typename 因为迭代器实例化要在后面才进行 
	// 这里要让编译器 这是个类
	typedef typename RBTree<K, pair<const K, V>, MapOFT>:: iterator iterator;

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

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

private:
	RBTree<K, pair<const K, V>, MapOFT> _m;
};


template<class K>
	class set
	{
	public:
		struct SetOFT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

		typedef typename RBTree<K, K, SetOFT> ::iterator  iterator;

		iterator begin()
		{
			return _s.begin();
		}

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

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

	private:
		RBTree<K, K, SetOFT> _s;
	};

封装完成,我们就先拿来用一用;

 

 map/set大致的玩法 行得通。

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

当然不能忘了map 重载的[];


 

(4)反向迭代器:

这里的反向迭代器,并非来自迭代器萃取。而是由正向迭代器构造出来的。

template<class Iterator>
struct _RBTree_Reverse_iterator
{  
	//这里只设置了一个 模板参数 
	//因为 反向迭代器 可以借用正向迭代器的 其他两个模板参数
	//同样这是 内置类型的模板 需要typename
	typedef typename Iterator::reference Ref;
	typedef typename Iterator::pointer  Ptr;

	typedef _RBTree_Reverse_iterator<Iterator> Self;
	Iterator _it;

};

 

template<class Iterator>
struct _RBTree_Reverse_iterator
{  
	//这里只设置了一个 模板参数 
	//因为 反向迭代器 可以借用正向迭代器的 其他两个模板参数
	//同样这是 内置类型的模板 需要typename
	typedef typename Iterator::reference Ref;
	typedef typename Iterator::pointer  Ptr;

	typedef _RBTree_Reverse_iterator<Iterator> Self;

	Iterator _it;

	//拿正向 迭代器初始化
	_RBTree_Reverse_iterator(Iterator it)
		:_it(it)
	{}

	//operator*
	Ref& operator*()
	{
		return *_it;
	}
	
	Self& operator++()
	{
		--_it;
		return *this;
	}

	Self& operator--()
	{
		++_it;
		return *this;
	}

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

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

	Ptr operator->()
	{
		return _it.operator->();
	}
};

 

 


本篇也就到此为止。

感谢你的阅读

祝你好运~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值