红黑树(2) - 插入操作

原创 2015年07月09日 00:56:17

1.插入介绍

首先以二叉排序树的方法增加节点并标记它为红色。(为何不是红色?因为如果设为黑色,就会导致根到叶子的所有路径中,有一条路径上会多出一个额外的黑节点,这个是很难调整的)。但是,设为红色节点后,可能会导致出现两个连续红色节点的冲突,则可以通过重新着色和旋转来调整。具体的调整操作取决于其他临近节点的颜色。

下面分析一下插入新节点后可能对红黑树性质产生的影响:
性质1-节点是红色或黑色。和性质3-所有叶子都是黑色。这两条总是可以维持不变。
性质4-每个红色节点的两个子节点都是黑色。只在增加红色节点、重绘黑色节点为红色,或做旋转时受到威胁。
性质5-从每个叶子到根的所有路径都包含相同数目的黑色节点。只在增加黑色节点、重绘红色节点为黑色,或做旋转时受到威胁。

2.插入时的算法

在AVL的插入操作中,我们会使用旋转这个工具来保持插入之后的平衡。在红黑树中,我们会使用到两个工具来保持平衡:
(1) 重新着色
(2) 旋转

首先,会先尝试去重新着色,如果它不能工作,则使用旋转。并且,空节点的颜色默认为黑色。
基于叔节点的颜色,这个算法主要有两种情形。如果叔节点为红色,则需要重新着色。如果是黑色,则使用旋转以及重新着色。

插入过程:
假设x代表将要插入的新节点。p为x的父节点,u为x的叔节点,g为x的祖父节点。
执行标准的二叉排序树(BST)的节点插入操作,并将x置为红色。
此时,会可能形成3种主要的场景,参考下面这一节的具体介绍。

3.场景分类(Scenario)


3.1 场景1:如果X的父结点是黑色,则无需进一步处理

分析:在这种情形下,树仍是有效的。性质4没有失效(新节点是红色的)。性质5也未受到威胁,尽管新节点x有两个黑色叶子节点;但由于x是红色,通过它的每个子节点的路径就都有同通过它所取代的黑色的叶子的路径相等数量的黑色节点,所以依然满足这个性质。

3.2 场景2:如果x是根节点,则将其置为黑色

分析:置为黑色是为了满足性质2。对于完全二叉树,此时黑色节点高度增加1。

3.3 场景3:如果x的父节点是红色,并且x的叔节点是红色

如果x的叔节点是红色(则根据性质4,祖父节点必定是黑色)
(a)将父节点以及叔节点置为黑色。
(b)将祖父节点置为红色。
      至此,现在我们的新节点x有了一个黑色的父节点p。因为通过父节点p或叔节点u的任何路径都必定通过祖父节点g,在这些路径上的黑节点数目没有改变。
      但是,红色的祖父节点g的父节点也有可能是红色的,这就违反了性质4。为了解决这个问题,我们需要在祖父节点g上递归地进行场景分析(即把g当成是新加入的节点进行各种情形的检查)。
(c)执行赋值操作"x = g",将祖父节点赋值给x后,然后递归的对新的x进行场景分析。
以上的步骤,可以使用下图来演示。



3.4 场景4:如果x的父节点是红色,并且x的叔节点是黑色

    在这种情形下,对于x, p以及g有下面4种情形(类似于AVL树)。

    (a)Left Left Case       (p是g的左孩子,且x是p的左孩子)
    (b)Left Right Case    (p是g的左孩子,且x是p的右孩子)
    (c)Right Right Case (p是g的右孩子,且x是p的右孩子)
    (d)Right Left Case    (p是g的右孩子,且x是p的左孩子)

这4种情形,可用下面的这4幅图来进行描述。
(a)Left Left Case (参考g, p以及x)


(b)Left Right Case (参考g, p 以及x)


(c)Right Right Case (参考g, p 以及x)


(d)Right Left Case (参考g, p 以及x)


4.插入操作的例子演示

下面的这个例子,演示了红黑树中插入新节点的过程,覆盖了上面描述的多种场景。

5.代码实现

下面的代码,是严格遵循第3节中所介绍的哪些场景写的。

// C++程序:实现红黑树的插入操作
#include <iostream>

//红黑树节点
struct Node
{
	int data;    //节点数据
	char color;  //颜色属性

	//左孩子,右孩子以及父节点指针
	Node *left, *right, *parent;
};

//左旋
/*
    y                           x
   / \     Right Rotation      /  \
  x   T3   – – – – – – – >    T1   y
 / \       < - - - - - - -        / \
T1  T2     Left Rotation         T2  T3
*/
void LeftRotate(Node **root, Node *x)
{
	//y保存x的右孩子
	Node *y = x->right;

	//将y的左孩子做为x的右孩子
	x->right = y->left;

	//更新x的右孩子的父指针
	if (x->right != NULL)
		x->right->parent = x;

	//更新y的父指针
	y->parent = x->parent;

	//如果x的父节点为null,则y成为新的根节点
	if (x->parent == NULL)
		(*root) = y;
	//将y保存在x的原始位置
	else if (x == x->parent->left)
		x->parent->left = y;
	else    
		x->parent->right = y;

	//将x做为y的左孩子
	y->left = x;

	//更新x的父节点为y
	x->parent = y;
}

//右旋(与左旋正好相反)
/*
    y                           x
   / \     Right Rotation      /  \
  x   T3   – – – – – – – >    T1   y
 / \       < - - - - - - -        / \
T1  T2     Left Rotation         T2  T3
*/
void rightRotate(Node **root, Node *y)
{
	//x保存y的右孩子
	Node *x = y->left;

	//将x的右孩子做为y的左孩子
	y->left = x->right;

	//更新x的右孩子的父指针
	if (x->right != NULL)
		x->right->parent = y;

	//更新x的父指针
	x->parent = y->parent;

	//如果x的父节点为null,则x成为新的根节点
	if (x->parent == NULL)
		(*root) = x;
	//将x保存在y的原始位置
	else if (y == y->parent->left)
		y->parent->left = x;
	else
		y->parent->right = x;

	//将y做为x的右孩子
	x->right = y;

	//更新y的父节点为x
	y->parent = x;
}

//功能函数,在执行标准的BST插入后,调整红黑树。
void insertFixUp(Node **root, Node *z)
{
	//遍历树,直到z不是根节点,并且z的父节点是红色[对应情形3]
	while (z != *root && z->parent->color == 'R')
	{
		Node *y;

		//找到叔节点,保存为y
		if (z->parent == z->parent->parent->left)
			y = z->parent->parent->right;
		else
			y = z->parent->parent->left;

		//如果叔节点为红色,执行下面操作。[对应情形3]
		//(a) 将父节点以及叔节点置为黑色
		//(b) 将祖父节点置为红色
		//(c) 将祖父节点赋值给z,继续检测
		if (y->color == 'R')
		{
			y->color = 'B';
			z->parent->color = 'B';
			z->parent->parent->color = 'R';
			z = z->parent->parent;
		}
		//如果叔节点是黑色,则存在4种情形,LL,LR,RL,RR。 [对应情形4]
		else
		{
			//Left-Left (LL) case
			//(a) 交换父节点与祖父节点的颜色
			//(b) 祖父节点进行右旋
			if (z->parent == z->parent->parent->left &&
				z == z->parent->left)
			{
				char ch = z->parent->color;
				z->parent->color = z->parent->parent->color;
				z->parent->parent->color = ch;
				rightRotate(root, z->parent->parent);
			}

			//Left-Right (LR) case
			//(a) 交换当前节点与祖父节点的颜色
			//(b) 父节点进行左旋
			//(c) 祖父节点进行右旋
			if (z->parent == z->parent->parent->left &&
				z == z->parent->right)
			{
				char ch = z->color;
				z->color = z->parent->parent->color;
				z->parent->parent->color = ch;
				LeftRotate(root, z->parent);
				rightRotate(root, z->parent->parent);
			}

			//Right-Right (RR) case
			//(a) 交换父节点与祖父节点的颜色
			//(b) 祖父节点进行左旋
			if (z->parent == z->parent->parent->right &&
				z == z->parent->right)
			{
				char ch = z->parent->color;
				z->parent->color = z->parent->parent->color;
				z->parent->parent->color = ch;
				LeftRotate(root, z->parent->parent);
			}

			//Right-Left (RL) case
			//(a) 交换当前节点与祖父节点的颜色
			//(b) 父节点进行右旋
			//(c) 祖父节点进行左旋
			if (z->parent == z->parent->parent->right &&
				z == z->parent->left)
			{
				char ch = z->color;
				z->color = z->parent->parent->color;
				z->parent->parent->color = ch;
				rightRotate(root, z->parent);
				LeftRotate(root, z->parent->parent);
			}
		}
	}
	(*root)->color = 'B'; //根节点总是黑色
}

//功能函数:插入新节点至红黑树
void insert(Node **root, int data)
{
	//分配新节点内存
	Node *z = new Node;
	z->data = data;
	z->left = z->right = z->parent = NULL;

	//如果根节点为null,则z成为根节点
	if (*root == NULL)
	{
		z->color = 'B';  //根节点总是黑色
		(*root) = z;
	}
	else
	{
		Node *y = NULL;
		Node *x = (*root);

		// Follow standard BST insert steps to first insert the node
		while (x != NULL)
		{
			y = x;
			if (z->data < x->data)
				x = x->left;
			else
				x = x->right;
		}
		z->parent = y;
		if (z->data > y->data)
			y->right = z;
		else
			y->left = z;
		z->color = 'R';   //新插入节点总是红色

		// 调用insertFixUp来修复红黑树的属性,因为它可能因为插入操作而被破坏。
		insertFixUp(root, z);
	}
}

//功能函数:中序遍历红黑树
void inorder(Node *root)
{
	if (root == NULL)
		return;
	inorder(root->left);
	std::cout << root->data << "-->";
	inorder(root->right);
}

int main()
{
	Node *root = NULL;
	insert(&root, 5);
	insert(&root, 3);
	insert(&root, 7);
	insert(&root, 2);
	insert(&root, 4);
	insert(&root, 6);
	insert(&root, 8);
	insert(&root, 11);
	std::cout<<"Inorder Traversal Is: \n";
	inorder(root);
	std::cout << "null"<<std::endl;

	return 0;
}
运行结果:
Inorder Traversal Is:
2-->3-->4-->5-->6-->7-->8-->11-->null

相关文章推荐

红黑树插入操作分析及代码实现

首先说一下红黑树的五个性质: 1、每个结点要么是红色的,要么是黑色的; 2、根结点是黑色的; 3、每个叶结点,即空结点(NIL)是黑色的; 4、如果一个结点是红色的,那么它的两个子结点都是黑色...

【数据结构】树(三):红黑树插入与删除操作(C++实现)

红黑树性质及操作分析,包含C++代码实现

数据结构-----红黑树的插入操作

红黑树是一棵二叉搜索树;树种每一个节点的颜色不是黑色就是红色。本篇中只实现用节点的颜色来描述红黑树,性质如下: RB1:根节点和所有外部节点都是黑色; RB2:在根至外部节点路径上,没有连续两个节点是...

数据结构之红黑树(二)——插入操作

插入或删除操作,都有可能改变红黑树的平衡性,利用颜色变化与旋转这两大法宝就可应对所有情况,将不平衡的红黑树变为平衡的红黑树。 在进行颜色变化或旋转的时候,往往要涉及祖孙三代节点:X表示操作的基准节点,...

红黑树(基本性质及插入操作)

从二叉查找树中我们可以看到,一些基本的动态操作如查询,求后继、前驱,插入,删除等,其时间复杂度都与树的高度有关,当树的高度较低,也就是二叉树比较平衡的时候,这些操作执行的就会很快;但当树的高度较高时,...

红黑树的基本插入和删除操作

红黑树是一种二叉搜索树,它在每个结点上增加了一个存储位来表示结点的颜色,可以是RED或者BLACK,通过对任何一条从根到叶子的简单路径上各个结点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出2...
  • Mr_Avin
  • Mr_Avin
  • 2017年02月02日 17:12
  • 342

红黑树插入操作超详解

说明:1. 下述图片多参照网上资源(July红黑树)或者电子书资源(STL源码剖析)等              2. 个人感觉STL中思路以及实现方式比其他版本都易懂,建议大家好好琢磨 参考...

今天完成了红黑树的删除操作。插入、删除完成。

#ifndef __BLACK_RED_TREE #define __BLACK_RED_TREE #include using namespace std; #define RED 1 #d...

Red-Black Tree红黑树(插入操作)

插入操作可能会造成不平衡。 为了使数平衡,AVL树需要rotate操作。那么红黑树怎么弄呢?红黑树需要: 1)recoloring 2) rotate 下面举例说明 假设x是新插入的节点。 ...
  • bdss58
  • bdss58
  • 2014年12月12日 16:00
  • 449
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:红黑树(2) - 插入操作
举报原因:
原因补充:

(最多只允许输入30个字)