数据结构之——红黑树

红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是red或black。通过对任何一条从根到叶子简单路径上的颜色来约束,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡。

红黑树是满足下面红黑性质的二叉搜索树:

  1. 每个节点,不是红色就是黑色的
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个子节点是黑色的(没有连续的红节点)
  4. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。(每条路径的黑色节点的数量相等)

这里分析一下为什么红黑树能保证最长路径不超过最短路径的两倍:首先因为第4条约束条件假设一棵树如下所示:

B

B   B

B      B    B表示为黑色结点,那么要在其中插入任何一个黑色结点就需要保证第4条约定,而如果要插入红色结点,则第3条约束又使得红色结点只能插在黑色结点之间,因此一条路径最多变成:

B

B   R

B      B

R

B      因此,最长路径不超过最短路径的两倍,也就保证了搜索的效率;

下面是实现红黑树的插入过程:

#pragma once

#include <iostream>
using namespace std;

//结点的颜色 红or黑
enum Color
{
	RED,
	BLACK
};

//结点结构体
template <class K, class V>
struct RBTreeNode
{
	K _key;
	V _val;
	RBTreeNode<K, V>* _left;
	RBTreeNode<K, V>* _right;
	RBTreeNode<K, V>* _parent;
	Color _col;

	RBTreeNode(const K& key, const V& val)
		:_key(key)
		,_val(val)
		,_left(NULL)
		,_right(NULL)
		,_parent(NULL)
		,_col(RED)
	{}
};

//红黑树类
template <class K, class V>
class RBTree
{
	typedef RBTreeNode<K, V> Node;

public:
	RBTree()
		:_root(NULL)
	{}
	~RBTree()
	{
		_Clear(_root);
	}

	//插入结点
	bool Insert(const K& key, const V& val)
	{
		if(_root == NULL)//如果根结点为NULL,创建新的结点为根结点返回true
		{
			_root = new Node(key, val);
			_root->_col = BLACK;
			return true;
		}

		//如果根结点不为NULL,则遍历树直到找到合适的插入位置
		Node* cur = _root;
		Node* prev = cur;
		while(cur != NULL)
		{
			if(cur->_key == key)//如果树中已经有该结点,则返回false
				return false;
			else if(cur->_key > key)//如果关键值小于结点的关键值,则去左子树找
			{
				prev = cur;
				cur = cur->_left;
			}
			else//否则关键值大于结点的关键值,去右子树找
			{
				prev = cur;
				cur = cur->_right;
			}
		}
		//循环结束,找到了合适的插入位置,判断应该插入到结点的左边还是右边
		Node* tmp = new Node(key, val);
		if(prev->_key > key)
			prev->_left = tmp;
		else
			prev->_right = tmp;
		tmp->_parent = prev;

		//插入结点之后,就要开始判断目前的树是否符合红黑树的性质
		while((tmp != _root) && (prev->_col == RED))
		{
			Node* grandfather = prev->_parent;//提取出tmp的祖父结点
			if(grandfather->_left == prev)//如果prev是grandfather的左结点
			{
				//第一种情况
				//tmp为红,prev为红,grandfather为黑,uncle存在且为红
				//则将prev,uncle改为黑,grandfather改为红,然后把grandfather当成tmp,继续向上调整。
				Node* uncle = grandfather->_right;
				if(uncle != NULL && uncle->_col == RED)
				{
					prev->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					tmp = grandfather;
					prev = tmp->_parent;
				}
				else//第二种情况:tmp为红,prev为红,grandfather为黑,uncle不存在/uncle为黑
					//prev为grandfather的左孩子,tmp为prev的左孩子,则进行右单旋转;
					//prev、grandfather变色--prev变黑,grandfather变红

				{
					//第三种情况
					//tmp为红,prev为红,grandfather为黑,uncle不存在/uncle为黑
					//prev为grandfather的左孩子,tmp为prev的右孩子,则针对prev做左单旋转;
					//则转换成了情况二

					if(prev->_right == tmp)
					{
						_RotateL(prev);
						swap(tmp, prev);
					}
					_RotateR(grandfather);//进行右单旋
					prev->_col = BLACK;
					grandfather->_col = RED;
					break;
				}
			}
			else//当perv是grandfather的右结点的时候,和上面的情况相反
			{
				//第一种情况
				//tmp为红,prev为红,grandfather为黑,uncle存在且为红
				//则将prev,uncle改为黑,grandfather改为红,然后把grandfather当成tmp,继续向上调整。
				Node* uncle = grandfather->_left;
				if(uncle != NULL && uncle->_col == RED)
				{
					prev->_col = BLACK;
					uncle->_col = BLACK;
					grandfather->_col = RED;

					tmp = grandfather;
					prev = tmp->_parent;
				}
				else//第二种情况:tmp为红,prev为红,grandfather为黑,uncle不存在/uncle为黑
					//prev为grandfather的右孩子,tmp为prev的右孩子,则进行左单旋转
					//prev、grandfather变色--prev变黑,grandfather变红

				{
					//第三种情况
					//tmp为红,prev为红,grandfather为黑,uncle不存在/uncle为黑
					//prev为grandfather的右孩子,tmp为prev的左孩子,则针对prev做右单旋转
					//则转换成了情况二

					if(prev->_left == tmp)
					{
						_RotateR(prev);
						swap(tmp, prev);
					}
					_RotateL(grandfather);//进行右单旋
					prev->_col = BLACK;
					grandfather->_col = RED;
					break;
				}
			}
		}
		//如果根结点被调整成了红色,将其改为黑色,并会不影响左右黑结点的个数
		if(_root->_col == RED)
			_root->_col = BLACK;
		return true;
	}

	void InOrder()
	{
		_InOrder(_root);
		cout<<endl;
	}

	/*bool Find(const K& key)
	{

	}*/

private:
	void _Clear(Node* root)
	{
		if(root == NULL)
			return;

		_Clear(root->_left);
		_Clear(root->_right);
		delete root;
		root = NULL;
	}

	void _RotateL(Node* parent)//左单旋
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
 
        parent->_right = subRL;
        if (subRL != NULL)
            subRL->_parent = parent;
         
        Node* ppNode = parent->_parent;
 
        subR->_left = parent;
        parent->_parent = subR;
 
        if (ppNode == NULL)
            _root = subR;
        else
        {
            if (ppNode->_left == parent)
                ppNode->_left = subR;
            else
                ppNode->_right = subR;
        }
		subR->_parent = ppNode;
    }

    void _RotateR(Node* parent)//右单旋
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
 
        parent->_left = subLR;
        if (subLR != NULL)
            subLR->_parent = parent;
 
        Node* ppNode = parent->_parent;
        subL->_right = parent;
        parent->_parent = subL;
 
        if (ppNode == NULL)
            _root = subL;
        else
        {
            if (ppNode->_left == parent)
                ppNode->_left = subL;
            else
                ppNode->_right = subL;
        }
		subL->_parent = ppNode;
	}

	void _InOrder(Node* root)
	{
		if(root != NULL)
		{
			_InOrder(root->_left);
			cout<<root->_key<<" ";
			_InOrder(root->_right);
		}
	}

private:
	Node* _root;
};

void Test()
{
	int arr[] = {3, 4, 6, 1, 7, 2, 8};
	RBTree<int, int> t;
	for(int i = 0; i < sizeof(arr)/sizeof(arr[0]); ++i)
		t.Insert(arr[i], i);

	t.InOrder();
}

运行程序:

《完》

时间: 2024-08-02 15:47:57

数据结构之——红黑树的相关文章

D&amp;F学数据结构系列——红黑树

红黑树 定义:一棵二叉查找树如果满足下面的红黑性质,则为一棵红黑树: 1)每个结点不是红的就是黑的 2)根结点是黑的 3)每个叶结点是黑的 4)如果一个结点是红的,它的两个儿子都是黑的(即不可能有两个连续的红色结点) 5)对于每个结点,从该结点到其子孙结点的所有路径上包含相同数目的黑结点 性质: 这些约束确保了红黑树的关键特性: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长.结果是这个树大致上是平衡的.因为操作比如插入.删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上

nginx学习九 高级数据结构之红黑树ngx_rbtree_t

1红黑树简介 先来看下算法导论对R-B Tree的介绍: 红黑树,一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black. 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平的. 红黑树,作为一棵二叉查找树,满足二叉查找树的一般性质.下面,来了解下 二叉查找树的一般性质. 二叉查找树 二叉查找树,也称有序二叉树(ordered binary tree),或已排序二叉树(sorted binary tree

数据结构之红黑树

红黑树(Red Black Tree) 是一种自平衡二叉查找树 红黑树和AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能.它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目.(度娘)C++ stl里面的set,map底层就是用红黑树实现的.红黑树具体的插入删除原理请参考<<算法导论>> 维基上面也讲得不错.反正插入过程就是要解决&q

数据结构:红黑树解析

本文参考:Google.算法导论.STL源码剖析.计算机程序设计艺术. 推荐阅读: Left-Leaning Red-Black Trees, Dagstuhl Workshop on Data Structures, Wadern, Germany, February, 2008,直接下载:http://www.cs.princeton.edu/~rs/talks/LLRB/RedBlack.pdf. 本文的github优化版:https://github.com/julycoding/The

数据结构--树--红黑树

R-B Tree简介 R-B Tree,全称是Red-Black Tree,又称为“红黑树”,它一种特殊的二叉查找树.红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black). 红黑树的特性:(1)每个节点或者是黑色,或者是红色.(2)根节点是黑色.(3)每个叶子节点(NIL)是黑色. [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!](4)如果一个节点是红色的,则它的子节点必须是黑色的.(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点.

数据结构之红黑树(三)——删除操作

删除一个节点相同有可能改变树的平衡性,并且,删除所造成的不平衡性比插入所造成的平衡性的修正更加复杂. 化繁为简是算法分析中一个经常使用的方法.以下我们将欲删除节点分为三大类:欲删除节点为叶子节点.欲删除节点仅仅有一个子节点和欲删除有两个子节点. 而欲删除节点有两种可能的颜色,也须要分别对待. 为简化讨论,我们以欲删除节点在左側的情况为例进行修正,假设欲删除节点在右側,进行镜像地修正操作就可以. 1. 欲删除节点是叶子节点 1.1 欲删除节点为红色,父节点必为黑色.必无兄弟节点. 仅仅有下图所看到

【数据结构】红黑树

红黑树 目的 在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能. 效率 查找,插入和 删除 时间复杂度:O(log n) ,n 是树中元素数目. 性质 节点是红色或黑色. 根节点是黑色. 每个叶节点(NIL节点,空节点)是黑色的. 每个红色节点的两个子节点都是黑色.(从每个叶子到根的所有路径上不能有两个连续的红色节点) 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点. 版权声明:本文为博主原创文章,未经博主允许不得转载.

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

插入或删除操作,都有可能改变红黑树的平衡性,利用颜色变化与旋转这两大法宝就可应对所有情况,将不平衡的红黑树变为平衡的红黑树. 在进行颜色变化或旋转的时候,往往要涉及祖孙三代节点:X表示操作的基准节点,P代表X的父节点,G代表X的父节点的父节点. 我们先来大体预览一下插入的过程: 1.沿着树查找插入点,如果查找过程中发现某个黑色节点的两个子节点都是红色,则执行一次颜色变换(父节点变为红色,而两个红色子节点变为黑色). 2.第1步中,不会改变子树的黑色高度,但是可能会出现颜色冲突(红-红颜色冲突),

linux内核数据结构之红黑树

首先我先回顾一下二叉树 然后回顾一下二叉搜索树 下面是重头戏 自平衡二叉搜索树满足二叉搜索树的条件.即每个节点左边的节点值都要比自己小,然后满足平衡,即树(包括子树)的末尾节点深度相差小于1,这样的树称为平衡二叉搜索树 最后红黑树 红黑树有着插入,删除,搜索非常快的优点,特别是插入和删除要比平衡二叉搜索树要快,所以在有频繁的插入和删除操作的情况下,使用红黑树进行存储是非常有效的. linux内核中提供了红黑树的基本算法,我们只需要构造自己的插入,删除,和搜索函数就可以根据自己的需求使用红黑树了.