map/set封装

目录

  • 1. set和map的底层结构
    • 1.1 红黑树
    • 1.2 set
    • 1.3 map
  • 2. 模拟实现
    • 2.1 红黑树
    • 2.1 map和set以及仿函数
    • 2.3 迭代器
      • 2.3.1 const迭代器
    • 2.3 set和map封装

1. set和map的底层结构

1.1 红黑树

这两个容器底层都是对红黑树的封装,因此需要先看一下红黑树结构部分的底层源码:

//树结点的定义:
struct __rb_tree_node_base
{
  typedef __rb_tree_color_type color_type;
  typedef __rb_tree_node_base* base_ptr;

  color_type color; 
  base_ptr parent;
  base_ptr left;
  base_ptr right;
};
//子类继承
template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
  typedef __rb_tree_node<Value>* link_type;
  //存储有效数据
  Value value_field;
};

//红黑树部分成员
template <class Key, class Value, class KeyOfValue, class Compare,
          class Alloc = alloc>
class rb_tree {
protected:
  typedef __rb_tree_node<Value> rb_tree_node;
public:
  typedef Key key_type;
  typedef Value value_type;
  typedef rb_tree_node* link_type;
protected:
  size_type node_count; // keeps track of size of tree
  link_type header;  
  Compare key_compare;
 }

可以发现红黑树的结点中存储的元素类型只与第二个模板参数Value有关,对于set而言value是Key,对于map而言value是pair类型。

Key也需要传递,树中有些接口的实现需要用到,比如find等。

1.2 set

set底层结构的部分构成:

template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:
  // typedefs:
  typedef Key key_type;
  typedef Key value_type;
  typedef Compare key_compare;
  typedef Compare value_compare;
private:
  ///红黑树
  typedef rb_tree<key_type, value_type, 
                  identity<value_type>, key_compare, Alloc> rep_type;
  rep_type t;  // red-black tree representing set
 }

set是K模型,因此key_type是Key,value_type也是Key,实例化后树中要存储的元素类型也是Key类型。

1.3 map

template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:
// typedefs:
  typedef Key key_type;
  typedef T data_type;
  typedef T mapped_type;
  typedef pair<const Key, T> value_type;
  typedef Compare key_compare;

private:
  typedef rb_tree<key_type, value_type, 
                  select1st<value_type>, key_compare, Alloc> rep_type;
  rep_type t;  // red-black tree representing map
}

对于map而言,key_type是Key,而value_type则是一个pair类型,因此实例化后树中的结点存储的就是pair类型。

2. 模拟实现

2.1 红黑树

由于map和set用到的是同一个类模板,因此红黑树类模板的设计要支持泛型。

树结点的定义:

template<class T>
struct RBTreeNode {
	RBTreeNode(const T& data)
		:_data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED)
	{ }
	T _data;
	enum Color _col;
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
};

对于set而言,T就是key,而对于map而言T为pair类型。

树的定义:

template<class K, class T, class KeyOfT>
//第三个模板参数为仿函数
//作用是取出date中的key来进行比较
//插入元素时要按照key的关键码进行比较选择合适的插入位置
//但是插入的是data,对于map来说data无法按照预期进行比较
//因为data的类型T为pair,需要取出data中的key
//而对于set而言,可以用data比,因为data的类型T就是key
//不过为了统一处理,都通过仿函数来取出key
class RBTree {
	typedef RBTreeNode<T> Node;
public:
	typedef Iterator<T> iterator;
public:
	pair<iterator, bool> insert(const T& data) {
		if (!_root) {
			_root = new Node(data);
			_root->_col = BLACK;
			return pair<iterator, bool>(_root, true);
		}
		
		Node* cur = _root;
		Node* parent = nullptr;
		//定义仿函数对象
		KeyOfT kot;
		while (cur) {
			//取出data中的key进行比较
			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 pair<iterator, bool>(cur, false);
			}
		}
		cur = new Node(data);
		pair<iterator, bool> ret(cur, true);
		if (kot(parent->_data) < kot(data)) {
			parent->_right = cur;
		}
		else {
			parent->_left = cur;
		}
		cur->_parent = parent;

		//parent为红需要一直往上调整
		while (parent && parent->_col == RED) {
			Node* grandpa = parent->_parent;
			if (grandpa->_left == parent) {
				Node* uncle = grandpa->_right;
				//叔叔存在且为红
				if (uncle && uncle->_col == RED) {
					//把父亲和叔叔染黑
					//爷爷染红继续往上调整
					parent->_col = uncle->_col = BLACK;
					grandpa->_col = RED;

					cur = grandpa;
					parent = cur->_parent;
				}
				//叔叔不存在或者存在且为黑
				else {
					//单纯的一边高(直线)单旋
					if (cur == parent->_left) {
						rotateRight(grandpa);
						parent->_col = BLACK;
						cur->_col = grandpa->_col = RED;
					}
					//折线情况需要双旋
					else {
						rotateLeft(parent);
						rotateRight(grandpa);
						cur->_col = BLACK;
						parent->_col = grandpa->_col = RED;
					}
					break;
				}
			}
			//同样的逻辑
			else {
				Node* uncle = grandpa->_left;
				if (uncle && uncle->_col == RED) {
					parent->_col = uncle->_col = BLACK;
					grandpa->_col = RED;
					
					cur = grandpa;
					parent = cur->_parent;
				}
				else {
					if (parent->_right == cur) {
						rotateLeft(grandpa);
						grandpa->_col = cur->_col = RED;
						parent->_col = BLACK;
					}
					else {
						rotateRight(parent);
						rotateLeft(grandpa);
						parent->_col = grandpa->_col = RED;
						cur->_col = BLACK;
					}
					break;
				}		 
			}
		}
		_root->_col = BLACK;
		return ret;
	}	 
private:
	Node* _root = nullptr;
};

2.1 map和set以及仿函数

set:

template<class K>
class set {
	struct SetKeyOfT {
		const K& operator()(const K& key) {
			return key;
		}
	};
	//显式传递仿函数
	typedef RBTree<K, const K, SetKeyOfT>	rbt;
private:
	rbt t;
};

map:

template<class K, class T>
class map {
	//内部类
	struct MapKeyOfT {
		const K& operator()(const pair<const K, T>& key) {
			return key.first;
		}
	};
	typedef RBTree<K, pair<const K, T>, MapKeyOfT>	rbt;
private:
	rbt t;
};

仿函数的作用在于统一取出结点中的Key进行比较。

2.3 迭代器

map和set通过迭代器进行遍历时,本质是对这颗树进行中序遍历,得到的结果是有序的,因此迭代器中最重要的功能是++和–的实现逻辑,对于++,本质是中序遍历时的下一个位置,具体可分为两步:

  1. 右子树不为空时,需要去找到右子树的最小结点也就是最左节点。
  2. 右子树为空时,说明当前子树遍历完毕,需要去找到一个根结点(可能是整棵树的根也可能是某颗子树的根),该根结点的左子树是我或者是我的某个祖先结点,因为中序遍历的顺序是:左-根-右
self& operator++() {
	//右子树不为空,去找右树的最左结点
	if (_it->_right) {
		Node* leftMin = _it->_right;
		while (leftMin && leftMin->_left) {
			leftMin = leftMin->_left;
		}
		_it = leftMin;
	}
	//为空
	//去找孩子是父亲的左的那个根结点
	else {
		Node* cur = _it;
		Node* parent = cur->_parent;
		while (parent && cur == parent->_right) {
			cur = cur->_parent;
			parent = parent->_parent;
		} 
		_it = parent;
	}
	return *this;
}

2.3.1 const迭代器

由于set中的元素不允许修改,所以set的普通和const迭代器都是树中的const迭代器:
map/set封装_第1张图片
map/set封装_第2张图片
但是这种设计会出现一个问题:
map/set封装_第3张图片

会导致类型不匹配,这是因为树的insert返回值pair中的迭代器类型是普通迭代器,而set中的迭代器的类型是const类型,普通迭代器无法构造const迭代器,所以会报错,解决方法为:设计一个支持普通迭代器转化为cons迭代器的构造函数。
map/set封装_第4张图片
对于实例化const迭代器类型的模板类而言,该函数是构造函数,而对于实例化普通迭代器类型的模板类而言,该函数是拷贝构造,通过该函数即可解决上述问题。

map的普通和const迭代器的设计则与其它容器是类似的,只是不管是普通还是const迭代器都无法对key进行修改,而value是否能修改则取决于迭代器的具体类型。

迭代器整体实现代码:

template <class T, class Ref, class Ptr>
struct Iterator {
	typedef Iterator<T, Ref, Ptr>			self;
	typedef Iterator<T, T&, T*>				iterator;
	typedef Iterator<T, const T&, const T*> const_iterator;

	typedef RBTreeNode<T> Node;

	Iterator(const iterator& it) :_it(it._it)
	{  }

	Iterator(Node* it) :_it(it)
	{  }

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

	bool operator==(const self& s) const {
		return !(*this != s);
	}

	self& operator++() {
		if (_it->_right) {
			Node* leftMin = _it->_right;
			while (leftMin && leftMin->_left) {
				leftMin = leftMin->_left;
			}
			_it = leftMin;
		}
		else {
			Node* cur = _it;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right) {
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_it = parent;
		}
		return *this;
	}

	self& operator--() {
		if (_it->_left) {
			Node* leftMax = _it->_left;
			while (leftMax && leftMax->_right) {
				leftMax = leftMax->_right;
			}
			_it = leftMax;
		}
		else {
			Node* cur = _it;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left) {
				cur = cur->_parent;
				parent = parent->_parent;
			}
			_it = parent;
		}
		return *this;
	}

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

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

	Node* _it;
};

2.3 set和map封装

set:

#include "RBTree.h"

namespace lzh {
	template<class K>
	class set {
		struct SetKeyOfT {
			const K& operator()(const K& key) {
				return key;
			}
		};
		typedef RBTree<K, K, SetKeyOfT>	rbt;
	public:
		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;

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

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

		pair<iterator, bool> insert(const K& data) {
			pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> p = _t.insert(data);
			return pair<iterator, bool>(p.first, p.second);
		}

	private:
		rbt _t;
	};
};

map:

#include "RBTree.h"

namespace lzh {
	template<class K, class T>
	class map {
		struct MapKeyOfT {
			const K& operator()(const pair<const K, T>& key) {
				return key.first;
			}
		};
		typedef RBTree<K, pair<const K, T>, MapKeyOfT>	rbt;
	public:
		typedef  typename RBTree<K, pair<const K, T>, MapKeyOfT>::iterator		 iterator;
		typedef  typename RBTree<K, pair<const K, T>, MapKeyOfT>::const_iterator const_iterator;
	public:
		iterator begin() {
			return t.begin();
		}

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

		pair<iterator, bool> insert(const pair<const K, T>& data) {
			return t.insert(data);
		}

		T& operator[](const K& k) {
			return insert({ k, T() }).first->second;
		}
	private:
		rbt t;
	};
};

你可能感兴趣的:(C++,数据结构,c++)