红黑树的代码实现
C语言实现红黑树详细步骤+代码

C语⾔实现红⿊树详细步骤+代码⽬录红⿊树的概念红⿊树的性质红⿊树的定义与树结构插⼊新增结点插⼊后维护红⿊树性质的主逻辑拆解讨论:旋转验证红⿊树与AVl树的⽐较红⿊树的应⽤总结红⿊树的概念红⿊树,是⼀种⼆叉搜索树,但在每个结点上增加⼀个存储位表⽰结点的颜⾊,可以是Red或Black。
通过对任何⼀条从根到叶⼦的路径上各个结点着⾊⽅式的限制,红⿊树确保没有⼀条路径会⽐其他路径长出俩倍,因⽽是接近平衡的概念总结:红⿊树是⼆叉搜索树的升级,结点⾥⾯存放的成员col标记当前结点的颜⾊,它的最长路径最多是最短路径的⼆倍,红⿊树通过各个结点着⾊⽅式的限制接近平衡⼆叉树,但是不同于AVL的是AVL是⼀颗⾼度平衡的⼆叉树,红⿊树只是接近平衡红⿊树的性质每个结点不是红⾊就是⿊⾊根节点是⿊⾊的如果⼀个节点是红⾊的,则它的两个孩⼦结点是⿊⾊的对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数⽬的⿊⾊结点每个叶⼦结点都是⿊⾊的(此处的叶⼦结点指的是空结点)红⿊树性质总结:1、红⿊树结点的颜⾊只能是红⾊或者⿊⾊2、红⿊树根节点必须是⿊⾊3、红⿊树并没有连续的红⾊结点4、红⿊树中从根到叶⼦的每⼀条路径都包含相同的⿊⾊结点5、叶⼦是⿊⾊,表⽰空的位置最长路径和最短路径概念:最短路径:从根结点到叶⼦结点每⼀条路径的结点颜⾊都是⿊⾊的不包含红⾊最长路径:红⿊交替,⿊⾊结点和红⾊结点的个数相等思考:为什么满⾜上⾯的性质,红⿊树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?假设结点个数为N,那么最短路径就是logN,最长路径就是2 * logN,所有并不存在最长路径超过最短路径2倍的情况红⿊树的定义与树结构//枚举红⿊颜⾊enum colour{RED,BLACK,};//定义红⿊树结点结构template<class K,class V>struct RBTreeNode{//构造RBTreeNode(const pair<K, V>& kv = {0,0}):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv),_col(BLACK){ }//定义三叉链RBTreeNode<K, V>* _left; //左孩⼦RBTreeNode<K, V>* _right;//右孩⼦RBTreeNode<K, V>* _parent; //⽗亲pair<K, V> _kv; //pair对象//节点的颜⾊colour _col; //定义枚举变量};//定义红⿊树template<class K, class V>class RBTree{typedef RBTreeNode<K, V> Node;public://构造RBTree():_root(nullptr){}private:Node* _root; //定义树的根节点};插⼊插⼊过程类似搜索树的插⼊,重要的是维护红⿊树的性质pair<Node*, bool> Insert(const pair<K, V>& kv){if (!_root) //空树处理{_root = new Node(kv);_root->_col = BLACK;return { _root, true };}//⼆叉搜索树的插⼊逻辑Node* cur = _root, * parent = nullptr;while (cur){if (cur->_kv.first < kv.first)//插⼊结点⽐当前结点⼤{parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first) //插⼊结点⽐当前结点⼩{parent = cur;cur = cur->_left;}else{return { cur, false }; //插⼊失败}}cur = new Node(kv);cur->_col = RED; //新增结点颜⾊默认设置为RED//判断插⼊结点是否在parent的左边或者右边if (parent->_kv.first > kv.first) //左边{parent->_left = cur;cur->_parent = parent;}else //右边{parent->_right = cur;cur->_parent = parent;}/* 红⿊树性质处理:如果这棵树⼀开始是符合红⿊树的性质,但在新增结点之后,导致失去了红⿊树的性质,这⾥需要控制结点的颜⾊和限制每条路径上⿊⾊结点的个数,以上情况都要处理*/while (parent && parent->_col == RED) //⽗亲存在且⽗亲为红⾊{Node* grandfather = parent->_parent; //祖⽗//⽗亲出现在祖⽗的左边需要考虑的情况if(parent == grandfather ->left){//1、uncle存在,uncle为红⾊/*如果parent和uncle都存在并且都为红⾊这是情况⼀,需要将parent和uncle的颜⾊变成红⾊,祖⽗颜⾊变成⿊⾊更新cur、parent、grandfather、uncle 继续向上调整*///2、uncle不存在/* 这⾥考虑两种旋转情况,直线单旋转,折线双旋/*cur出现在parent的左边 ,右单旋转经过右单旋后,parent去做树的根,祖⽗做为右⼦树//调节结点颜⾊parent->_col = BLACK;grandfather->_col = RED;*//*cur出现在parent的右边,左右双旋经过双旋后,cur作为树的根,grandfather为右⼦树调节结点颜⾊cur->_col = BLACK;grandfather->_col = RED;*/*/}else //⽗亲出现在祖⽗的右边{Node* uncle = grandfather->_left; //叔叔在左⼦树/*情况⼀:叔叔存在,且叔叔和⽗亲都是红⾊,那么就需要将⽗亲和叔叔结点的颜⾊变成⿊⾊,再将祖⽗的颜⾊变成红⾊,继续向上调整,更新孩⼦、⽗亲、祖⽗、叔叔的位置*//*情况⼆:叔叔不存在/*1、新增结点出现在⽗亲的右边,直线情况,左单旋处理旋转完后parent去做⽗亲的根,grandfather做⽗亲的左⼦树//调节颜⾊,根为⿊,左右孩⼦为红2、新增结点出现在⽗亲的左边,会出现折现的情况,引发双旋,旋转完后,cur变成根,parent和grandfaher去做cur的左右孩⼦//调节颜⾊,根结点为⿊,左右孩⼦为红*/*/}}//如果⽗亲不存在为了保证根结点是⿊⾊的,这⾥⼀定得将根结点处理为⿊⾊_root->_col = BLACK;}新增结点插⼊后维护红⿊树性质的主逻辑//1、⽗亲⼀定存在的情况,叔叔存在/不存在⽗亲叔叔结点颜⾊为红⾊while (parent && parent->_col == RED) //⽗亲存在且⽗亲为红⾊{Node* grandfather = parent->_parent; //祖⽗//如果⽗亲和叔叔结点颜⾊都是红⾊if (parent == grandfather->_left){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED) //对应情况:uncle存在且为红{//处理:⽗亲和叔叔变成⿊⾊,祖⽗变成红⾊,继续向上调整uncle->_col = parent->_col = BLACK;grandfather->_col = RED;//向上调整cur = grandfather; //调整孩⼦parent = cur->_parent;//调整⽗亲}else //uncle不存在,uncle存在且为⿊{//直线情况(cur在parent的左边):只考虑单旋,以grandfather为旋转点进⾏右单旋转,//旋转完后将祖⽗的颜⾊变成红⾊,将⽗亲的颜⾊变成⿊⾊if (parent->_left == cur){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else //parent->_right == cur{//折线情况(cur在parent的右边):这⾥会引发双旋RotateL(parent); //以parent为旋转点进⾏左单旋RotateR(grandfather); //以grandfather为旋转点进⾏右单旋转//旋转完后cur会去做树的根,那么设置为⿊⾊,//为了保证每条路径的⿊⾊结点个数相同,grandfather结点颜⾊设置为红cur->_col = BLACK;grandfather->_col = RED; //⿊⾊结点个数相同}}}else //⽗亲在右⼦树{Node* uncle = grandfather->_left; //叔叔在左⼦树if (uncle&& uncle->_col == RED) //情况⼀处理:叔叔存在,且叔叔的颜⾊是红⾊的(包含了⽗亲的颜⾊是红⾊的情况){//根据情况⼀处理即可:叔叔和⽗亲变⿊,//祖⽗变红(⽬的是为了每条路径的⿊⾊结点个数相同),继续向上cur = grandfather; //孩⼦parent = cur->_parent;//⽗亲}else //叔叔不存在{if (cur == parent->_right) //新增结点在⽗亲的右边,直线情况左单旋处理{//左单旋转,以grandfather为旋转点,旋转完后parent去做新的根,grandfather去做左⼦树RotateL(grandfather);//调节颜⾊grandfather->_col = RED;parent->_col = BLACK;}else //新增结点在⽗亲的左边,折线情况,引发双旋{//处理:以parenrt为旋转点做右单旋,再以grandfather为旋转点做左单旋RotateR(parent); //右旋RotateL(grandfather); //左旋parent->_col = grandfather->_col = RED;cur->_col = BLACK;}break;}}_root->_col = BLACK;}拆解讨论:以下只列举parent在grandfather左边的情况,⽽parent在grandfather右边的情况处理⽅式只是反过来的,读者可以⾃⾏画图,这⾥仅留参考代码Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED) //对应情况:uncle存在且为红{//处理:⽗亲和叔叔变成⿊⾊,祖⽗变成红⾊,继续向上调整uncle->_col = parent->_col = BLACK;grandfather->_col = RED;//向上调整cur = grandfather; //调整孩⼦parent = cur->_parent;//调整⽗亲}else //uncle不存在,uncle存在且为⿊{//直线情况(cur在parent的左边):只考虑单旋,以grandfather为旋转点进⾏右单旋转, //旋转完后将祖⽗的颜⾊变成红⾊,将⽗亲的颜⾊变成⿊⾊if (parent->_left == cur){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else //parent->_right == cur{//双旋转}}//折线情况(cur在parent的右边):这⾥会引发双旋RotateL(parent); //以parent为旋转点进⾏左单旋RotateR(grandfather); //以grandfather为旋转点进⾏右单旋转//旋转完后cur会去做树的根,那么设置为⿊⾊,//为了保证每条路径的⿊⾊结点个数相同,grandfather结点颜⾊设置为红cur->_col = BLACK;grandfather->_col = RED;旋转void RotateR(Node* parent) //右单旋{Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR) subLR->_parent = parent; //防⽌subLR为nullptrsubL->_right = parent;Node* parent_parent = parent->_parent; //指针备份parent->_parent = subL;if (_root == parent) //如果parent就是树的根{_root = subL; //subL取代parent_root->_parent = nullptr;}else //如果parent并不是树的根{if (parent_parent->_left == parent) parent->_left = subL;else parent_parent->_right = subL;subL->_parent = parent_parent; //subL去做parent_parent的孩⼦ }}//左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL) subRL->_parent = parent;subR->_left = parent;Node* parent_parent = parent->_parent;parent->_parent = subR;if (_root == parent){_root = subR;_root->_parent = nullptr;}else{if (parent_parent->_left == parent) parent_parent->_left = subR; else parent_parent->_right = subR;subR->_parent = parent_parent;}}验证/*红⿊树的⼏点性质在于:1、根结点必须是红⾊的2、不会出现连续的红⾊结点3、所有路径的⿊⾊结点个数是相同的*/bool _CheckBlance(Node* root, int isBlackNum, int count){if (!root){if (isBlackNum != count){printf("⿊⾊结点个数不均等\n");return false;}return true; //遍历完整棵树,如果以上列举的⾮法情况都不存在就返回true}//检查是否出现连续的红⾊结点if (root->_col == RED && root->_parent->_col == RED){printf("出现了连续的红⾊结点\n");return false;}//⾛前序遍历的过程中记录每⼀条路径⿊⾊结点的个数if (root->_col == BLACK) count++;//递归左右⼦树return _CheckBlance(root->_left, isBlackNum, count) &&_CheckBlance(root->_right, isBlackNum, count);}//验证红⿊树bool CheckBlance(){if (!_root) return true; //树为null//根结点是⿊⾊的if (_root->_col != BLACK){printf("根结点不是⿊⾊的\n");return false;}//每⼀条路径⿊⾊结点的个数必须是相同的,int isBlcakNum = 0;Node* left = _root;while (left){if (left->_col == BLACK) isBlcakNum++; // 统计某⼀条路径的所以⿊⾊结点个数left = left->_left;}//检查连续的红⾊结点,检查每⼀条路径的⿊⾊结点个数是否相等return _CheckBlance(_root, isBlcakNum ,0);}红⿊树与AVl树的⽐较红⿊树与AVL树的⽐较红⿊树和AVL树都是⾼效的平衡⼆叉树,增删改查的时间复杂度都是O( log n),红⿊树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对⽽⾔,降低了插⼊和旋转的次数,所以在经常进⾏增删的结构中性能⽐AVL树更优,⽽且红⿊树实现⽐较简单,所以实际运⽤中红⿊树更多。
java treemap实现原理

java treemap实现原理Java TreeMap是Java中非常常用的一种数据结构,使用红黑树作为其底层实现。
它提供了一种将键映射到值的方式,键是唯一的,并且按照升序进行排序。
Java TreeMap的实现原理是非常有趣的,它主要涉及到红黑树、迭代器、比较器等知识点。
在本文中,我们将深入了解Java TreeMap的实现原理,并理解如何在代码中使用它。
1. 红黑树红黑树是一种自平衡的二叉搜索树。
它通过保持一些简单规则来保证树的平衡,以确保左右子树的高度之差不超过1,并且保证每个节点的颜色都为红色或黑色。
这些规则允许红黑树保持在O(log n)的时间复杂度下进行插入、搜索和删除操作。
在Java TreeMap中,红黑树被用作底层存储结构。
当添加一个新的键值对时,它会首先检查根节点是否为空。
如果是,则创建一个新的节点并将其设置为根节点。
否则,它会沿着树的路径查找适当的叶子节点,并将其插入为其左侧或右侧的子节点。
为了保持树的平衡,通过旋转和重新着色来调整节点的颜色和位置。
每个节点都有一个颜色标记,标记为红色或黑色,红色表示该节点是一个违反规则的节点,黑色表示该节点是一个符合规则的节点。
2. TreeMap的比较器Java TreeMap还有另一个重要的组件,即比较器。
所有元素的排序都是通过比较器来定义的。
比较器定义了如何将元素按照升序排列,应该提供一个实现了 Comparator 接口的类。
在Java TreeMap的实现中,比较器用来将元素按照顺序排列。
它允许 TreeMap 将元素按照自定义顺序排序而不是按照它们的自然顺序排序。
也就是说,比较器的作用是自定义元素排序的规则并将其存储在TreeMap中。
3. TreeMap的迭代器Java TreeMap还提供了迭代器,用于遍历TreeMap中的元素。
什么是迭代器?迭代器是用于遍历集合或列表中元素的指针。
在Java中,每个集合或列表都可以通过iterator() 方法返回它的迭代器。
红黑树代码

#ifndef REDBLACK_INCLUDE#define REDBLACK_INCLUDEtemplate<typename T>class RedBlackTree{struct Node{T key;bool color;Node* parent;Node* left;Node* right;Node(T data_, bool color_, Node* p, Node* l, Node* r):key(data_),color(color_),parent(p),left(l),right(r){}};Node* NIL;Node* root;bool RED ;bool BLACK ;public:RedBlackTree(T data=0, bool str=false, Node* p=NULL, Node* q=NULL, Node* e=NULL):RED(true),BLACK(false){NIL = new Node(0, BLACK, NIL, NIL, NIL);//哨兵结点root = NIL;}~RedBlackTree(){DeleteTree(root);delete NIL;}void LeftRotate(Node* x);void RightRotate(Node* y);void RBInsert(T data);void RBInsertFixup(Node* z);void RBDelete(T data);void RBDeleteFixUp(Node* x);Node* TreeSearchNumber(Node* x, T k);Node* TreeSuccessor(Node* x);void DeleteTree(Node* x);void PrintTree(Node* x);Node* GetNode(void);Node* TreeMinIMUM(Node* x );};#endif//RedBlackTree_.h#ifndef REDBLACKTREE_H_INCLUDE#define REDBLACKTREE_H_INCLUDE#include "RedBlack.h"#include <assert.h>#include <string>#include <iostream>template<typename T>RedBlackTree<T>::Node* RedBlackTree<T>::TreeMinIMUM(Node* x ) {while(x->left != NIL)x = x->left;return x;}template<typename T>RedBlackTree<T>::Node* RedBlackTree<T>::GetNode(void){return root;}template<typename T>void RedBlackTree<T>::PrintTree(Node* x){if(NIL != x){if(NIL != x->left){PrintTree(x->left);}if(NIL != x->right){PrintTree(x->right);}std::cout<<x->key<<std::endl;}}template<typename T>void RedBlackTree<T>::DeleteTree(Node* x){if(x != NIL){if(NIL != x->left ){DeleteTree(x->left);}if(NIL != x->right ){DeleteTree(x->right);}delete x;x = NULL;}}template<typename T>RedBlackTree<T>::Node* RedBlackTree<T>::TreeSearchNumber(Node* x, T k) {if( x == NIL || k == x->key )return x;if( k < x->key )return TreeSearchNumber(x->left, k);elsereturn TreeSearchNumber(x->right, k);}template<typename T>RedBlackTree<T>::Node* RedBlackTree<T>::TreeSuccessor(Node* x)//存在两种情况: {if (x->right != NIL)//如果结点x的右子树非空,则x的后继即右子树中的最左的结点。
c++ 红黑树 map原理

c++ 红黑树 map原理
C++中的红黑树是一种自平衡的二叉查找树,它常用于实现关联
容器中的map数据结构。
红黑树通过对节点进行着色和旋转操作来
保持树的平衡,从而确保在最坏情况下的查找、插入和删除操作的
时间复杂度为O(log n)。
红黑树的基本原理包括以下几点:
1. 节点着色,每个节点都被标记为红色或黑色,这些颜色标记
用于确保树的平衡。
2. 根节点和叶子节点,根节点是黑色的,叶子节点(NIL节点)是黑色的。
3. 红色节点规则,红色节点的子节点必须是黑色的,即不能出
现两个相连的红色节点。
4. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色
节点。
这保证了树的黑色平衡。
在C++中,STL中的map数据结构通常使用红黑树来实现。
map 是一种关联容器,它提供了一种将键和值关联起来的方式。
当我们向map中插入新的键值对时,红黑树会自动进行调整以保持平衡。
查找、插入和删除操作都能在O(log n)的时间复杂度内完成,这使得map非常高效。
总之,红黑树是一种高效的自平衡二叉查找树,它通过节点着色和旋转操作来维持平衡。
在C++中,map数据结构通常使用红黑树来实现,从而提供了高效的插入、删除和查找操作。
这些原理和实现细节都是为了确保数据结构的高效性和稳定性。
c语言红黑树遍历序算法

c语言红黑树遍历序算法红黑树是一种自平衡的二叉查找树,它具有良好的平衡性能,能够保持在较低的高度范围内。
在C语言中,我们可以使用递归或者非递归的方式来实现红黑树的遍历。
下面我将分别介绍中序遍历、前序遍历和后序遍历的算法。
1. 中序遍历算法:中序遍历的顺序是先遍历左子树,然后访问根节点,最后遍历右子树。
在C语言中,我们可以使用递归实现中序遍历:c.void inOrderTraversal(struct Node root) {。
if (root != NULL) {。
inOrderTraversal(root->left);printf("%d ", root->data);inOrderTraversal(root->right);}。
}。
2. 前序遍历算法:前序遍历的顺序是先访问根节点,然后遍历左子树,最后遍历右子树。
同样可以使用递归实现前序遍历:c.void preOrderTraversal(struct Node root) {。
if (root != NULL) {。
printf("%d ", root->data);preOrderTraversal(root->left);preOrderTraversal(root->right);}。
}。
3. 后序遍历算法:后序遍历的顺序是先遍历左子树,然后遍历右子树,最后访问根节点。
同样可以使用递归实现后序遍历:c.void postOrderTraversal(struct Node root) {。
if (root != NULL) {。
postOrderTraversal(root->left);postOrderTraversal(root->right);printf("%d ", root->data);}。
}。
另外,以上是使用递归实现的遍历算法,也可以使用非递归的方式来实现,利用栈来辅助实现遍历。
教你透彻了解红黑树概论

红黑树系列,六篇文章于今日已经完成:1、教你透彻了解红黑树2、红黑树算法的实现与剖析3、红黑树的c源码实现与剖析4、一步一图一代码,R-B Tree5、红黑树插入和删除结点的全程演示6、红黑树的c++完整实现源码------------------------------一、红黑树的介绍先来看下算法导论对R-B Tree的介绍:红黑树,一种二叉查找树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red 或Black。
通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的。
前面说了,红黑树,是一种二叉查找树,既然是二叉查找树,那么它必满足二叉查找树的一般性质。
下面,在具体介绍红黑树之前,咱们先来了解下二叉查找树的一般性质:1.在一棵二叉查找树上,执行查找、插入、删除等操作,的时间复杂度为O(lgn)。
因为,一棵由n个结点,随机构造的二叉查找树的高度为lgn,所以顺理成章,一般操作的执行时间为O(lgn)。
//至于n个结点的二叉树高度为lgn的证明,可参考算法导论第12章二叉查找树第12.4节。
2.但若是一棵具有n个结点的线性链,则此些操作最坏情况运行时间为O(n)。
而红黑树,能保证在最坏情况下,基本的动态几何操作的时间均为O(lgn)。
ok,我们知道,红黑树上每个结点内含五个域,color,key,left,right,p。
如果相应的指针域没有,则设为NIL。
一般的,红黑树,满足以下性质,即只有满足以下全部性质的树,我们才称之为红黑树:1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对于任一结点而言,其到叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点。
(注:上述第3、5点性质中所说的NULL结点,包括wikipedia.算法导论上所认为的叶子结点即为树尾端的NIL指针,或者说NULL结点。
红黑树(二)之C语言的实现

红⿊树(⼆)之C语⾔的实现概要红⿊树在⽇常的使⽤中⽐较常⽤,例如Java的和,C++的STL,以及Linux内核中都有⽤到。
之前写过⼀篇⽂章专门介绍红⿊树的,本⽂将给出红⿊数的C语⾔的实现代码,后序章节再分别给出C++和Java版本的实现。
还是那句话,三种实现原理相同,择其⼀了解即可;若⽂章有错误或不⾜的地⽅,望不吝指出!⽬录转载请注明出处:更多内容:(01)(02)(03) (04) (05) (06)红⿊树的介绍红⿊树(Red-Black Tree,简称R-B Tree),它⼀种特殊的⼆叉查找树。
红⿊树是特殊的⼆叉查找树,意味着它满⾜⼆叉查找树的特征:任意⼀个节点所包含的键值,⼤于等于左孩⼦的键值,⼩于等于右孩⼦的键值。
除了具备该特性之外,红⿊树还包括许多额外的信息。
红⿊树的每个节点上都有存储位表⽰节点的颜⾊,颜⾊是红(Red)或⿊(Black)。
红⿊树的特性:(1) 每个节点或者是⿊⾊,或者是红⾊。
(2) 根节点是⿊⾊。
(3) 每个叶⼦节点是⿊⾊。
[注意:这⾥叶⼦节点,是指为空的叶⼦节点!](4) 如果⼀个节点是红⾊的,则它的⼦节点必须是⿊⾊的。
(5) 从⼀个节点到该节点的⼦孙节点的所有路径上包含相同数⽬的⿊节点。
关于它的特性,需要注意的是:第⼀,特性(3)中的叶⼦节点,是只为空(NIL或null)的节点。
第⼆,特性(5),确保没有⼀条路径会⽐其他路径长出俩倍。
因⽽,红⿊树是相对是接近平衡的⼆叉树。
红⿊树⽰意图如下:红⿊树的C实现(代码说明)红⿊树的基本操作是添加、删除和旋转。
在对红⿊树进⾏添加或删除后,会⽤到旋转⽅法。
为什么呢?道理很简单,添加或删除红⿊树中的节点之后,红⿊树就发⽣了变化,可能不满⾜红⿊树的5条性质,也就不再是⼀颗红⿊树了,⽽是⼀颗普通的树。
⽽通过旋转,可以使这颗树重新成为红⿊树。
简单点说,旋转的⽬的是让树保持红⿊树的特性。
旋转包括两种:左旋和右旋。
下⾯分别对旋转(左旋和右旋)、添加、删除进⾏介绍。
红黑树算法原理与实现

红黑树算法原理与实现红黑树是一种自平衡二叉查找树,它能够保证查找、插入和删除操作最坏情况下的时间复杂度都为O(log n),是一种十分高效的数据结构。
本文将对红黑树算法的原理和实现进行介绍,帮助读者深入了解红黑树的运作流程和应用场景。
一、红黑树的定义和性质红黑树和其他的自平衡二叉查找树(如AVL树)一样,能够自动调整树的结构以保证平衡,并且能够保持树中每个节点的黑高相等。
下面是红黑树的具体定义:(1)每个节点要么是红色,要么是黑色。
(2)根节点是黑色。
(3)每个叶子节点(NIL节点,空节点)都是黑色的。
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)任意一个节点到每个叶子节点所经过的黑色节点数都相同。
这些性质确保了红黑树的平衡,在插入和删除节点时能够自动平衡而不需要人工干预,从而保证了树的性能。
二、红黑树的基本操作红黑树的插入和删除操作是它最为关键和难点的部分,下面我们将针对这两个操作进行介绍。
(1)插入操作插入节点时,首先按照二叉查找树的方式将新节点插入到树中。
接下来需要进行的操作是将节点的颜色进行调整,保证该节点符合红黑树的五大性质。
如果插入的节点不满足性质(4),需要执行进行一系列颜色调整和旋转操作,使得节点重新满足红黑树性质。
一般情况下,情况分为两种:(a)情况1:插入的节点为根节点此时只需要将节点的颜色设置为黑色即可。
(b)情况2:插入的节点的父节点为红色此时需要进行一系列的旋转和颜色调整操作。
可以分为以下三种情况:①插入节点的叔叔节点是红色的,此时需要进行颜色调整和旋转。
②插入节点的叔叔节点是黑色的,并且插入节点为父节点的右子节点,此时需要进行左旋操作。
③插入节点的叔叔节点是黑色的,并且插入节点为父节点的左子节点,此时需要进行颜色调整、右旋操作。
(2)删除操作删除节点时,首先按照二叉查找树的方式删除节点。
接着需要对树进行自平衡操作,使之重新满足红黑树性质。
与插入操作相比,删除操作需要考虑更多的情况。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
//by svking//2012.5#include <stdio.h>#include <stdlib.h>#include <time.h>#define MAXSIZE 1000typedef int ElemType;#define RED 0#define BLACK 1typedef struct RBTNode{char color;ElemType data;struct RBTNode * p;struct RBTNode * left;struct RBTNode * right;}RBTNode, * PRBTNode;typedef struct RBTree{PRBTNode root;PRBTNode nil; //统一的空节点,该节点是黑的}RBTree, * PRBTree;int leftRotate (PRBTree tree, PRBTNode t);int rightRotate (PRBTree tree, PRBTNode t);PRBTNode insertRB (PRBTree tree, ElemType d);int insertRB_fixup (PRBTree tree, PRBTNode t);int createRBTree (PRBTree tree, ElemType d[], int n); int initRB (PRBTree tree);PRBTNode maximum (PRBTree tree, PRBTNode t); PRBTNode minimum (PRBTree tree, PRBTNode t); PRBTNode next (PRBTree tree, PRBTNode t);PRBTNode precursor (PRBTree tree, PRBTNode t);int walkNext (PRBTree tree);int inOrderWalk (PRBTree tree, PRBTNode t);int deleteRB_fixup (PRBTree tree, PRBTNode c); PRBTNode deleteRB (PRBTree tree, PRBTNode t);int main (){PRBTNode p;int d[MAXSIZE];int n = 0;int i;RBTree tree;initRB(&tree);/*insertRB(&tree, 11);insertRB(&tree, 2);insertRB(&tree, 14);insertRB(&tree, 1);insertRB(&tree, 7);insertRB(&tree, 15);insertRB(&tree, 5);insertRB(&tree, 8);insertRB(&tree, 4);*/p= insertRB(&tree, 26);insertRB(&tree, 17);insertRB(&tree, 41);insertRB(&tree, 14);insertRB(&tree, 21);insertRB(&tree, 30);insertRB(&tree, 47);insertRB(&tree, 10);insertRB(&tree, 16);insertRB(&tree, 19);insertRB(&tree, 23);insertRB(&tree, 28);insertRB(&tree, 38);insertRB(&tree, 7);insertRB(&tree, 12);insertRB(&tree, 15);insertRB(&tree, 20);insertRB(&tree, 3);insertRB(&tree, 35);insertRB(&tree, 39);srand(time(NULL));/*puts("请输入数据的个数:");scanf("%d",&n);printf("随机生成的%d个数据是:\n",n); for (i = 0; i < n; i++){d[i] = rand()%1000;printf("%d ",d[i]);}puts("");puts("建树开始");createRBTree(&tree, d, n);*/inOrderWalk(&tree,tree.root);puts("");printf("根是%d \n",tree.root->data);printf("删除%d后:",p->data);deleteRB(&tree, p);inOrderWalk(&tree,tree.root);puts("");printf("根是%d \n",tree.root->data);return0;}PRBTNode insertRB (PRBTree tree, ElemType d){//插入元素//!!!记得插入的元素的初始化,p指向为父母节点,left和right赋值为NULL。
PRBTNode t = NULL;PRBTNode p = NULL;int flag = 0; //用来表示插入在左边的树还是右边的树t = tree->root;//插入的节点是root,并做相应的初始化if (tree->root == tree->nil){tree->root = (PRBTNode)malloc(sizeof(RBTNode));tree->root->data = d;tree->root->color = BLACK;tree->root->p = tree->root->left =tree->root->right = tree->nil;return tree->root;}while (t != tree->nil){p = t;if (d < t->data){flag = 0;t = t->left;}else{if (d > t->data){flag = 1;t = t->right;}else{if ( (flag=rand()%2) == 0)t = t->left;elset = t->right;}}}//while//将t指向带插入节点的地址,并初始化t = (PRBTNode)malloc(sizeof(RBTNode));t->data = d;t->color = RED;t->p = p;t->left = t->right = tree->nil;if (!flag)p->left = t;elsep->right = t;insertRB_fixup(tree, t);return t;}int insertRB_fixup (PRBTree tree, PRBTNode t){//插入的节点可能破坏红黑树的性质。
该函数检测插入的节点是否破坏了红黑树的性质。
如果破坏了,就对树进行调整,使其满足红黑树的性质while (t->p->color == RED) //只有插入节点的父亲是红色的才会破坏红黑树的性质(4.如果一个结点是红的,那么它的俩个儿子都是黑的){if (t->p->p->left == t->p) //插入节点的父节点本身是left{if (t->p->p->right->color == RED) //case 1{t = t->p->p;t->left->color = t->right->color = BLACK;t->color = RED;}else{if (t->p->right == t) //case 2{//将case 2转换为了case 3t = t->p; //这步赋值是为了在转换为case 3时,t指向的是下面的红节点,和case 3的情况相一致leftRotate(tree, t);}//case 3t->p->color = BLACK;t->p->p->color = RED;rightRotate(tree, t->p->p);}}//ifelse//插入节点的父节点本身是right{if (t->p->p->left->color == RED) //case 1{t = t->p->p;t->left->color = t->right->color = BLACK;t->color = RED;}else{if (t->p->left == t) //case 2{//将case 2转换为了case 3t = t->p; //这步赋值是为了在转换为case 3时,t指向的是下面的红节点,和case 3的情况相一致rightRotate(tree, t);}//case 3t->p->color = BLACK;t->p->p->color = RED;leftRotate(tree, t->p->p);}}//else}//whiletree->root->color = BLACK;return0;}int leftRotate (PRBTree tree, PRBTNode t){PRBTNode c; //左旋,c指向t的rightc = t->right;if (t->right == tree->nil) //左旋,t的right不能为空return1;//这个if-else用于将t的父亲节点的left或right点指向c,如果t的父节点为不存在,则树的root指向cif (t->p != tree->nil) //判断t是否为root{if (t->p->left == t) //判断t是t的父节点的left还是rightt->p->left = c;elset->p->right = c;}elsetree->root = c;c->p = t->p; //更新c的父节点t->right = c->left;if (c->left != tree->nil)c->left->p = t;c->left = t;t->p = c;return0;}int rightRotate (PRBTree tree, PRBTNode t){PRBTNode c; //右旋,c指向t的leftc = t->left;if (t->left == tree->nil) //右旋,t的left不能为空return1;//这个if-else用于将t的父亲节点的left或right点指向c,如果t的父节点为不存在,则树的root指向cif (t->p != tree->nil) //判断t是否为root{if (t->p->left == t) //判断t是t的父节点的left还是rightt->p->left = c;elset->p->right = c;}elsetree->root = c;c->p = t->p; //更新c的父节点t->left = c->right;if (c->right != tree->nil)c->right->p = t;c->right = t;t->p = c;return0;}int createRBTree (PRBTree tree, ElemType d[], int n) {//用元素的插入建树int index = -1;int tmp = -1;srand(time(NULL));while (n--){index =(int) rand()%(n+1);//此时共有n+1个数据tmp = d[index];d[index] = d[n];d[n] = tmp;insertRB(tree, d[n]);printf("插入%d\t",d[n]);}puts("");return0;}//createRBTreeint initRB (PRBTree tree){//红黑树的初始化if (tree == NULL)return0;tree->nil = (PRBTNode)malloc(sizeof(RBTNode));tree->nil->color = BLACK;tree->root = tree->nil;return0;}//initRBPRBTNode minimum (PRBTree tree, PRBTNode t){//返回最小值,如果t是NULL返回NULLif (t == tree->nil)return NULL;while (t->left != tree->nil)t = t->left;return t;}//minimumPRBTNode maximum (PRBTree tree, PRBTNode t){//返回最大值,如果t是NULL返回NULLif (t == tree->nil)return NULL;while (t->right != tree->nil)t = t->right;return t;}//maximumPRBTNode next (PRBTree tree, PRBTNode t){//给出t的后继的节点。