set/map的封装,底层为红黑树.

首先看看map对K和V的定义

然后看看set的

可以发现的一点就是源代码中的K/V与我们之前定义的K/V不同,这里的K可以说没什么价值,而V自身就有了我们传统意义上的K/V。

map中成员变量的定义:

set中成员函数的定义:

从typedef的角度上讲,二者的唯一区别就是value_type的不同,且二者的本质只是红黑树模板类中的一个类模板。因此map和set的本质和list中的迭代器用法差不多,实现一个共用类,然后通过模板参数的不同来实现不同的功能。

(简化理解typedef一个模板类就是typedef一个类型,只是这个类型中有模板参数而已)

例:

cpp 复制代码
template<class K,class V>
class RBtree
{
//..
};
//set和map只是RBtree中的一个类模板
template<class K,class V>
class map
{
	typedef RBtree<K, V> __map;
	//..
	__map _map;
};
template<class K>
class set
{
	typedef RBtree<K, K> __set;
	//..
	__set _set;
};

2.要设计K和V的原因在于map中的find和pop成员函数要用K类型来找而非V类型,说白了就是set中的value没有意义,是为了map而做牺牲的。

3.模拟实现set和map的大框

map:

cpp 复制代码
template<class K,class V>
class map
{
	//const K,因为map中的K不允许修改,但map允许
	typedef RBTree<K, pair<const V,K>,mapcom> __map;
private:
	__map _map;
};

set:

复制代码
template<class K>
class set
{
	typedef RBTree<K, K> __set;
private:
	__set _set;
};

2.然后就是改造一下RBTree

(1)node的改造

cpp 复制代码
//一个模板参数即可,因为map中传进来的就是pair类型了
template<class T>
struct RBTreeNode
{
	RBTreeNode* _left;
	RBTreeNode* _right;
	RBTreeNode* _parent;
	T _val;
	int _col;
	RBTreeNode(const T& val)
		:_val(val)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(RED)
	{ }
};

(2)insert

复制代码
//修改一下形参类型即可
typedef RBTreeNode<V> Node;
bool insert(const V& val)

(3)把本来应该用interator实现的成员函数都删掉

4.pair的比较逻辑是先比较first再比较second,但在map中我们只想first进行比较,因此set和map的insert中的比较逻辑并不兼容,因此我们要分别为二者设计一个仿函数

set:

cpp 复制代码
template<class T>
struct setcom
{
	const T& operator()(const T& v)
	{
		return v;
	}
};

template<class K>
class set
{
	typedef RBTree<K, K,setcom<K>> __set;
private:
	__set _set;
};

map:

cpp 复制代码
template<class K,class V>
struct mapcom
{
	const K& opertor()(const pair<K,V>& v)
	{
		return v.first;
    }
};
template<class K,class V>
class map
{
	//const K,因为map中的K不允许修改,但map允许
	typedef RBTree<K, pair<const V,K>,mapcom<K,V>> __map;
private:
	__map _map;
};

RBtree中:

cpp 复制代码
多一个传仿函数的模板参数
template<class K, class V,class typecom>
class RBTree
//insert中的比较逻辑的举例
if (com(cur->_val) < com(val))
{
	parent = cur;
	cur = cur->_right;
}
else if (com(cur->_val) > com(val))
{
	parent = cur;
	cur = cur->_left;
}

5.在set和map中调用函数的方式就是套壳

例set中:

复制代码
bool insert(const K& key)
{
return _set.insert(key);
}

6.set和map的itertator

set和map的iterator就是RBtree对应模板类型内部的原生迭代器。

原因也很简单:set和map就是给RBtree中的模板类,其内部结构就是红黑树,因此是包装RBtree的迭代器也很合理。

(1)iterator的遍历是按中序遍历,因此begin()返回的是中序的第一个结点。end()返回nullptr(没有头结点时)

(2)大框还是指针+重载

(3)遍历逻辑

{

1.当前结点右子树不为空就去找右子树的最左结点(说明该树未遍历完)

2.当前结点右子树为空就去找祖先(当前树已遍历完)

}

RBtree中的iterator代码大框

cpp 复制代码
template<class T, class ref, class ptr>
struct RBTreeItreator
{
	typedef RBTreeNode<T> Node;
	typedef RBTreeItreator<T, ref, ptr> self;
	//_root是用于处理特殊情况的
	RBTreeItreator(Node* node,Node*_root)
		:_node(node)
		,_root(_root)
	{ }
	//...(下面补充iterator的重载)
	Node* _node;
	Node* _root;
};

RBtree的begin和end实现:

cpp 复制代码
Iterator begin()
{
	Node* cur = _root;
	while (cur&&cur->_left)
	{
		cur = cur->_left;
	}
	return	Iterator(cur, _root);
}
Iterator end()
{
	return Iterator(nullptr, _root);
}

RBtree的iterator的++重载

cpp 复制代码
self& operator++()
{
    //将_node更新到右子树的最左结点
    if (_node->_right)
    {
        Node* cur = _node->_right;
        while (cur->_left)
        {
            cur = cur->_left;
        }
        _node = cur;
    }
    else
    {
        Node* parent = _node->_parent;
        Node* cur = _node;
        //parent==nullptr说明cur到根了
        //cur != parent->_right说明目前这颗树未遍历完
        while (parent && cur==parent->_right)
        {
            cur = parent;
            parent = parent->_parent;
        }
        //此处的_node要么是遍历完的nullptr,要么是下一个要遍历的根节点
        _node = parent;
    }
    return *this;
}

由于我的能力有限,因此只能改变写的方式了。

--重载

这里想说明的就是_root成员的意义,当想iterator从end()开始的反向遍历,就需要对_node==nullptr的情况进行特殊处理,让其在这中情况下走到最右结点处。

cpp 复制代码
Self operator--()
{
	if (_node == nullptr)  // --end()
	{
		// --end(),特殊处理,走到中序最后一个结点,整棵树的最右结点
		Node* rightMost = _root;
		while (rightMost && rightMost->_right)
		{
			rightMost = rightMost->_right;
		}
		_node = rightMost;
	}
	else if (_node->_left)
	{
		// 左子树不为空,中序左子树最后一个
		Node* rightMost = _node->_left;
		while (rightMost->_right)
		{
			rightMost = rightMost->_right;
		}
		_node = rightMost;
	}
	else
	{
		// 孩子是父亲右的那个祖先
		Node* cur = _node;
		Node* parent = cur->_parent;
		while (parent && cur == parent->_left)
		{
			cur = parent;
			parent = cur->_parent;
		}
		_node = parent;
	}

	return *this;
}

RBTree中iterator的解引用重载

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

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

这样重载有一个问题,*map时可以直接修改first(key)使树结构出问题,此时在map中的传模板参数时就要给pair中的K加上const。

cpp 复制代码
//务必注意iterator的类型也要跟着修改
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::ConstIterator const_iterator;
RBTree<K, pair<const K, V>, MapKeyOfT> _t;

哨兵位的意义:

指向树的最右和最左,其parent指向head,好处就是不用判空和begin(),end()的获取简单,坏处就是要一直维护。

map中的[]重载

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

此时就要修改一下insert的返回值了。

cpp 复制代码
pair<Iterator, bool> Insert(const T& data)
	{
      //判空
		if (_root == nullptr);
		
		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 { Iterator(cur, _root), false };
			}
		}

		cur = new Node(data);
		Node* newnode = cur;
		cur->_col = RED;
		if (kot(parent->_data) < kot(data))
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		//..后面的处理都删了

		
		return { Iterator(newnode, _root), true };
	}

后面写出这次的重要代码以便我以后复习:

map:

cpp 复制代码
	template<class K, class V>
	class map
	{
//仿函数可以为内部类
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};

	public:
                     //const K使解引用时K不可被修改
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::Iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::ConstIterator 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<K, V>& kv)
		{
			return _t.Insert(kv);
		}

		V& operator[](const K& key)
		{
//注意V()的默认构造
			pair<iterator, bool> ret = insert({ key, V() });
//两层pair
			return ret.first->second;
		}

	private:
//K的意义可以说没有,V的类型才重要
		RBTree<K, pair<const K, V>, MapKeyOfT> _t;
	};

set:

复制代码
cpp 复制代码
template<class K>
//没有[]重载
	class set
	{
//可以说这里的仿函数就是为了map才有的
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};

	public:
		typedef typename RBTree<K, const K, SetKeyOfT>::Iterator iterator;
		typedef typename RBTree<K, const K, SetKeyOfT>::ConstIterator 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 K& key)
		{
			return _t.Insert(key);
		}
	private:
//set的K直接就不允许修改
		RBTree<K, const K, SetKeyOfT> _t;
	};

RBTree:

cpp 复制代码
template<class T>
struct RBTreeNode
{
	// 这里更新控制平衡也要加入parent指针
	T _data;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	Colour _col;

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

template<class T, class Ref, class Ptr>
struct RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T, Ref, Ptr> Self;

	Node* _node;
	Node* _root;


	RBTreeIterator(Node* node, Node* root)
		:_node(node)
		,_root(root)
	{}

	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 = parent;
				parent = cur->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	

	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;
	}
};

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*> ConstIterator;
	ConstIterator Begin() const
	{
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}

		return ConstIterator(cur, _root);
	}

	ConstIterator End() const
	{
		return ConstIterator(nullptr, _root);
	}

	pair<Iterator, bool> Insert(const T& data)
	{
		
		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 { Iterator(cur, _root), false };
			}
		}

		cur = new Node(data);
		Node* newnode = cur;
		cur->_col = RED;
		if (kot(parent->_data) < kot(data))
		{
			parent->_right = cur;
		}
	//..
//要注意对iterator的创建与初始化
return { Iterator(cur, _root), true };
}

		
			
private:
	Node* _root = nullptr;
};

insert返回有iterator的pair,iterator由Node*构造。

相关推荐
晨非辰2 小时前
Git版本控制速成:提交三板斧/日志透视/远程同步15分钟精通,掌握历史回溯与多人协作安全模型
linux·运维·服务器·c++·人工智能·git·后端
gdizcm2 小时前
linux判断文件类型的多种方法
linux·c++
云栖梦泽2 小时前
Linux内核与驱动:3.驱动模块传参,内核模块符号导出
linux·服务器·c++
南境十里·墨染春水10 小时前
C++传记(面向对象)虚析构函数 纯虚函数 抽象类 final、override关键字
开发语言·c++·笔记·算法
2301_7971727510 小时前
基于C++的游戏引擎开发
开发语言·c++·算法
比昨天多敲两行11 小时前
C++ 二叉搜索树
开发语言·c++·算法
Season45011 小时前
C++11之正则表达式使用指南--[正则表达式介绍]|[regex的常用函数等介绍]
c++·算法·正则表达式
问好眼12 小时前
《算法竞赛进阶指南》0x04 二分-1.最佳牛围栏
数据结构·c++·算法·二分·信息学奥赛
海海不瞌睡(捏捏王子)12 小时前
C++ 知识点概要
开发语言·c++