二叉树进阶(红黑树改造)

目录

引言

改造比较

1.颜色定义

2.节点构造

3.迭代器

3.1  迭代器的模板参数

3.2  迭代器的构造函数

3.3  迭代器的运算符重载函数

3.3.1 前置++

3.3.2 前置--

3.3.3 源码对比

3.3.4 其它运算符重载

4.红黑树改造

4.1 红黑树的begin(),end()函数

4.2 红黑树的模板参数

4.3 下标访问


引言

在上篇中我们已经实现了红黑树的构造,但是和stl库中的红黑树还是有区别,这节我们将根据stl库

中的源码进行比对,改造红黑树,之后将用改造后的红黑树,来封装map和set,完成模拟实现

改造比较

1.颜色定义

第一个在颜色定义上,我们采用的是枚举的方法,而源码采用的是直接全局定义,不过两种方式本

质没什么区别,可以不需要改动

//源码
typedef bool __rb_tree_color_type;
const __rb_tree_color_type __rb_tree_red = false;
const __rb_tree_color_type __rb_tree_black = true;
//我们自己的代码
enum Colour {
	RED = 0,
	BLACK,
};

2.节点构造

其次是节点的构造,我们在模拟实现红黑树的时候,采用的是kv型模板,即构造二叉树结点,都是

用pair来进行构造,但是我们需要用改造后的红黑树来封装map和set,也就是套用同一个红黑树模

板,前者的value才是pair,后者的value则为key

因此,我们要进行模板参数的改造,将kv模型改为T模板,由上层来实例化相应的红黑树

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

当然,源码会比我们的节点类写得更为精妙,源码与节点相关的类有两个类,一个是节点基类,另

一个才是节点类(节点基类的派生类),并且在节点基类中,加入了minimum,maximum两个函数,

之后在实现像是leftmost函数(找最左节点,即红黑树的最小节点中直接赋用即可)

//源码
struct __rb_tree_node_base
{
  //重命名节点基类颜色为color_type
  typedef __rb_tree_color_type color_type;
  //重命名节点指针为base_ptr
  typedef __rb_tree_node_base* base_ptr;
  
  color_type color; 
  base_ptr parent;
  base_ptr left;
  base_ptr right;
  //找最左节点,返回节点指针
  static base_ptr minimum(base_ptr x)
  {
    while (x->left != 0) x = x->left;
    return x;
  }
  //找最右节点,返回节点指针
  static base_ptr maximum(base_ptr x)
  {
    while (x->right != 0) x = x->right;
    return x;
  }
};

template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
  typedef __rb_tree_node<Value>* link_type;
  //节点的成员仅有value_field,与我们的data成员是等价的
  //传pair,实例化出的就是map;传key,实例化出的就是set
  Value value_field;
};

3.迭代器

在之前红黑树的实现中,我们并没有实现红黑树的迭代器,而实际上,map和set都是支持双向迭

代器,即可以通过迭代器,实现中序遍历访问,因此,我们还需要在红黑树改造中,完成对迭代器

的实现

在链表那节中,我们已经提到过,迭代器要么是原生指针,要么是自定义类型对原生指针的封装,

模拟指针的行为,红黑树的迭代器也不例外,它实际就是原生指针的封装,然后在自定义类型中加

入相应前置++,前置--等函数

3.1迭代器的模板参数

和list类的迭代器相同,模板参数有三个,分别是T(Value),Ref,Ptr

其中后两个模板参数发挥的作用和list迭代器发挥的作用是相同的,目的就是完成const迭代器和普

通迭代器的实例化用同一个模板

当我们不想修改节点的值,需要使用const迭代器时,Ref模板参数开始发挥作用,只要往里面传

入const Value&即可实例化出相应的const迭代器 

同理,假如树节点中存的是自定义类型,我们想要通过迭代器重载->来模拟实现用迭代器访问自定

义类型数据,Ptr模板参数开始发挥作用,只要往里面传入const T*或T*,即可分别实例化出不能通

过->修改节点中的数据的const迭代器和能通过->修改节点中的数据的普通迭代器

当然stl源码中,同样采取了迭代器基类,迭代器类(继承迭代器基类)的方式

基类包含了两个函数,一个是increment自增函数,另一个是decrement自减函数,两者都是服务

于后面迭代器前置++,--运算符重载 

3.2  迭代器的构造函数

这里先提前说明,set类是不能对value进行修改的,而map类键值对应,是可以对value进行修改的

因此我们仿照源码中,模拟实现set和map封装时,set中的普通迭代器和const迭代器,实际上都是

const迭代器,不过这会导致上层红黑树类在调用begin函数时,发生冲突

因为set类的成员变量是普通对象_t,即便我们在红黑树类中已经实现了const迭代器版本的begin和end函数,但是在set类中,编译器依旧会调用普通迭代器的begin与end函数

//源码中set类的成员变量
typedef rb_tree<key_type, value_type, 
                  identity<value_type>, key_compare, Alloc> rep_type;
rep_type t;  // red-black tree representing set
//源码中红黑树类有关begin,end函数的实现
link_type& leftmost() const { return (link_type&) header->left; }
link_type& rightmost() const { return (link_type&) header->right; }

iterator begin() { return leftmost(); }
const_iterator begin() const { return leftmost(); }
iterator end() { return header; }
const_iterator end() const { return header; }

因此,在迭代器构造函数中,除了默认构造外,还要增加普通迭代器转为const迭代器的构造函数

//自己模拟实现
__RBtree_iterator() {}
__RBtree_iterator(Node* node)
	:_node(node)
{}
// 1、typedef __RBTreeIterator<T, T&, T*> iterator;  拷贝构造
// 2、 typedef __RBTreeIterator<T, const T&, const T*> const_iterator;
//  支持普通迭代器构造const迭代器的构造函数

__RBtree_iterator(const __RBtree_iterator<T, T&, T*>& it)
	:_node(it._node)
{}


//源码实现
typedef __rb_tree_iterator<Value, Value&, Value*>             iterator;
typedef __rb_tree_iterator<Value, const Value&, const Value*> const_iterator;
//迭代器自身重命名
typedef __rb_tree_iterator<Value, Ref, Ptr>                   self;
//节点指针重命名
typedef __rb_tree_node<Value>* link_type;

__rb_tree_iterator() {}
__rb_tree_iterator(link_type x) { node = x; }
//参数是普通迭代器引用,当传入普通迭代器,它是拷贝构造;
//当传入const迭代器,它是支持普通迭代器构造const迭代器的构造函数
__rb_tree_iterator(const iterator& it) { node = it.node; }

3.3  迭代器的运算符重载函数

3.3.1 前置++

运算符重载函数中最关键就是实现前置++,--这两个运算符重载

  对于红黑树来说,迭代器的移动要求符合中序遍历的要求,即用迭代器遍历整棵树时,会得到有

序序列

思路如下:

    假如当前节点的右子树不为空,下一个打印的节点就是右子树的最左节点

   假如当前节点的右子树为空,因为中序遍历是左,根,右的顺序进行遍历的,所以意味着这棵树已经结束,需要向上找孩子为父亲左孩子的祖先节点,其实就是找对应的根

当遍历完最后一个节点时,迭代器会一直向上沿着路径找孩子为父亲左孩子的祖先节点,找不到,

最后为空,则可以直接跳出循环 

//实现前置++,保证指针往后移动,能够实现树的中序遍历
	self& operator++()
	{
		//如果当前节点的右子树不为空
		if (_node->_right)
		{
			// 1、右不为空,下一个就是右子树的最左节点
			Node* subLeft = _node->_right;
			while (subLeft->_left)
			{
				subLeft = subLeft->_left;
			}


			_node = subLeft;
		}
//如果当前节点的右子树为空,由于按照左子树,根,右子树遍历,说明此时该子树已经全部遍历
//需要向上找孩子为父亲左孩子的祖先节点
		else
		{
			Node* parent = _node->_parent;
			Node* cur = _node;

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

			_node = parent;
		}

		return *this;
	}
3.3.2 前置--

前置--则是相反操作,我们可以将它看作是按照右子树,根,左子树的顺序遍历整棵树,因此,进

行的操作和前置++其实完全是一样的,不过此时的left变成right,right变成left

	//实现前置--
	self& operator--()
	{

		//如果当前节点的左子树不为空
		if (_node->_left)
		{
			// 1、左不为空,下一个就是左子树的最右节点
			Node* subRight = _node->_left;
			while (subRight->_right)
			{
				subRight = subRight->_right;
			}


			_node = subRight;
		}
		// 2、左为空,孩子是父亲的右的那个祖先
		else
		{
			Node* parent = _node->_parent;
			Node* cur = _node;

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

			_node = parent;
		}

		return *this;
	}
3.3.3 源码对比

源码中由于已经在基类已经实现过increment,decrement函数,因此在迭代器类已经继承,直接调

用即可,代码的整体思想是一样的,不过会有所差别

原因在于stl库中采用的红黑树结构会有自带一个头节点,为了进一步方便后面关联式容器set,

map的实现,为了与根节点进行区分,将头结点给成红色,并且让头结点的 pParent 域指向红黑树

的根节点,pLeft 域指向红黑树中最小的节点(leftmost),_pRight域指向红黑树中最大的节点

 (rightmost)

 void init() {
    header = get_node();
    color(header) = __rb_tree_red; // used to distinguish header from 
                                   // root, in iterator.operator++
    root() = 0;
    leftmost() = header;
    rightmost() = header;
  }

 可以看到,由于多了一个header的头节点,因此increment函数需要作出相应的改变

void increment()
  { 
    //右子树不为空,找右子树的最左节点
    if (node->right != 0) {
      node = node->right;
      while (node->left != 0)
        node = node->left;
    }
    //右子树为空,沿路径找节点为父节点的左孩子的祖先节点
    else {
      base_ptr y = node->parent;
      while (node == y->right) {
        node = y;
        y = y->parent;
      }
      if (node->right != y)
        node = y;
    }
  }

右子树不为空的情况都是一样的,但右子树为空的情况,因为header而变得复杂起来

右子树为空,最后迭代器指向的位置,肯定停留在父节点上

比如说5遍历完,迭代器++后,移动到的位置就是parent的位置,即源码中的y

但是为什么赋值前还需要判断节点的右孩子不与父节点相同呢?

因为会出现如图下面的情况,此时迭代器指向根节点,而恰好根节点没有右孩子,如果对迭代器进

行++操作,新的迭代器指向的就是header,而不需要再把y赋值给_node,这样++就能够继续推进

 同理,由于多了一个header的头节点,在进行我们原来的所有操作前,还需要判断现在迭代器指

向的是否是头节点,假如是头节点,则进行迭代器--操作,则迭代器指向根节点

如果不进行该特殊情况判断,则进行--操作,反而迭代器会指向begin()

void decrement()
  {
    if (node->color == __rb_tree_red &&
        node->parent->parent == node)
      node = node->right;
    else if (node->left != 0) {
      base_ptr y = node->left;
      while (y->right != 0)
        y = y->right;
      node = y;
    }
    else {
      base_ptr y = node->parent;
      while (node == y->left) {
        node = y;
        y = y->parent;
      }
      node = y;
    }
  }

 下面给出stl库中源码,顺便包括了后置++,后置--的实现

//iterator基类
struct __rb_tree_base_iterator
{
  typedef __rb_tree_node_base::base_ptr base_ptr;
  typedef bidirectional_iterator_tag iterator_category;
  typedef ptrdiff_t difference_type;
  base_ptr node;

  void increment()
  { 
    //右子树不为空,找右子树的最左节点
    if (node->right != 0) {
      node = node->right;
      while (node->left != 0)
        node = node->left;
    }
    //右子树为空,沿路径找节点为父节点的左孩子的祖先节点
    else {
      base_ptr y = node->parent;
      while (node == y->right) {
        node = y;
        y = y->parent;
      }
      if (node->right != y)
        node = y;
    }
  }

  void decrement()
  {
    if (node->color == __rb_tree_red &&
        node->parent->parent == node)
      node = node->right;
    else if (node->left != 0) {
      base_ptr y = node->left;
      while (y->right != 0)
        y = y->right;
      node = y;
    }
    else {
      base_ptr y = node->parent;
      while (node == y->left) {
        node = y;
        y = y->parent;
      }
      node = y;
    }
  }
};

//iterator类运算符重载 
self& operator++() { increment(); return *this; }
  self operator++(int) {
    self tmp = *this;
    increment();
    return tmp;
  }
    
  self& operator--() { decrement(); return *this; }
  self operator--(int) {
    self tmp = *this;
    decrement();
    return tmp;
  }
3.3.4 其它运算符重载

源码中将这类运算符重载函数,全部改为了内联函数,其它没有什么变化

//自己实现的代码
Ref operator*()
{
	return _node->_data;
}

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

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

//源码
inline bool operator==(const __rb_tree_base_iterator& x,
                       const __rb_tree_base_iterator& y) {
  return x.node == y.node;
}

inline bool operator!=(const __rb_tree_base_iterator& x,
                       const __rb_tree_base_iterator& y) {
  return x.node != y.node;
}

4.红黑树改造

4.1 红黑树的begin(),end()函数

由于在上面已经实现了迭代器的前置++,--运算符

接下来,只要在红黑树类实现begin,end函数,其实就可以用迭代器遍历这棵树了

对于我们模仿实现红黑树,由于不存在头节点,因此begin函数返回的就是红黑树的最左节点

end函数返回的是红黑树的最右节点的下一个节点,这里我们直接用空指针构造即可

同样实现const版本和普通迭代器两个版本

而由于源码中红黑树多了一个头节点,因此begin函数返回的是最左节点,而end函数返回的则是头

节点.

//我们自己模拟实现
iterator begin()
{
	Node* cur = _root;
	//找树的最左节点
	while (cur && cur->_left)
	{
		cur = cur->_left;
	}

	return iterator(cur);
}

const_iterator begin() const
{
	Node* cur = _root;
	//找树的最左节点
    while (cur && cur->_left)
	{
		cur = cur->_left;
	}

	return iterator(cur);
}

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

//源码

link_type& root() const { return (link_type&) header->parent; }
link_type& leftmost() const { return (link_type&) header->left; }
link_type& rightmost() const { return (link_type&) header->right; }

iterator begin() { return leftmost(); }
const_iterator begin() const { return leftmost(); }
iterator end() { return header; }
const_iterator end() const { return header; }

4.2 红黑树的模板参数

与我们之前模拟实现的红黑树不同,除了Key,Value两个参数外,源码中的红黑树有五个参数

除去最后一个和空间支配器相关的参数外,还增加了两个KeyOfValue,和Compare参数

第一个Key参数发挥的作用我们比较好理解,因为无论是map,或者是set,底层封装的这棵红黑

树,它首先是一棵搜索平衡二叉树,是按照key来建树的,find,erase这些函数接口参数都是key,

如果单纯只有一个Value模板,显然是不够的

第二个Value参数主要是服务于insert函数,它决定了树的节点里面存的是什么,map存的就是

pair,set存的就是key

第三个KeyOfValue参数,则是服务于比较

RBtree判断不了你是map还是set,我们需要自己在封装map和set的时候提供对应的Value,以此

完成红黑树类中值的判断

  • 比如set的keyofvalue就是它自己的key
  • 而map的keyofvalue是pair.first
//对于set类型而言,其value比较的是key,所以增加了一个模板参数
//改造前
Node* Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first > key)
		{
			cur = cur->_left;
		}
		else if (cur->_kv.first < key)
		{
			cur = cur->_right;
		}
		else
		{
			return cur;
		}
	}

	return nullptr;
}

//改造后
//假如是set,则传入的则是key比较;假如是map。传入的则是pair.first
Node* Find(const K& key)
{
	Node* cur = _root;
	KeyOfValue kot;
	while (cur)
	{   
		if (kot(cur-> _data) > key)
		{
			cur = cur->_left;
		}
		else if (kot(cur-> _data) < key)
		{
			cur = cur->_right;
		}
		else
		{
			return cur;
		}
	}

	return nullptr;
}

 第四个Compare参数则是给用户机会,能够提供比较的方法,键值大于往左边插入还是往右边插

入等等

4.3 下标访问

map非常特殊的一点就是,可以直接用map[key]=value来修改value的内容,如果不存在,则直接

插入相应的新节点

原理也很简单,就是稍微改造一下insert函数,不单单返回bool,来判断是否插入成功,还需要返

回插入元素的迭代器,通过迭代器,我们就可以在map中重载方括号运算符,来修改对应的Value

//修改后的insert函数代码
pair<iterator,bool> Insert(const T& data)
	{
		//假如刚开始没有节点,直接使其成为根即可
		//满足规则,根节点必须为黑色
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root), true);
		}

		//同样满足二叉树的搜索规则,先找到新节点的正确位置
		Node* parent = nullptr;
		Node* cur = _root;
		KeyOfValue 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 make_pair(iterator(cur),false);
			}
		}

		//建新节点
		cur = new Node(data);
		Node* newnode = cur;
		if (kot(parent->_data) > kot(data))
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		cur->_parent = parent;

		//调整节点颜色
		while (parent && parent->_col == RED)
		{
			//找爷爷
			Node* grandfather = parent->_parent;
			//父亲为爷爷的左节点
			if (grandfather->_left == parent)
			{
				//则叔叔是爷爷的右节点
				Node* uncle = grandfather->_right;
				// 情况1:u存在且为红,变色处理,并继续往上处理
				if (uncle && uncle->_col == RED)
				{
					//父亲和叔叔节点都调节为黑色
					parent->_col = BLACK;
					uncle->_col = BLACK;
					//爷爷调节为红色
					grandfather->_col = RED;

					//往上调节
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3:u不存在/u存在且为黑,旋转+变色
				{
					//     g
					//   p   u
					// c
					//右单旋
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						//    g
					   //   p   u
					  //      c
						//LR双旋
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						//parent->_col = RED;
						grandfather->_col = RED;
					}

					break;
				}
			}
			//父亲为爷爷的右节点
			else
			{
				//则叔叔是爷爷的左节点
				Node* uncle = grandfather->_left;
				// 情况1:u存在且为红,变色处理,并继续往上处理
				if (uncle && uncle->_col == RED)
				{
					//父亲和叔叔节点都调节为黑色
					parent->_col = BLACK;
					uncle->_col = BLACK;
					//爷爷调节为红色
					grandfather->_col = RED;

					//往上调节
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3:u不存在/u存在且为黑,旋转+变色
				{
					//     g
					//   u   p
					//        c
					//左单旋
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						//     g
						//   u   p
						//     c
						//RL双旋
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						//parent->_col = RED;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;

		return make_pair(iterator(newnode),true);
	}

源码的insert_unique的实现

  

 // insert/erase
 //插入声明
  pair<iterator,bool> insert_unique(const value_type& x);

//源码中插入的具体实现
rb_tree<Key, Value, KeyOfValue, Compare, Alloc>::insert_unique(const Value& v)
{
  link_type y = header;
  //从根节点开始往下找合适的插入点
  link_type x = root();
  bool comp = true;
  while (x != 0) {
    y = x;
    //小于则true,大于等于则false,循环中只会出现一次false
    comp = key_compare(KeyOfValue()(v), key(x));
    //x遇上大,则往左走;遇到小于等于,则往右走
    x = comp ? left(x) : right(x);
  }
//x为新插入的节点位置,y为其父节点,v为插入节点的value值
//循环结束时,此时x必为空,y必为叶节点
  //令迭代器j指向父节点y
  iterator j = iterator(y);   
  //如果comp为真,说明x位于y的左边
  if (comp)
    //如果j指向树的最左节点,则直接插入
    if (j == begin())     
      return pair<iterator,bool>(__insert(x, y, v), true);
    else
    //插入点之父节点不为最左节点,回调j
      --j;
  //小于新值,则往右进行插入
  if (key_compare(key(j.node), KeyOfValue()(v)))
    return pair<iterator,bool>(__insert(x, y, v), true);
  //到这一步说明键值重复,那么就不该插入该值,返回false
  return pair<iterator,bool>(j, false);
}

PS:上述代码展示的是插入中不允许重复元素

源码解决重复问题的方法也比较巧妙

   父节点为最左节点,也就是最小的,插入的新节点比父节点还小,则插入的新节点一定不是重复元素,可以直接插入

   如果不是最左节点,就会出现两种情况

   第一种情况,comp为真,此时要回调父节点,--j,再判断是否会有重复元素

   比如说【8 9 11】,往里面插入10,就需要判断9,如果9小于10,则插入

   第二种,comp为假

   比如说[【9 8 13 10 12】,往里面插入11,11大于10,往右直接插入

关键其实在于理解,假如出现相同元素后,会发生什么?

  x会往右走,然后一直往左移动,因为右子树所有的元素一定比根要大

五.完整代码展示

#include <iostream>
using namespace std;

enum Colour {
	RED = 0,
	BLACK,
};

//为了实现map与set封装使用同一个模板红黑树,前者的value是pair,后者的value为key
//因此我们需要对红黑树的模板进行改造
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 T, class Ref, class Ptr>
struct __RBtree_iterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBtree_iterator<T, Ref, Ptr> self;
	Node* _node;

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

	// 1、typedef __RBTreeIterator<T, T&, T*> iterator;  拷贝构造
	// 2、 typedef __RBTreeIterator<T, const T&, const T*> const_iterator;
	//  支持普通迭代器构造const迭代器的构造函数

	__RBtree_iterator(const __RBtree_iterator<T, T&, T*>& it)
		:_node(it._node)
	{}

	//实现前置++,保证指针往后移动,能够实现树的中序遍历
	self& operator++()
	{
		//如果当前节点的右子树不为空
		if (_node->_right)
		{
			// 1、右不为空,下一个就是右子树的最左节点
			Node* subLeft = _node->_right;
			while (subLeft->_left)
			{
				subLeft = subLeft->_left;
			}


			_node = subLeft;
		}
//如果当前节点的右子树为空,由于按照左子树,根,右子树遍历,说明此时该子树已经全部遍历
		//需要向上找孩子为父亲左孩子的祖先节点
		else
		{
			Node* parent = _node->_parent;
			Node* cur = _node;

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

			_node = parent;
		}

		return *this;
	}

	//实现前置--
	self& operator--()
	{

		//如果当前节点的左子树不为空
		if (_node->_left)
		{
			// 1、左不为空,下一个就是左子树的最右节点
			Node* subRight = _node->_left;
			while (subRight->_right)
			{
				subRight = subRight->_right;
			}


			_node = subRight;
		}
		// 2、左为空,孩子是父亲的右的那个祖先
		else
		{
			Node* parent = _node->_parent;
			Node* cur = _node;

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

			_node = parent;
		}

		return *this;
	}

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

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

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

//仿函数
template<class K, class T, class KeyOfValue>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	~RBTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}

public:
	typedef __RBtree_iterator<T, T&, T*> iterator;
	typedef __RBtree_iterator<T, const T&, const T*> const_iterator;

	iterator begin()
	{
		Node* cur = _root;
		//找树的最左节点
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}

		return iterator(cur);
	}

	const_iterator begin() const
	{
		Node* cur = _root;
		//找树的最左节点
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}

		return iterator(cur);
	}

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

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

	//对于set类型而言,其value比较的是key,所以增加了一个模板参数
	Node* Find(const K& key)
	{
		Node* cur = _root;
		KeyOfValue kot;
		while (cur)
		{   
			if (kot(cur-> _data) > key)
			{
				cur = cur->_left;
			}
			else if (kot(cur-> _data) < key)
			{
				cur = cur->_right;
			}
			else
			{
				return cur;
			}
		}

		return nullptr;
	}

	pair<iterator,bool> Insert(const T& data)
	{
		//假如刚开始没有节点,直接使其成为根即可
		//满足规则,根节点必须为黑色
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root), true);
		}

		//同样满足二叉树的搜索规则,先找到新节点的正确位置
		Node* parent = nullptr;
		Node* cur = _root;
		KeyOfValue 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 make_pair(iterator(cur),false);
			}
		}

		//建新节点
		cur = new Node(data);
		Node* newnode = cur;
		if (kot(parent->_data) > kot(data))
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		cur->_parent = parent;

		//调整节点颜色
		while (parent && parent->_col == RED)
		{
			//找爷爷
			Node* grandfather = parent->_parent;
			//父亲为爷爷的左节点
			if (grandfather->_left == parent)
			{
				//则叔叔是爷爷的右节点
				Node* uncle = grandfather->_right;
				// 情况1:u存在且为红,变色处理,并继续往上处理
				if (uncle && uncle->_col == RED)
				{
					//父亲和叔叔节点都调节为黑色
					parent->_col = BLACK;
					uncle->_col = BLACK;
					//爷爷调节为红色
					grandfather->_col = RED;

					//往上调节
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3:u不存在/u存在且为黑,旋转+变色
				{
					//     g
					//   p   u
					// c
					//右单旋
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						//    g
					   //   p   u
					  //      c
						//LR双旋
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						//parent->_col = RED;
						grandfather->_col = RED;
					}

					break;
				}
			}
			//父亲为爷爷的右节点
			else
			{
				//则叔叔是爷爷的左节点
				Node* uncle = grandfather->_left;
				// 情况1:u存在且为红,变色处理,并继续往上处理
				if (uncle && uncle->_col == RED)
				{
					//父亲和叔叔节点都调节为黑色
					parent->_col = BLACK;
					uncle->_col = BLACK;
					//爷爷调节为红色
					grandfather->_col = RED;

					//往上调节
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3:u不存在/u存在且为黑,旋转+变色
				{
					//     g
					//   u   p
					//        c
					//左单旋
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else
					{
						//     g
						//   u   p
						//     c
						//RL双旋
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						//parent->_col = RED;
						grandfather->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;

		return make_pair(iterator(newnode),true);
	}

	int Height()
	{
		return _Height(_root);
	}

	bool IsRBTree()
	{
		//假如根节点存在,但颜色不是黑色,则不是红黑树
		if (_root && _root->_col == RED)
		{
			cout << "根节点颜色是红色" << endl;
			return false;
		}

		//随便选一条路径作为黑色节点参考点
		int benchmark = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				benchmark++;
			cur = cur->_left;
		}

		// 连续红色节点
		return _Check(_root, 0, benchmark);
	}

private:
	void _Destroy(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_Destroy(root->_left);
		_Destroy(root->_right);
		delete root;
	}

	int _Height(Node* root)
	{
		if (root == NULL)
			return 0;

		int leftH = _Height(root->_left);
		int rightH = _Height(root->_right);

		return leftH > rightH ? leftH + 1 : rightH + 1;
	}

	bool _Check(Node* root, int blackNum, int benchmark)
	{
		//假如到空节点(叶子节点),说明已经走完一条路径,可以开始判断
		if (root == nullptr)
		{
			//假如统计出的黑色节点个数和参考黑色节点个数不同,则一定不是红黑树
			if (blackNum != benchmark)
			{
				cout << "某条路径黑色节点的数量不相等" << endl;
				return false;
			}

			return true;
		}

		//递归遇到黑色节点时,则blackNum可以加1
		if (root->_col == BLACK)
			blackNum++;

//假如连续存在两个红色节点,则也不是红黑树,注意还需要判断父节点是否存在
		if (root->_col == RED && root->_parent && root->_parent->_col == RED)
		{
			cout << "存在连续的红色节点" << endl;
			return false;
		}

		//递归判断是否是红黑树,左子树和右子树都为红黑树,则为红黑树
		return _Check(root->_left, blackNum, benchmark)
			&& _Check(root->_right, blackNum, benchmark);
	}

	//左旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		//b变成30的右
		parent->_right = subRL;
		//父节点也需要调整,但subRL可能为空
		if (subRL)
			subRL->_parent = parent;

//调整时未必是整棵树的调整,所以还需要考虑parent的链接问题,因此需要先记录ppNode
		Node* ppNode = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

		if (ppNode == nullptr)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			//在调整爷爷节点指向的时候,还需要考虑原来parent是爷爷的左还是右
			//subR重新链接回爷爷的左或者右
			if (ppNode->_right == parent)
			{
				ppNode->_right = subR;
			}
			else
			{
				ppNode->_left = subR;
			}

			subR->_parent = ppNode;
		}
	}

	//右旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		//b变成60的左
		parent->_left = subLR;
		//父节点也需要调整,但subRL可能为空
		if (subLR)
			subLR->_parent = parent;

//调整时未必是整棵树的调整,所以还需要考虑parent的链接问题,因此需要先记录ppNode
		Node* ppNode = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

		if (ppNode == nullptr)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			//在调整爷爷节点指向的时候,还需要考虑原来parent是爷爷的左还是右
			//subL重新链接回爷爷的左或者右
			if (ppNode->_right == parent)
			{
				ppNode->_right = subL;
			}
			else
			{
				ppNode->_left = subL;
			}

			subL->_parent = ppNode;
		}
	}

	void _Inorder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_Inorder(root->_left);
		cout << root->_kv.first << " ";
		_Inorder(root->_right);
	}
private:
	Node* _root = nullptr;
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值