红黑树封装map和set

文章目录

    • 封装的一些小问题
      • 封装要实现什么
      • 伏笔一
      • 伏笔二
      • 伏笔三
    • RBTree迭代器
      • 基础架构和简单内容实现
      • ++的实现
    • RBTree的修改
    • map.h
    • set.h
    • 全部代码

封装的一些小问题

封装要实现什么

一般来说封装需要实现map和set的迭代器,包括普通迭代器和const迭代器,其次是++和–操作,还有判断是否相等的重载

除此之外我们需要实现map和set的特性,例如set的值不允许修改,map的key不允许修改

伏笔一

set的构造参数只有一个,而map的构造参数有两个,我们怎么只用一个数据结构(红黑树)来封装两个容器呢

RBTree的模板参数前两个是K和T,K表示key关键字类型,T表示其中的数据类型

当用来构造map时刚刚好,一个用来存key,另一个用来存数据,这里我们考虑使用pair,分别存入const K和V,用来确保K不被修改

当用来构造set时,K和T是相同的,我们都传入K即可

伏笔二

RBTree的模板参数里面有一个KeyOfT,这是一个仿函数,因为map的结构特殊性,是一个pair,所以我们需要用仿函数来自动调用对应的取key中的T的操作,类似于C语言的回调函数,根据数据的类型调用不同的函数

伏笔三

在实现Insert函数时,返回值是一个pair,first是Node*,second是一个布尔值

这里的Node*一方面是需要返回给外部调用函数,另一方面是不能返回对应的迭代器和布尔类型,等下我们写到对应代码时再具体解释

RBTree迭代器

基础架构和简单内容实现

template<class T>
struct __TreeIterator {
   
	typedef RBTreeNode<T> Node;
	typedef __TreeIterator<T> Self;
	Node* _node;

	__TreeIterator(Node* node) 
		:_node(node)
	{
   }

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

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

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

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

++的实现

要实现++我们首先要明白++的本质是什么

一般来说迭代器的加减本身是用于寻找下一个(上一个)位置的迭代器,那对于红黑树这个数据结构来讲,他是中序有序的,因此我们要找的就是中序遍历的下一个节点

这里直接想其实是不容易的,我们通过例子来讲解

红黑树封装map和set_第1张图片

第一个例子是,1的下一个节点是6,8的下一个节点是11,17的下一个节点是22,那我们其实就发现,cur的右子树存在,直接走到他的右子树的最左节点即可,那么如果他的右节点不存在呢

第二个例子,6的下一个节点是8,11的下一个节点是13,15的下一个节点是17,22的下一个节点是25,规律不怎么明显,结论是如果右节点不存在则回溯,回溯到孩子是父亲左的第一个祖先节点

这个规律似乎很奇怪

但其实我们只需要想一下中序遍历的顺序:左子树、根、右子树

cur就是根,我们已经访问完了根,想要找他的下一个节点,那根的下一个就是右子树最左节点,如此往复,一直到没有右子树了,接下来寻找的孩子是父亲左的第一个节点,这句话实际上就保证了左子树全部被访问了,因此孩子是父亲左的第一个节点就是下一个cur,也就是根,接下来就需要继续访问右子树了

那类似的–的过程跟++的过程完全相反,找cur的左节点,如果左节点不存在则找孩子是父亲右的第一个节点

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

这里有个细节是parent为空时我们也认为他找到了,主要是需要包含根节点的情况

RBTree的修改

	typedef __TreeIterator<T> iterator;

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

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

begin就是返回中序的第一个位置的迭代器,也就是最小的值,就是最左节点

end返回最后一个位置的下一个位置的迭代器,自然就是空指针

map.h

#pragma once
#include"RBTree.h"

namespace xu {
   
	template<class K, class V>
	class map {
   
	public:
		struct MapKeyOfT {
   
			const K& operator() (const pair<K, V>& kv) {
   
				return kv.first;
			}
		};

		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;

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

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

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

		pair<iterator, bool> insert(const pair<K, V>& kv) {
   
			return _t.Insert(kv);
		}
	private:
		RBTree<K, pair<const K, V

你可能感兴趣的:(C++,c++,红黑树,map,set,封装)