set 和 map 的封装

目录

1. set 和 map 的封装

2. 红黑树的迭代器

2.1. 普通迭代器 

2.1.1. 前置++

2.1.2. 前置--

2.1.3. 普通迭代器的完整实现

2.2. const 迭代器

2.2.1. set 的 const 迭代器

2.2.2. map 的 const 迭代器

3. insert 的实现

4. map 中的operator[]

5. 完整的封装

5.1. set 的封装

5.2. map 的封装

5.3. 底层红黑树


1. set 和 map 的封装

我们说过, set 和 map 底层的数据结构都是红黑树,难道说,set 需要写一颗红黑树,map 也要写一棵红黑树?

因为,set 是只有 Key 的,而 map 是需要一个键值对 ( Key - Value ) 的。 

上述方法的确可以,但是太挫了,而 STL 是如何设计的呢?

set 如下:

map 如下: 

我们发现,set 和 map 所用的红黑树中有一个差异:

  • set 中的 value_type 就是 Key;
  • map 中的 value_type 是一个 pair<const Key, T>。

换言之,第二个模板参数决定了这颗红黑树是给map还是给set用的:

  • 如果第二个模板是 Key,那么这颗红黑树就是给 set 用的;
  • 如果第二个模板参数是 pair,那么这颗红黑树就是给 map 用的。

有了上面的理解,我们就可以写出 set 和 map的大致框架了。

set 封装的初步框架:

namespace Xq
{
	template<class K>
	class set
	{
	private:
		rb_tree<K, K> _tree;
	};
}

map 封装的初步框架:

namespace Xq
{
	template<class K, class V>
	class map
	{
	private:
		rb_tree<K, std::pair<K, V>> _tree;
	};
}

从上面,我们可以看出,红黑树自身是不知道它的 Value 是什么的,因此,红黑树的 Value 不能写死,即不能写成 Key,也不能写成 pair,而应该用一个泛型,换言之,此时,红黑树的节点是一个泛型,既不是 Key - Key,也不是 Key - Value,如下:

template<class T>
struct rb_tree_node
{
	rb_tree_node<T>* _left;
	rb_tree_node<T>* _right;
	rb_tree_node<T>* _parent;
	T _data;
	color _col;

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

当走到这里之后,又出现问题了,比如我们的 find,如下:

bool find(const K& data)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_data > data)
			cur = cur->_left;
		else if (cur->_data < data)
			cur = cur->_right;
		else
			return true;
	}
	return false;
}

上面的 find 对于 set 来说,没有任何问题,因为此时的 _data 就是 Key,而我们的目的本来就是要用 Key 进行比较;

但是对于 map 来说,因为此时的 _data 是一个 pair,而默认情况下的 pair 的比较不是我们需要的,我们需要的是通过 Key 进行比较。

因此这里就需要让红黑树具有一个功能,获得 set 中的 Key 和获得 map 中的 pair 中的 Key,因此,此时的红黑树需要一个类模板参数,这个模板参数是一个仿函数,红黑树通过这个模板参数获得 set 中的 Key和 map 中 pair 中的 Key。

// 这了的 key_of_t 就是用来获取 Key 的
template<class K, class T, class key_of_t>
class rb_tree
{
    // ...
};

当红黑树提供了这个模板参数 (key_of_t) ,set 和 map 就需要显示传递这个模板参数,同时,因为此时的 rb_tree 是一个类模板,故需要传递类型,如下: 

set 如下:

namespace Xq
{
	template<class K>
	class set
	{
	public:
		struct set_key_of_t
		{
			const K& operator(const K& key)
			{
				return key;
			}
		};
	private:
		rb_tree<K, K, set_key_of_t /* 这里传递的是类型*/ > _tree;
	};
}

map 如下:

namespace Xq
{
	template<class K, class V>
	class map
	{
	public:
		struct map_key_of_t
		{
			const K& operator(const std::pair<K,V>& data)
			{
				return data.first;
			}
		};
	private:
		rb_tree<K, std::pair<K, V>, map_key_of_t /* 这里传递的也是类型 */> _tree;
	};
}

此时在红黑树中,就可以通过这个仿函数正确获得 Key,无论是 set 还是 map,比如 find,如下:

bool find(const K& key)
{
	Node* cur = _root;
    // 定义一个仿函数对象
	key_of_t kot;
	while (cur)
	{
		// 如果你是set, 直接取出 Key
		// 如果你是map, 从 pair 中得到 first,也就是 Key
        // 无论是set还是map, 都是通过Key进行比较的, 符合预期.
		if (kot(cur->_data) > key)
			cur = cur->_left;
		else if (kot(cur->_data) < key)
			cur = cur->_right;
		else
			return true;
	}
	return false;
}

根据上面的思路,我们可以修改一下红黑树的 insert,如下:

bool insert(const T& data)
{
	if (!_root)
	{
		_root = new Node(data);
		_root->_col = BLACK;
		return true;
	}
	else
	{
		Node* cur = _root;
		Node* parent = nullptr;
		key_of_t kot;
		while (cur)
		{
			if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(data);
		if (kot(data) > kot(parent->_data))
			parent->_right = cur;
		else
			parent->_left = cur;
		cur->_parent = parent;

		while (parent && parent->_col == RED)
		{
            // 这里的旋转调整就省略了
		}
		_root->_col = BLACK;
		return true;
	}
}

此时,我们就可以封装一下 set 和 map的 insert了,如下:

set 如下:

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

		bool insert(const K& key)
		{
			return _tree.insert(key);
		}

	private:
		rb_tree<K, K, set_key_of_t /* 这里传递的是类型*/ > _tree;
	};
}

map 如下:

namespace Xq
{
	template<class K, class V>
	class map
	{
	public:
		struct map_key_of_t
		{
			const K& operator()(const std::pair<K,V>& data)
			{
				return data.first;
			}
		};

		bool insert(const std::pair<K,V>& kv)
		{
			return _tree.insert(kv);
		}

	private:
		rb_tree<K, std::pair<K, V>, map_key_of_t /* 这里传递的也是类型 */> _tree;
	};
}

走到这里,暂停一下,我们需要实现下红黑树的迭代器。

2. 红黑树的迭代器

迭代器的实现我们在 list 就已经详细论述过,因为 Node* 是一个内置类型,无法重载 ++,--等行为 (因为这些 Node* 的地址并非连续,指针的++,-- 不符合需求),而要重载 ++,--就需要是一个自定义类型,故,在这里就将一个 Node* 封装成一个类,这个类起了一个好听的名字:迭代器,这个迭代器通常需要实现以下操作:

加加 (++)、减减 (--)、解引用 (*)、访问成员 (->)、等于 (==)、不等于 (!=) 。

2.1. 普通迭代器 

我们先实现一个普通迭代器,大致框架如下:

template<class T>
struct tree_iterator
{
	typedef rb_tree_node<T> Node;
	typedef tree_iterator<T> Self;
	Node* _node;
	tree_iterator(Node* node = nullptr) :_node(node){}

	// 前置++
	Self& operator++() {}

	// 后置++
	Self operator++(int) {}

	// 前置--
	Self& operator--() {}

	// 后置--
	Self operator--(int) {}

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

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

	// 返回对象的引用
	T& operator*() { return _node->_data; }

	// 返回对象的地址
	T* operator->() { return &(operator*()) };
};

这里面最有难度的就是 ++和--操作了,我们只详细论述前置++和前置--了,理解了这些,后置++和后置--就水到渠成了。

2.1.1. 前置++

前置++的核心就是找中序的下一个节点,而中序遍历的规则是左子树、根、右子树

我们以下面这颗红黑树举例:

而我们是如何用迭代器的呢?如下 demo: 

void Test2(void)
{
	Xq::set<int> s = { 25, 15, 50, 10, 22, 30, 70, 18, 45 };
	Xq::set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		std::cout << *it << " ";
		++it;
	}
	std::cout << std::endl;
}

因为是中序遍历,所以 begin() 的位置,就是这棵树的最左节点,如下所示:

那么下一个节点是什么呢? 依据左子树、根、右子树,因为此时 it 是最左节点,故它不可能有左孩子,他只可能有右孩子,但是此时没有,即右孩子为空,此时说明 it 这颗子树已经遍历完了,那么下一个位置就是:

如果右孩子为空,那么下一个位置就是,孩子是父亲的左孩子的那个父节点

比如,此时的 it 正好是它父亲的左孩子,因此,下一个位置就是父节点自身。

依据左子树、根、右子树,当 it 所在的红黑树左子树和根都遍历完了,就需要遍历右子树了,因此,下一个位置:

如果右子树不为空,下一个位置就是右子树的最左节点。 如下:

这里有一种特殊情况,当 it 走到这里的时候,如下:

此时的 it 是这棵红黑树中最后的一个有效节点 (左子树、根、右子树),此时它的情况就是,右子树为空,那么就要找孩子是父亲的左孩子的父节点,过程如下:

因为,cur_parent->_left != cur,故继续向上遍历,cur = cur_parent,cur_parent = cur_parent->_parent,可以看出,这个过程是依赖于三叉链的,如下: 

同上, cur_parent->_left != cur,继续上述过程,cur = cur_parent,cur_parent = cur_parent->_parent,但此时就出问题了,此时的 cur_parent->_parent 等于 nullptr (根节点没有父亲),因此,无法找到孩子是父亲的左孩子的那个父节点,故当 cur_parent 走到空时,结束循环,并将 nullptr 构造一个迭代器并返回,这个迭代器就是 end(),如下:

总结: 前置++的大致实现思路如下:

  • 如果右子树不为空,下一个位置就是,右子树的最左节点;
  • 如果右子树为空,下一个位置就是:
    • 如果可以找到,孩子是父节点的左孩子,那么下一个位置就是这个父节点;
    • 如果不能找到,孩子是父节点的左孩子,即 cur_parent 走到了空,那么下一个位置就是 nullptr,即 end()。

有了上面理解,代码如下:

// 前置++
Self& operator++() 
{
	// 如果右子树不为空, 下一个位置就是右子树的最左节点
	if (_node->_right)
	{
		Node* left_node = _node->_right;
		while (left_node->_left)
			left_node = left_node->_left;
		_node = left_node;
	}
	// 如果右子树为空
	else
	{
		// 找孩子是父亲的左孩子的那个父节点
		Node* cur = _node;
		Node* cur_parent = _node->_parent;
		while (cur_parent && cur_parent->_right == cur)
		{
			cur = cur_parent;
			cur_parent = cur_parent->_parent;
		}
		// 走到这里有两种情况
		// case 1: 找到了孩子是父亲的左孩子的父节点
		// case 2: 父亲走到了空, 此时的 _node 就是 nullptr
		_node = cur_parent;
	}
	return *this; 
}

2.1.2. 前置--

对于--操作,它的遍历规则我们可以理解为右子树、根、左子树。

同理,我们也以下面这颗红黑树举例,如下:

依据右子树、根、左子树,故此时的第一个节点是这棵树的最右节点, 如下:

因为此时的 it 是树的最右节点,故不存在右孩子,只可能存在左孩子,但此时的左孩子为空。

如果一个节点的左子树为空,那么下一个位置就是,孩子是父亲的右孩子的那个父节点

对于上面来说,此时的 it 正好是父亲的右孩子,故下一个位置如下所示:

依据右子树、根、左子树判定,接下来就要访问左子树了,因此:

如果一个节点的左子树不为空,那么下一个位置就是左子树的最右节点。 如下所示:

对于 -- 仍存在一种特殊情况,当 it 走到了这个位置,如下所示: 

此时的 it 是这棵红黑树中最后的一个有效节点 (右子树、根、左子树),此时它的情况就是,左子树为空,那么就要找孩子是父亲的右孩子的父节点,过程如下:

因为此时 cur_parent->_right != cur,故向上遍历,即 cur = cur_parent; cur_parent = cur_parent->_parent;

如下所示: 

同上,继续向上遍历,如下: 

总结: 前置--的大致实现思路如下:

  • 如果左子树不为空,下一个位置就是,左子树的最右节点;
  • 如果左子树为空,下一个位置就是:
    • 如果可以找到,孩子是父节点的右孩子,那么下一个位置就是这个父节点;
    • 如果不能找到,孩子是父节点的右孩子,即 cur_parent 走到了空,那么下一个位置就是 nullptr。

有了上面理解,代码如下:

// 前置--
Self& operator--() 
{
	// 依据的思想 右子树、根、左子树.
	// 如果左子树不为空, 找左子树的最右节点
	if (_node->_left)
	{
		Node* right_node = _node->_left;
		while (right_node->_right)
			right_node = right_node->_right;
		_node = right_node; 
	}
	// 如果左子树为空, 找孩子是父亲的右孩子的那个父亲
	else
	{
		Node* cur = _node;
		Node* cur_parent = cur->_parent;
		while (cur_parent && cur_parent->_left)
		{
			cur = cur_parent;
			cur_parent = cur_parent->_parent;
		}
		_node = cur_parent;
	}
	return *this;
}

2.1.3. 普通迭代器的完整实现

有了这些,剩下就简单了,普通迭代器的实现如下:

template<class T>
struct tree_iterator
{
	typedef rb_tree_node<T> Node;
	typedef tree_iterator<T> Self;
	Node* _node;
	tree_iterator(Node* node = nullptr) :_node(node){}

	// 前置++
	Self& operator++() 
	{
		// 如果右子树不为空, 下一个位置就是右子树的最左节点
		if (_node->_right)
		{
			Node* left_node = _node->_right;
			while (left_node->_left)
				left_node = left_node->_left;
			_node = left_node;
		}
		// 如果右子树为空
		else
		{
			// 找孩子是父亲的左孩子的那个父节点
			Node* cur = _node;
			Node* cur_parent = _node->_parent;
			while (cur_parent && cur_parent->_right == cur)
			{
				cur = cur_parent;
				cur_parent = cur_parent->_parent;
			}
			// 走到这里有两种情况
			// case 1: 找到了孩子是父亲的左孩子的父节点
			// case 2: 父亲走到了空, 此时的 _node 就是 nullptr
			_node = cur_parent;
		}
		return *this; 
	}

	// 后置++
	Self operator++(int) {}

	// 前置--
	Self& operator--() 
	{
		// 依据的思想 右子树、根、左子树.
		// 如果左子树不为空, 找左子树的最右节点
		if (_node->_left)
		{
			Node* right_node = _node->_left;
			while (right_node->_right)
				right_node = right_node->_right;
			_node = right_node; 
		}
		// 如果左子树为空, 找孩子是父亲的右孩子的那个父亲
		else
		{
			Node* cur = _node;
			Node* cur_parent = cur->_parent;
			while (cur_parent && cur_parent->_left)
			{
				cur = cur_parent;
				cur_parent = cur_parent->_parent;
			}
			_node = cur_parent;
		}
		return *this;
	}

	// 后置--
	Self operator--(int) {}

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

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

	// 返回对象的引用
	T& operator*()
	{
		return _node->_data;
	}

	// 返回对象的地址
	T* operator->() 
	{ 
		return &(operator*());
	};
};

实现了迭代器后, 红黑树自身也需要提供一些接口,如下:

template<class K, class T, class key_of_t>
class rb_tree
{
private:
	typedef rb_tree_node<T> Node;
public:
	typedef tree_iterator<T> iterator;

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

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

    // ... 省略

private:
    Node* _root;
}

此时 set 和 map 就可以使用这个迭代器了。

set 如下:

template<class K>
class set
{
public:

	struct set_key_of_t
	{
		const K& operator()(const K& key)
		{
			return key;
		}
	};
	// 对类模板取内置类型,加 typename 告诉编译器这个是类型, 而不是静态变量
	typedef typename rb_tree<K, K, set_key_of_t>::iterator iterator;

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

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

}
private:
	rb_tree<K, K, set_key_of_t /* 这里传递的是类型*/ > _tree;
};

map 如下:

template<class K, class V>
class map
{
public:
	struct map_key_of_t
	{
		const K& operator()(const std::pair<K,V>& data)
		{
			return data.first;
		}
	};

	// 对类模板取内置类型,加 typename 告诉编译器这个是类型, 而不是静态变量
	typedef typename rb_tree<K, std::pair<K, V>, map_key_of_t>::iterator iterator;

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

	iterator end()
	{
		return _tree.end();
	}
private:
	rb_tree<K, std::pair<K, V>, map_key_of_t /* 这里传递的也是类型 */> _tree;
};

2.2. const 迭代器

在这里提示一下,这里是在标题3和标题4的基础之上进行的。

有了上面的基础,我们实现 const 迭代器就简单了,如下:

template<class T, class Ref, class Ptr>
struct tree_iterator
{
	typedef rb_tree_node<T> Node;
	typedef tree_iterator<T, Ref, Ptr> Self;

	Node* _node;
	tree_iterator(Node* node = nullptr) :_node(node){}

	Self& operator++() { /*省略*/}
	Self operator++(int) {}
	Self& operator--() { /*省略*/}
	Self operator--(int) {}
	bool operator==(const Self& cmp) { /*省略*/}
	bool operator!=(const Self& cmp) { /*省略*/}

	Ref operator*() { /*省略*/}
	Ptr operator->()  { /*省略*/}
};
template<class K, class T, class key_of_t>
class rb_tree
{
private:
	typedef rb_tree_node<T> Node;
public:
    // 如果你是普通迭代器, 那么 Ref 就是 T&, Ptr 就是 T*
	typedef tree_iterator<T, T&, T*> iterator; 
    // const 迭代器, 那么 Ref 就是 const T&, Ptr 就是 const T*
	typedef tree_iterator<T, const T&, const T*> const_iterator;
    
    iterator begin()
	{
		Node* cur = _root;
		while (cur->_left)
			cur = cur->_left;
		return iterator(cur);
	}

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

    const_iterator begin() const
	{
		Node* cur = _root;
		while (cur->_left)
			cur = cur->_left;
		return const_iterator(cur);
	}

	const_iterator end() const
	{
		return const_iterator(nullptr);
	}
    
    // 省略 ...
private:
    Node* _root;
};

那么 set 和 map 的const 迭代器如何实现呢? 

对于 set 而言,它的 Key 不可以修改。

对于 map 而言,Key 不可以修改,但是 Value 可以修改。

SGI-STL 中的 set 的迭代器如下:

SGI-STL 中的 map 的迭代器如下:

可以看到, set 的普通迭代器和 const 迭代器都是 const 迭代器, map 的普通迭代器就是普通迭代器, const 迭代器是 const 迭代器。

2.2.1. set 的 const 迭代器

我们按照STL的实现思路,将 set 的普通迭代器和const迭代器都设置为const迭代器,如下:

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

	// 对类模板取内置类型,加 typename 告诉编译器这个是类型, 而不是静态变量
    // 普通迭代器也是const迭代器
	typedef typename rb_tree<K, K, set_key_of_t>::const_iterator iterator;
    // const迭代器
	typedef typename rb_tree<K, K, set_key_of_t>::const_iterator const_iterator;

	// 对于 set 而言, 这里只需要提供一个返回const迭代器的begin和end就可以了
	const_iterator begin() const { return _tree.begin(); }
	iterator end() const { return _tree.end(); }

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

private:
	rb_tree<K, K, set_key_of_t /* 这里传递的是类型*/ > _tree;
};

通过下面的 demo 测试上面有没有问题:

void Test2(void)
{
	int a[] = { 25, 15, 50, 10, 22, 30, 70, 18, 45 };
	Xq::set<int> s;
	for (auto & it : a)
		s.insert(it);
	Xq::set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		std::cout << *it << " ";
		++it;
	}
	std::cout << std::endl;
}

编译代码,得到如下问题: 

原因如下:

此时Xq::rb::insert 返回值中的iterator 和 Xq::set::insert 返回值中的 iterator 并不是一个类型,此时是两个不相干的类型,无法直接转换。 

在这里的一个简单解决方案就是更改 Xq::rb_tree::insert,pair 的first 不要固定为迭代器,而是固定为一个节点,如下所示:

std::pair<Node*, bool> insert(const T& data)
{
	if (!_root)
	{
		_root = new Node(data);
		_root->_col = BLACK;
		return {_root, true};
	}
	else
	{
		Node* cur = _root;
		Node* parent = nullptr;
		key_of_t kot;
		while (cur)
		{
			if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return{ nullptr, false };
			}
		}
		cur = new Node(data);
		// 由于下面的旋转可能会更改cur, 因此提前保存一下
		Node* RetNode = cur;
		if (kot(data) > kot(parent->_data))
			parent->_right = cur;
		else
			parent->_left = cur;
		cur->_parent = parent;

		while (parent && parent->_col == RED){ // 旋转逻辑省略 }
		_root->_col = BLACK;

		return std::pair<Node*, bool>(RetNode, true);
	}
}

此时在 Xq::set::insert 中,返回的 pair 是一个 std::pair<Node*, bool>;

std::pair<iterator, bool> insert(const K& key)
{
    // 返回的是一个 std::pair<Node*, bool>
    // 此时会调用 std::pair 的拷贝构造
    // 对于pair的first, 会调用 iterator 的构造
    // 这里的 iterator 实际上就是 const_iterator
	return _tree.insert(key);
}

此时就解决了这个问题,这是一种很巧妙的方式,这种方案的基础是 std::pair 提供了一个拷贝构造,如下:

template<class T1, class T2>
struct pair
{
	template<class U, class V>
	pair(const pair<U, V>& pr)
		:first(pt.first)
		, second(pt.second)
	{}

	T1 first;
	T2 second;
};

如果U的类型就是T1的类型,V的类型就是T2的类型,那么初始化列表中就是拷贝构造; 

如果他们类型不匹配,那么初始化列表中就是构造。

就例如,上面的insert:

  • Xq::rb_tree::insert 中的返回值类型为 pair<Node*, bool>;
  • Xq::set::insert 中的返回值类型为 pair<iterator, bool>;

此时两个pair的first的类型就不一致,此时在初始化列表中就会调用构造函数,即用一个 Node* 构造一个迭代器。

2.2.2. map 的 const 迭代器

class map
{
public:
	struct map_key_of_t
	{
		const K& operator()(const std::pair<K,V>& data)
		{
			return data.first;
		}
	};

	// 对类模板取内置类型,加 typename 告诉编译器这个是类型, 而不是静态变量
	// 普通迭代器
	typedef typename rb_tree<K, std::pair<K, V>, map_key_of_t>::iterator iterator;
	// const迭代器
	typedef typename rb_tree<K, std::pair<K, V>, map_key_of_t>::const_iterator const_iterator;

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

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

    const_iterator begin() const { return _tree.begin(); }

    const_iterator end() const { return _tree.end(); }

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

	V& operator[](const K& key)
	{
		std::pair<iterator, bool> ret = insert(std::make_pair(key, V()));
	}

private:
    rb_tree<K, std::pair<K, V>, map_key_of_t /* 这里传递的也是类型 */> _tree;
};

而 map 需要注意一个问题,它的Key是不能修改的,但是Value是可以修改的,这该如何实现呢? 很简单,只需要在实例化类模板的时候,用const修饰K就可以了 (std::pair中的Key),如下:

class map
{
public:
    // 在实例化类模板的时候, 用const修饰Key
	typedef typename rb_tree<K, std::pair<const K, V>, map_key_of_t>::iterator iterator;
    // 在实例化类模板的时候, 用const修饰Key
	typedef typename rb_tree<K, std::pair<const K, V>, map_key_of_t>::const_iterator const_iterator;
    
    // 省略
private:
    // 在实例化类模板的时候, 用const修饰Key
    rb_tree<K, std::pair<const K, V>, map_key_of_t /* 这里传递的也是类型 */> _tree;
};

3. insert 的实现

上面我们已经完成了迭代器的实现,有了迭代器,我们就需要重新调整一下 insert 的返回值了。

// std::set::insert
pair<iterator,bool> insert (const value_type& val);

// std::map::insert
pair<iterator,bool> insert (const value_type& val);

红黑树的 insert,更改如下:

// Xq::rb_tree::insert

std::pair<iterator, bool> insert(const T& data)
{
	if (!_root)
	{
		_root = new Node(data);
		_root->_col = BLACK;
		return {iterator(_root), true};
	}
	else
	{
		Node* cur = _root;
		Node* parent = nullptr;
		key_of_t kot;
		while (cur)
		{
			if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else
			{
				return{ iterator(nullptr), false };
			}
		}
		cur = new Node(data);
		// 由于下面的旋转可能会更改cur, 因此提前保存一下
		Node* RetNode = cur;
		if (kot(data) > kot(parent->_data))
			parent->_right = cur;
		else
			parent->_left = cur;
		cur->_parent = parent;

		// 当父节点parent的颜色为红色时,需要调整
		while (parent && parent->_col == RED)
		{
            // 旋转逻辑直接干掉
		}
		_root->_col = BLACK;
		return std::pair<iterator, bool>(iterator(RetNode), true);
	}
}

set 中的 insert,如下:

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

map 中的 insert,如下:

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

4. map 中的operator[]

有了 insert 的实现,operator[] 就手到擒来了,因为 operator[] 返回的是 Value 的引用,如下所示:

V& operator[](const K& key)
{
	std::pair<iterator, bool> ret = insert(std::make_pair(key, V()));
	// ret.first 返回的是一个迭代器, eg. iterator(Node*)
	// ret.first->  这里实际上是有两个 -> , 编译器为了可读性, 省去了一个 ->
	// 第一个 -> 在调用 operator-> , 返回数据的指针, 即返回的是 pair<K,V>*
	// 第二个 -> 访问pair的second成员, 即Value
	return ret.first->second;
}

5. 完整的封装

5.1. set 的封装

#ifndef _MYSET_HPP_
#define _MYSET_HPP_

#include "MyRBTree.hpp"

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

		// 对类模板取内置类型,加 typename 告诉编译器这个是类型, 而不是静态变量
		typedef typename rb_tree<K, K, set_key_of_t>::const_iterator iterator;
		typedef typename rb_tree<K, K, set_key_of_t>::const_iterator const_iterator;

		// 对于 set 而言, 这里只需要提供一个返回const迭代器的begin和end就可以了
		const_iterator begin() const { return _tree.begin(); }

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

		std::pair<iterator, bool> insert(const K& key)
		{
			return _tree.insert(key);
		}
	private:
		rb_tree<K, K, set_key_of_t /* 这里传递的是类型*/ > _tree;
	};
}

#endif

5.2. map 的封装

#ifndef _MYMAP_HPP_
#define _MYMAP_HPP_
#include "MyRBTree.hpp"

namespace Xq
{
	template<class K, class V>
	class map
	{
	public:
		struct map_key_of_t
		{
			const K& operator()(const std::pair<K,V>& data) { return data.first; }
		};

		// 对类模板取内置类型,加 typename 告诉编译器这个是类型, 而不是静态变量
		// 普通迭代器
		typedef typename rb_tree<K, std::pair<const K, V>, map_key_of_t>::iterator iterator;
		// const迭代器
		typedef typename rb_tree<K, std::pair<const K, V>, map_key_of_t>::const_iterator const_iterator;

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

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

		const_iterator begin() const { return _tree.begin(); }

		const_iterator end() const { return _tree.end(); }

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

		V& operator[](const K& key)
		{
			std::pair<iterator, bool> ret = insert(std::make_pair(key, V()));
			// ret.first 返回的是一个迭代器, eg. iterator(Node*)
			// ret.first->  这里实际上是有两个 -> , 编译器为了可读性, 省去了一个 ->
			// 第一个 -> 在调用 operator-> , 返回数据的指针, 即返回的是 pair<K,V>*
			// 第二个 -> 访问pair的second成员, 即Value
			return ret.first->second;
		}

	private:
		rb_tree<K, std::pair<const K, V>, map_key_of_t /* 这里传递的也是类型 */> _tree;
	};
}

#endif

5.3. 底层红黑树

#ifndef _MYRBTREE_HPP_
#define _MYRBTREE_HPP_

#include <iostream>
#include <utility>
#include <assert.h>
#include <queue>
#include <vector>
#include <time.h>


namespace Xq
{
	enum color
	{
		RED,
		BLACK
	};

	template<class T>
	struct rb_tree_node
	{
		rb_tree_node<T>* _left;
		rb_tree_node<T>* _right;
		rb_tree_node<T>* _parent;
		T _data;
		color _col;

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

	template<class T, class Ref, class Ptr>
	struct tree_iterator
	{
		typedef rb_tree_node<T> Node;
		typedef tree_iterator<T, Ref, Ptr> Self;
		Node* _node;
		tree_iterator(Node* node = nullptr) :_node(node){}

		// 前置++
		Self& operator++()
		{
			// 如果右子树不为空, 下一个位置就是右子树的最左节点
			if (_node->_right)
			{
				Node* left_node = _node->_right;
				while (left_node->_left)
					left_node = left_node->_left;
				_node = left_node;
			}
			// 如果右子树为空
			else
			{
				// 找孩子是父亲的左孩子的那个父节点
				Node* cur = _node;
				Node* cur_parent = _node->_parent;
				while (cur_parent && cur_parent->_right == cur)
				{
					cur = cur_parent;
					cur_parent = cur_parent->_parent;
				}
				// 走到这里有两种情况
				// case 1: 找到了孩子是父亲的左孩子的父节点
				// case 2: 父亲走到了空, 此时的 _node 就是 nullptr
				_node = cur_parent;
			}
			return *this;
		}

		// 后置++
		Self operator++(int) {}

		// 前置--
		Self& operator--()
		{
			// 依据的思想 右子树、根、左子树.
			// 如果左子树不为空, 找左子树的最右节点
			if (_node->_left)
			{
				Node* right_node = _node->_left;
				while (right_node->_right)
					right_node = right_node->_right;
				_node = right_node;
			}
			// 如果左子树为空, 找孩子是父亲的右孩子的那个父亲
			else
			{
				Node* cur = _node;
				Node* cur_parent = cur->_parent;
				while (cur_parent && cur_parent->_left)
				{
					cur = cur_parent;
					cur_parent = cur_parent->_parent;
				}
				_node = cur_parent;
			}
			return *this;
		}

		// 后置--
		Self operator--(int) {}

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

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

		// 返回对象的引用
		Ref operator*()
		{
			return _node->_data;
		}

		// 返回对象的地址
		Ptr operator->()
		{
			return &(operator*());
		};
	};


	template<class K, class T, class key_of_t>
	class rb_tree
	{
	private:
		typedef rb_tree_node<T> Node;
	public:
		typedef tree_iterator<T, T&, T*> iterator;
		typedef tree_iterator<T, const T&, const T*> const_iterator;

		rb_tree(Node* root = nullptr) :_root(root){}

		std::pair<Node*, bool> insert(const T& data)
		{
			if (!_root)
			{
				_root = new Node(data);
				_root->_col = BLACK;
				return{ _root, true };
			}
			else
			{
				Node* cur = _root;
				Node* parent = nullptr;
				key_of_t kot;
				while (cur)
				{
					if (kot(cur->_data) > kot(data))
					{
						parent = cur;
						cur = cur->_left;
					}
					else if (kot(cur->_data) < kot(data))
					{
						parent = cur;
						cur = cur->_right;
					}
					else
					{
						return{ nullptr, false };
					}
				}
				cur = new Node(data);
				// 由于下面的旋转可能会更改cur, 因此提前保存一下
				Node* RetNode = cur;
				if (kot(data) > kot(parent->_data))
					parent->_right = cur;
				else
					parent->_left = cur;
				cur->_parent = parent;

				// 当父节点parent的颜色为红色时,需要调整
				while (parent && parent->_col == RED)
				{
					// 因为父节点parent->_col == RED,那么说明parent一定有父节点
					Node* grandfather = parent->_parent;
					if (parent == grandfather->_left)
					{
						Node* uncle = grandfather->_right;
						// case 1: 当父节点为红,且叔叔不为空且为红
						// Solution : 变色
						if (uncle && uncle->_col == RED)
						{
							parent->_col = uncle->_col = BLACK;
							grandfather->_col = RED;
							if (grandfather == _root)
								grandfather->_col = BLACK;
							// 继续向上判断,如果依旧符合case 1,继续变色
							cur = grandfather;
							parent = cur->_parent;
							continue;
						}
						// case 2: 父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c在同一条直线上
						// Solution: 单旋, 在这里就是对grandfather右单旋
						else if (parent->_left == cur && ((!uncle) || uncle->_col == BLACK))
						{
							//     g             p
							//   p   u  --->   c   g
							// c                     u
							right_rotate(grandfather);
							parent->_col = BLACK;
							grandfather->_col = RED;
							// 旋转完,更新完颜色,调整就结束
							break;
						}
						// case 3:父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c不在同一条直线上
						// Solution: 双旋,在这里就是先对p进行左单旋,在对g进行右单旋
						else if (parent->_right == cur && ((!uncle) || uncle->_col == BLACK))
						{
							//    g     先对p进行左单旋       g       在对g进行右单旋        c
							// p     u    --->            c     u        --->          p     g    
							//   c                      p                                       u
							left_rotate(parent);
							right_rotate(grandfather);
							cur->_col = BLACK;
							grandfather->_col = RED;
							// 旋转完,更新完颜色,调整就结束
							break;
						}
						else
						{
							// 非法情况
							assert(false);
						}
					}
					// 当parent是grandfather的右孩子,那么uncle就是grandfather的左孩子
					else
					{
						Node* uncle = grandfather->_left;
						// case 1: 当父节点为红,且叔叔不为空且为红
						// Solution : 变色
						if (uncle && uncle->_col == RED)
						{
							parent->_col = uncle->_col = BLACK;
							grandfather->_col = RED;
							if (grandfather == _root)
								grandfather->_col = BLACK;
							// 继续向上判断,如果依旧符合case 1,继续变色
							cur = grandfather;
							parent = cur->_parent;
							continue;
						}
						// case 2: 父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c在同一条直线上
						// Solution: 单旋, 在这里就是对grandfather左单旋
						else if (parent->_right == cur && ((!uncle) || uncle->_col == BLACK))
						{
							//   g    对g进行左单旋       p
							// u   p      --->         g     c
							//       c               u 
							left_rotate(grandfather);
							parent->_col = BLACK;
							grandfather->_col = RED;
							//旋转并将颜色更新后,就退出调整
							break;
						}
						// case 3:父节点为红,且叔叔为空或者不为空且为黑,并且g、p、c不在同一条直线上
						// Solution: 双旋,在这里就是先对p进行右单旋,在对g进行左单旋
						else if (parent->_left == cur && ((!uncle) || uncle->_col == BLACK))
						{
							//    g       先对p进行右单旋        g          在对g进行左单旋          c
							// u     p      --->             u     c          --->            g      p
							//     c                                  p                    u                            

							right_rotate(parent);
							left_rotate(grandfather);
							cur->_col = BLACK;
							grandfather->_col = RED;
							break;
						}
						else
						{
							// 非法情况,断死
							assert(false);
						}
					}
				}
				_root->_col = BLACK;
				return std::pair<Node*, bool>(RetNode, true);
			}
		}

		void level_order()
		{
			_level_order(_root);
		}

		bool is_balance_tree()
		{
			if (_root->_col != BLACK)
			{
				std::cout << "根节点是红色的,异常" << std::endl;
				return false;
			}

			return _is_balance_tree(_root);
		}

		int get_rb_tree_high()
		{
			return _get_rb_tree_high(_root);
		}

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

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

		const_iterator begin() const
		{
			Node* cur = _root;
			while (cur->_left)
				cur = cur->_left;
			return const_iterator(cur);
		}

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

	private:

		void _level_order(Node* root)
		{
			if (!root)
				return;
			else
			{
				std::queue<Node*> qu;
				qu.push(root);
				while (!qu.empty())
				{
					Node* front = qu.front();
					qu.pop();
					if (front)
					{
						qu.push(front->_left);
						qu.push(front->_right);
					}
					if (!front)
						std::cout << "N ";
					else
						std::cout << front->_kv.first << " ";
				}
				std::cout << std::endl;
			}
		}

		void left_rotate(Node* parent)
		{
			Node* cur = parent;
			Node* cur_right = cur->_right;
			Node* cur_right_left = cur_right->_left;
			Node* cur_parent = cur->_parent;

			cur->_right = cur_right_left;
			if (cur_right_left)
				cur_right_left->_parent = cur;

			cur_right->_left = cur;
			cur->_parent = cur_right;

			if (!cur_parent)
			{
				cur_right->_parent = nullptr;
				_root = cur_right;
			}
			else
			{
				if (cur_parent->_left == cur)
					cur_parent->_left = cur_right;
				else
					cur_parent->_right = cur_right;
				cur_right->_parent = cur_parent;
			}
		}

		void right_rotate(Node* parent)
		{
			Node* cur = parent;
			Node* cur_left = cur->_left;
			Node* cur_left_right = cur_left->_right;
			Node* cur_parent = cur->_parent;

			cur->_left = cur_left_right;
			if (cur_left_right)
				cur_left_right->_parent = cur;

			cur_left->_right = cur;
			cur->_parent = cur_left;

			if (!cur_parent)
			{
				cur_left->_parent = nullptr;
				_root = cur_left;
			}
			else
			{
				key_of_t kot;
				if (kot(cur_parent->_data) > kot(cur_left->_data))
					cur_parent->_left = cur_left;
				else
					cur_parent->_right = cur_left;
				cur_left->_parent = cur_parent;
			}
		}

		bool _is_balance_tree(Node* root)
		{
			if (!root)
				return true;
			else
			{
				int basic_value = _get_black_node_num(root);
				return _is_check_rb_tree(root, 0, basic_value);
			}
		}

		bool _is_check_rb_tree(Node* root, int black_num, int basic_value)
		{
			// basic_value: 红黑树的一条路径中的黑色节点个数,在这里作为基本值
			if (root == nullptr)
			{
				if (black_num != basic_value)
				{
					std::cout << "路径中的黑色节点个数不相等,异常" << std::endl;
					return false;
				}
				else
					return true;
			}
			else
			{
				if (root->_col == BLACK)
					++black_num;
				// 如果一个节点是红色的,那么该节点一定不是根,因此一定有父亲
				// 如果这个节点的父亲也是红色的,那么就说明出现异常
				if (root->_col == RED && root->_parent->_col == RED)
				{
					std::cout << "child: " << root->_kv.first << "parent: " << root->_parent->_kv.first << "出现了连续的红色节点,异常" << std::endl;
					return false;
				}
				return _is_check_rb_tree(root->_left, black_num, basic_value)
					&& _is_check_rb_tree(root->_right, black_num, basic_value);
			}
		}

		int _get_black_node_num(Node* root)
		{
			if (root == nullptr)
				return 0;
			else
			{
				int ret = 0;
				while (root)
				{
					if (root->_col == BLACK)
						++ret;
					root = root->_left;
				}
				return ret;
			}
		}

		int _get_rb_tree_high(Node* root)
		{
			if (root == nullptr)
				return 0;
			else
			{
				int left_high = _get_rb_tree_high(root->_left);
				int right_high = _get_rb_tree_high(root->_right);
				return left_high > right_high ? ++left_high : ++right_high;
			}
		}

	private:
		Node* _root;
	};
}
#endif

set 和 map的封装至此结束。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值