【数据结构】红黑树 的介绍及模拟实现

1. 红黑树的概念

红黑树(英语:Red–black tree)是一种自平衡二叉搜索树,是在计算机科学中用到的一种数据结构,典型用途是实现关联数组。它在1972年由鲁道夫·贝尔发明,被称为"对称二叉B树",它现代的名字源于Leo J. Guibas和Robert Sedgewick于1978年写的一篇论文。红黑树的结构复杂,但它的操作有着良好的最坏情况运行时间,并且在实践中高效:它可以在O(log n)时间内完成查找、插入和删除,这里的n是树中元素的数目

  • 在计算机科学中,关联数组(英语:Associative Array),又称映射(Map)、字典(Dictionary)是一个抽象的数据结构,它包含着类似于(键,值)的有序对。一个关联数组中的有序对可以重复(如C++中的multimap)也可以不重复(如C++中的map)。

2. 红黑树的性质

红黑树是每个节点都带有颜色属性二叉查找树颜色为红色或黑色,对于任何有效的红黑树,需要满足以下性质:

  1. 每个结点不是红色就是黑色
  2. 根节点必须是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

红黑树图例

在这里插入图片描述

关键特性:通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍

3. 红黑树的结构

head头结点中:

  • parent指针指向null
  • left指针指向树中最小值结点
  • right指针指向树中最大值结点

root根结点的parent指针指向head头结点

image-20210222200140639

4. 红黑树节点的定义

节点样式

在这里插入图片描述

// 红黑树节点的颜色
enum Color
{
   
	RED,
	BLACK
};

template<class T>
struct RBTreeNode
{
   
	RBTreeNode<T>* left;	// 节点的左孩子
	RBTreeNode<T>* right;	// 节点的右孩子
	RBTreeNode<T>* parent;	// 节点的父节点
	T data;		// 节点中保存的值
	Color color;	// 节点的颜色
	// 将结点颜色默认设置为红色,因为如果设置成黑色会破坏原有黑色节点的个数
	RBTreeNode(const T& x = T(), Color c = RED) :left(nullptr), right(nullptr), parent(nullptr), data(x), color(c){
   }
};

// 红黑树结构及定义
// T : 表示红黑树中放置的元素类型
// KOFP:表示从T中提取Key
template<class T, class KOFP>
class RBTree
{
   
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T> iterator;	//迭代器
    
private:
	Node* head;	 // 指向红黑树头结点的指针
    size_t _size;	// 保存红黑树的节点个数
};

5. 红黑树的迭代器

红黑树的迭代器实现了:重载了++、–、!=、==、*、->这些操作符

// 迭代器
template<class T>
struct RBTreeIterator
{
   
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T> self;
public:
	RBTreeIterator(Node* n = nullptr) : node(n){
   }
	// 具有指针类似的方法
	T& operator*()
	{
   
		return node->data;
	}
	T* operator->()
	{
   
		return &(operator*());
	}
	/*
	找比当前节点大的 所有节点中最小的节点:
	1. 如果当前节点的右子树存在,应该在其右子树中找最小值节点(最左侧)
	2. 如果当前节点的右子树不存在,应该在其双亲中不断查找
	*/
	self& operator++()
	{
   
		Increment();
		return *this;
	}
	self& operator++(int)
	{
   
		self temp(*this);
		Increment();
		return temp;
	}

	/*
	找比当前迭代器小的 所有节点中最大的:
	1. 如果当前节点的左子树存在,应该在其左子树中找最大值节点(最右侧)
	2. 如果当前节点的左子树不存在,
	*/
	self& operator--()
	{
   
		Decrement();
		return *this;
	}
	self& operator--(int)
	{
   
		self temp(*this);
		Decrement();
		return temp;
	}
	bool operator!=(const self& s)const
	{
   
		return node != s.node;
	}
	bool operator==(const self& s)const
	{
   
		return node == s.node;
	}

private:
	Node* node;		// 封装 节点指针

	// 找当前迭代器的后一个位置
	void Increment()
	{
   
		if (node->right)
		{
   
			// 当前节点的右子树如果存在,就在右子树中寻找最小值
			node = node->right;
			while (node->left)
			{
   
				node = node->left;
			}
		}
		else
		{
   
			// 当前节点的右子树不存在,在父节点中寻找比当前节点大的节点
			// 什么情况下才算大呢? 当node是双亲的左孩子时,才算找到
			Node* parent = node->parent;
			while (node == parent->right)
			{
   
				node = parent;
				parent = parent->parent;
			}
			// 防止特殊情况:当根结点没有右子树时,迭代器恰好在根节点的位置
			if (node->right != parent)
				node = parent;	// 此时node才指向父节点中比之前节点大的节点 
		}
	}
	// 找当前迭代器的前一个位置
	void Decrement()
	{
   
		if (node == node->parent->parent && RED == node->color)
		{
   
			// node 在 end 的位置
			node = node->right;

		}
		else if (node->left)
		{
   
			// 到 node 的左子树找最大的节点
			node = node->left;
			while (node->right)
			{
   
				node = node->right;
			}
		}
		else
		{
   
			// 右子树不存在 -> 应该到 node 的双亲中找出比 node 小的节点
			Node* parent = node->parent;
			while (node == parent->left)
			{
   
				node = parent;
				parent = node->parent;
			}
			node = parent;
		}
	}
};

6. 红黑树节点的插入

插入会遇到的破坏性质的三种情况:

情况1:当前插入的节点是红色的,父节点是红色的,祖父节点是黑色的,叔叔节点存在且为红色

解决方法:将父节点和叔叔节点变为黑色,将祖父节点变为红色,

image-20210222201608352

情况二:cur为红,p为红,g为黑,u不存在/u为黑

解决方式

①p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,p为g的右孩子,cur为p的右孩子,则进行左单旋转
②p、g变色–p变黑,g变红

在这里插入图片描述

情况三:cur为红,p为红,g为黑,u不存在/u为黑

解决方式:

①p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,p为g的右孩子,cur为p的左孩子,则针对p做右单旋转,则转换成了情况2

②按照情况二进行处理

image-20210222203024349

代码实现

// 插入数据构造红黑树
pair<iterator, bool> insert(const T& data)
{
   
    Node*& root = getRoot();	// 保存根结点

    // 1. 按照BST的规则寻找插入结点的位置
    // 1.1 若root为空,则需要建立root节点
    if (nullptr == root)
    {
   
        root = new Node(data, BLACK);	// 根结点默认为黑色
        root->parent = head;
        head->left = root;
        head->right = root;
        _size = 1;
        return make_pair
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值