红黑树操作

红黑树操作

实验目的

通过插入操作建立一个红黑树,输入为1…n的数,同时将每次插入后的树信息打印出来。

 

实验原理

红黑树是一棵二叉搜索树,它在每个结点上增加了一个存储位来表示结点的颜色,可以是RED或BLACK。通过对任何一条从根到叶子的简单路径上各个结点的颜色约束,红黑树确保没有一条路径比其他路径长出2倍,因而是近似于平衡的。

树中每个结点有五个属性:color,key,left,right和p;通过添加哨兵(T_nil)对红黑树的边界进行限定。

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

(1)每个结点或是红色,或是黑色的;

(2)根节点是黑色的;

(3)每个叶节点(NIL)是黑色的;

(4)如果一个结点是红色的,则它的两个子节点都是黑色的;

(5)对每个结点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色结点;

 

实验过程

旋转操作:左旋和右旋

LEFT-ROTATE(T, x)

1 y = x.right

2 x.right = y.left

3 if y.left !=T.nil

4     y.left.p = x

5 y.p = x.p

6 if x.p == T.nil

7     T.root = y

8 elseif x ==x.p.left

9     x.p.left = y

10 else x.p.right= y

11 y.left = x

12 x.p = y

 

RIGHT-ROTATE(T, x)

1 y = x.left

2 x.left = y.right

3 if y.right !=T.nil

4     y.right.p = x

5 y.p = x.p

6 if x.p == T.nil

7     T.root = y

8 elseif x ==x.p.right

9     x.p.right = y

10 else x.p.left =y

11 y.right = x

12 x.p = y

 

插入操作:插入一个新节点就像在二叉树中插入一个结点一样,但是由于红黑树多了一个颜色属性,因此初始插入的结点的颜色属性为RED。选择将插入结点的颜色属性初始为RED的原因:在红黑树中插入一个结点时,如果初始插入结点的颜色为BLACK时,那么将会破坏性质(5),而且在修改性质(5)的过程中,需要去遍历每条路径,显然这是低效率的;如果将颜色初始为RED时,那么将会破坏性质(2)和性质(4),性质(2)修改很简单,性质(4)的修改过程只需要考虑叔叔结点的颜色,就可以逐步实现;显然在通过将插入结点初始为RED是更高效的操作。

RB-INSERT(T, z)

1 y = T.nil

2 x = T.root

3 while x != T.nil

4     y = x

5     if z.key < x.key

6     x = x.left

7     else x = x.right

8  z.p = y

9  if y == T.nil

10     T.root = z

11 elseif z.key< y.key

12     y.left = z

13 else y.right =z

14 z.left = T.nil

15 z.right = T.nil

16 z.color = RED

17RB-INSERT-FIXUP(T, z)

 

RB-INSERT-FIXUP(T,z)

1 while z.p.color== RED

2     if z.p == z.p.p.left

3         y = z.p.p.right

4         if y.color == RED

5             z.p.color = BLACK

6             y.color = BLACK

7             z.p.p.color = RED

8             z = z.p.p

9         else if z == z.p.right

10              z = z.p     

11              LEFT-ROTATE(T, z)

12        else

13            z.p.color = BLACK

14            z.p.p.color = RED

15            RIGHT-ROTATE(T, z.p.p)

16      else(same as then clause with “right” and“left” exchanged)

17     T.root.color = BLACK

 

实验总结

(一)在实验过程中,输入是通过调用一个struct node * creat_node(tree_pointerT, int key)来实现的,但是这种方法存在一个问题:使用malloc分配内存时,在最后没有进行free处理,容易造成内存泄漏;

(二)关于树的打印,实验主要关注的是红黑树算法代码的实现,所以没有过于的去关注数的可视性。说明一下如何理解打印下来的树,右端最突出的是根节点,下端是左子树,上端是右子树;,

(三)在整个实验过程中,由于自己基础较差,可能附录的代码中有多处注释,想了想还是没有删掉,也是一种实现算法的过程,以及如何去调试程序吧。

 

附录(代码)

#include <stdio.h>

#include <stdlib.h>

 

typedef  structnode *tree_pointer;

typedef struct node {

    int  key;

    char color;       //R represents red; B represents black

    tree_pointerp;

    tree_pointerleft;

    tree_pointerright;

} *tree_pointer;

 

void RB_insert(tree_pointer T_nil, tree_pointerT_root, tree_pointer z);

void left_rotate(tree_pointer T_nil, tree_pointerT_root, tree_pointer x);

void right_rotate(tree_pointer T_nil, tree_pointerT_root, tree_pointer x);

void RB_insert_fixup(tree_pointer T_nil, tree_pointerT_root, tree_pointer z);

void print_tree(tree_pointer T_root, int n);

 

 

int get_tree_height(tree_pointer T_root);

 

struct node * creat_node(tree_pointer T, int key);

 

 

int main(){

    inti,num,key,tree_height;

 

    tree_pointernew_node = NULL;

    tree_pointerT_root   = NULL;

    tree_pointerT_nil    = NULL;

    T_nil =(struct node *)malloc(sizeof(struct node));

    T_nil->color= 'B';

   

    T_root =(struct node *)malloc(sizeof(struct node));

//  T_root->key   = 11;

    T_root->color= 'B';

    T_root->p     = T_nil;

    T_root->left  = T_nil;

    T_root->right= T_nil;

 

    printf("T_nil= %p; key = %d; color = %c; p = %p; left = %p; right = %p\n",T_nil,T_nil->key, T_nil->color, T_nil->p, T_nil->left, T_nil->right);

    printf("T_root= %p; key = %d; color = %c; p = %p; left = %p; right = %p\n",T_root,T_root->key, T_root->color, T_root->p, T_root->left,T_root->right);

   

    printf("pleaseinput the number of nodes:");

    scanf("%d",&num);

    printf("pleaseinput the key:");

    scanf("%d",&key);

    T_root->key= key;

    printf("\n");

    tree_height =get_tree_height(T_root);

    print_tree(T_root,tree_height);

 

    printf("\n");

    for(i = 0; i< num-1; i++){

       printf("pleaseinput the key:");

       scanf("%d",&key);

       printf("\n");

       new_node =creat_node(T_nil, key);

       RB_insert(T_nil,T_root, new_node);

//     tree_height= get_tree_height(T_root);

//     print_tree(T_root,tree_height);

//     printf("\n666666666666666666666666\n");

    }

 

   

 

/*

    tree_pointernew1 = NULL;

    tree_pointernew2 = NULL;

    tree_pointernew3 = NULL;

    tree_pointernew4 = NULL;

    tree_pointernew5 = NULL;

//  T_root =creat_node(T_nil, 11);

    new1 =creat_node(T_nil, 2);

    new2 =creat_node(T_nil, 14);

    new3 =creat_node(T_nil, 1);

    new4 =creat_node(T_nil, 7);

    new5 =creat_node(T_nil, 15);

   

 

   

    RB_insert(T_nil,T_root, new1);

    RB_insert(T_nil,T_root, new2);

    RB_insert(T_nil,T_root, new3);

    RB_insert(T_nil,T_root, new4);

    RB_insert(T_nil,T_root, new5);

 

    printf("%p,  %p   %p",new1->p,new1->left,new1->right);

    new1 = new2;

 

    printf("\n1111111111111111111111111\n");

    printf("new1= %p; key = %d; color = %c;",new1,new1->key,new1->color);

    printf("p=%p; left = %p; right = %p\n",new1->p,new1->left,new1->right);

 

    printf("new2= %p; key = %d; color = %c; p = %p; left = %p; right = %p\n",new2,new2->key, new2->color, new2->p, new2->left, new2->right);

*/

//  tree_height =get_tree_height(T_root);

//  print_tree(T_root,tree_height);

   

}

 

struct node * creat_node(tree_pointer T, int key){

    tree_pointernew = NULL;

    new = (structnode *)malloc(sizeof(struct node));

    new->key   = key;

    new->color= 'R';

    new->p     = T;

    new->left  = T;

    new->right= T;

    return new;

}

 

void RB_insert(tree_pointer T_nil, tree_pointerT_root, tree_pointer z){

    tree_pointerx = NULL;

    tree_pointery = NULL;

    y = T_nil;

    x = T_root;

 

//  printf("\nnowRB_insert is running!\n");

 

    while(x !=T_nil){

       y = x;

       if(z->key< x->key)

           x =x->left;

       else

           x =x->right;

    }

 

    z->p = y;

    if(y ==T_nil)

       T_root   = z;

    elseif(z->key < y->key)

       y->left  = z;

    else

       y->right= z;

   

    z->left  = T_nil;

    z->right =T_nil;

    z->color ='R';

 

   

 

    RB_insert_fixup(T_nil,T_root, z);

 

//  printf("\nnowRB_insert is over!\n");

}

 

void RB_insert_fixup(tree_pointer T_nil, tree_pointerT_root, tree_pointer z){

    tree_pointery = NULL;

 

//  printf("\nnowRB_insert_fixup is running!\n");

   

    while(z->p->color== 'R'){

       if(z->p== z->p->p->left){

           y =z->p->p->right;

          

           if(y->color== 'R'){

              z->p->color    = 'B';

              y->color       = 'B';

              z->p->p->color= 'R';

              z =z->p->p;

           }

           elseif(z == z->p->right){

              z =z->p;

              left_rotate(T_nil,T_root, z);

           }

           else{

              z->p->color    = 'B';

              z->p->p->color= 'R';

              right_rotate(T_nil,T_root, z->p->p);

             

           }

       }

       //////////////////////////////////

       elseif(z->p = z->p->p->right){

           y =z->p->p->left;

 

           if(y->color== 'R'){

              z->p->color    = 'B';

              y->color       = 'B';

              z->p->p->color= 'R';

              z =z->p->p;

           }

           elseif(z == z->p->left){

              z =z->p;

              right_rotate(T_nil,T_root, z);

           }

           else{

              z->p->color    = 'B';

              z->p->p->color= 'R';

              left_rotate(T_nil,T_root, z->p->p);

           }

       }

       T_root->color= 'B';

    }

 

//  printf("\nnowRB_insert_fixup is over!\n");

    inttree_height;

    if(T_root->p== T_nil){

       tree_height= get_tree_height(T_root);

       print_tree(T_root,tree_height);

    }

    else{

       tree_height= get_tree_height(T_root);

       print_tree(T_root->p,tree_height);

    }

}

 

 

void left_rotate(tree_pointer T_nil, tree_pointerT_root, tree_pointer x){

    tree_pointery = NULL;     

    y =x->right;

   

    x->right =y->left;

    if(y->left!= T_nil)

       y->left->p= x;

 

    y->p =x->p;

    if(x->p ==T_nil)

       T_root =y;

    else if(x ==x->p->left)

       x->p->left= y;

    else

       x->p->right= y;

   

    y->left =x;

    x->p    = y;

}

 

void right_rotate(tree_pointer T_nil, tree_pointerT_root, tree_pointer x){

    tree_pointery = NULL;

    y =x->left;

 

    x->left =y->right;

    if(y->right!= T_nil)

       y->right->p= x;

   

    y->p =x->p;

    if(x->p ==T_nil)

       T_root =y;

    else if(x ==x->p->right)

       x->p->right= y;

    else

       x->p->left= y;

   

    y->right =x;

    x->p = y;

}

 

int get_tree_height(tree_pointer T_root){

    if(!T_root)

       return 0;

    intleft_height,right_height;

    left_height  = get_tree_height(T_root->left);

    right_height= get_tree_height(T_root->right);

    return(left_height < right_height)?(right_height+1):(left_height+1);

}

 

void print_tree(tree_pointer T_root, int n){

    int i;

    if(T_root ==NULL)

       return;

    print_tree(T_root->right, n-1);

 

    for(i = 0; i< n-1; i++)

       printf("   ");

    if(n > 0){

       printf("---");

       printf("%d(%c)\n",T_root->key,T_root->color);

    }

   

    print_tree(T_root->left,n-1);

}

 

 

 

 

 

<think>首先,用户的问题是:“我想理解红黑树结构中哨兵节点的作用及实现原理请问红黑树中统一的哨兵节点是什么作用,为什么所有叶子节点都指向它” 根据系统级指令:-所有行内数学表达式必须使用$...$格式。-独立公式必须使用$$...$$格式并单独成段。 -LaTeX语法正确。-使用中文回答。-生成相关问题。 -在回答中引用的段落末尾自然地添加引用标识,例如[^1]。用户可见层:-回答结构清晰,帮助用户逐步解决问题。 -尽量保证回答真实可靠。 参考站内引用:-引用[1]、[2]、[3]、[4]、[5]提供了关于红黑树的信息,特别是哨兵节点的作用。关键点从引用中提取: -引用[1]:提到节点只能是黑或红,根节点与叶子节点必须是黑色(叶子节点指nullptr)。 -引用[2]:每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。 -引用[3]:每个叶节点(nil)是黑色的。-引用[4]:叶子节点为黑色。-引用[5]:在实现中,有一个哨兵节点NIL,用于表示叶子节点。用户的问题焦点是哨兵节点的作用和为什么所有叶子节点都指向它。 结构化回答:1.**介绍哨兵节点**:解释什么是哨兵节点。2.**作用**:详细说明哨兵节点的作用。 3. **实现原理**:描述如何实现。4.**为什么所有叶子节点都指向它**:解释原因。 5. **添加引用标识**:在相关部分末尾添加引用。 生成相关问题:在回答末尾,添加相关问题。 使用中文回答。 步骤: -开头简要介绍红黑树和哨兵节点。 -然后逐步解释。-确保使用正确的LaTeX格式(如果有数学表达式)。-结尾生成相关问题。内容大纲: 1. **什么是哨兵节点?**:在红黑树中,哨兵节点(通常称为NIL节点)是一个特殊的节点,用于表示所有叶子节点。它不是存储实际数据的节点,而是作为一个占位符。-引用[2]和[3]提到叶结点是NIL指针或NULL结点。2. **作用**:- **简化实现**:避免处理空指针,统一处理边界条件。- **维护红黑树性质**:确保所有叶子节点都是黑色的,满足红黑树的性质(如引用[1]、[2]、[3]所述)。-**减少条件检查**:在插入和删除操作中,减少对空指针的特殊处理。-引用[5]中显示实现中使用哨兵节点。3.**为什么所有叶子节点都指向它?**-在红黑树中,每个叶子节点(即没有子节点的节点)都指向同一个哨兵节点NIL。这是因为:-统一性:通过一个共享的NIL节点,确保所有叶子节点在逻辑上一致。-节省空间:避免为每个叶子节点创建单独的NIL节点,节省内存。-简化操作:在遍历和平衡操作中,可以统一处理NIL节点。-引用[3]提到“每个叶节点(nil)是黑色的”,并指向同一个节点。 4. **实现原理**:-在代码中,定义一个哨兵节点实例,通常是黑色(如引用[5]所示)。-所有叶子节点的左子节点和右子节点都指向这个NIL节点。-在插入和删除时,新节点初始化为指向NIL。 5. **总结**:重申重要性。 添加引用:-在解释作用时,引用[2]、[3]。-在实现时,引用[5]。 生成相关问题:-例如:1.红黑树中哨兵节点如何影响插入操作?2.不使用哨兵节点会有什么问题?3.红黑树的其他性质如何通过哨兵节点维护? 确保回答清晰、真实可靠。</think>### 红黑树中哨兵节点的作用及实现原理 红黑树是一种自平衡二叉搜索树,其核心特性通过严格的规则(如节点颜色、路径平衡)确保操作高效性。在实现中,哨兵节点(也称为NIL节点)是一个关键设计,用于统一表示所有叶子节点。下面我将逐步解释其作用和实现原理,确保回答基于可靠的技术细节。 #### 1. **什么是哨兵节点?** - 哨兵节点是一个特殊的“虚拟”节点,不存储实际数据,仅作为红黑树中所有叶子节点的统一占位符。在代码实现中,它通常被定义为一个共享的、颜色为黑色的单例对象(如`NIL`)。例如: - 所有实际叶子节点(即无子节点的节点)的左子指针和右子指针都指向这个唯一的哨兵节点。 - 根节点的父指针也可能指向哨兵节点(当树为空时)。 - 这避免了直接使用`nullptr`或`NULL`,简化了边界处理[^2][^3]。 #### 2. **哨兵节点的作用** - **维护红黑树性质**:红黑树有五大规则(如根节点为黑、不能有连续红节点、每条路径黑色节点数相同)。哨兵节点确保所有叶子节点“逻辑上”为黑色,满足性质: - 规则要求“每个叶结点必须是黑色的”(引用[2]),哨兵节点本身定义为黑色,统一了叶子节点的颜色属性。 - 例如,在插入新节点时,新节点的子节点自动指向哨兵节点,避免了颜色冲突。 - **简化算法实现**: - 在插入或删除操作中,哨兵节点作为边界哨兵,减少了对空指针的额外检查。例如,旋转或颜色调整时,无需特殊处理`null`,直接操作哨兵节点即可。 - 这提高了代码健壮性,降低了错误概率(如空指针解引用)。 - **节省内存和提升性能**: - 所有叶子节点共享同一个哨兵节点实例,避免了为每个叶子创建独立对象,节省内存开销。 - 在遍历(如中序遍历)时,哨兵节点作为终止标志,统一了递归或迭代的逻辑。 - 这些作用确保了红黑树的自平衡效率,尤其在动态数据场景(如C++ STL的`map`)中至关重要[^4][^5]。 #### 3. **为什么所有叶子节点都指向同一个哨兵节点?** - **统一性与一致性**:红黑树的性质依赖于路径上黑色节点的数量统计。如果每个叶子节点使用独立的`nullptr`,会导致: - 路径长度计算不一致(`nullptr`无法携带颜色信息)。 - 在插入/删除时需额外逻辑处理边界,破坏规则(如引用[3]所述“对每个节点,从该节点到其后代叶节点的简单路径上,均包含相同数目的黑色节点”)。 - **实现便捷性**:通过共享哨兵节点: - 所有叶子节点在逻辑上等价,简化了性质验证(例如,计算黑色高度时,哨兵节点直接贡献一个“黑色计数”)。 - 在代码中,初始化时只需创建一个哨兵实例(如`NIL`),并在构造新节点时将其子指针指向它。 - **避免错误**:直接使用`nullptr`可能导致: - 颜色规则失效(`nullptr`无法着色)。 - 旋转操作中边界条件复杂化,易引发崩溃或失衡。 - 因此,统一指向哨兵节点是平衡性能与正确性的最优设计[^1][^5]。 #### 4. **实现原理** - **定义哨兵节点**:在代码中,哨兵节点通常作为类的静态成员或全局常量,颜色固定为黑色,无数据字段。例如Java实现: ```java class RedBlackNode<T> { T data; RedBlackNode<T> left, right, parent; Color color; // 枚举: RED 或 BLACK } class RedBlackTree<T extends Comparable<T>> { private RedBlackNode<T> root; private final RedBlackNode<T> NIL = new RedBlackNode<>(null); // 哨兵节点 public RedBlackTree() { NIL.color = Color.BLACK; // 设置为黑色 root = NIL; // 空树时根指向哨兵 } } ``` - 这里`NIL`是共享的,所有新节点的`left`和`right`初始化为`NIL`[^5]。 - **操作中的使用**: - **插入**:新节点插入时,其子节点默认为`NIL`。调整平衡(旋转或变色)时,若遇到`NIL`,直接视为黑色节点处理。 - **删除**:当节点被删除后,其位置由`NIL`替代,确保树结构完整。 - **遍历**:递归终止条件为节点等于`NIL`。 - 这种设计严格遵循红黑树性质,并通过哨兵节点隐含了“叶子均为黑”的约束[^4]。 #### 总结 哨兵节点在红黑树中充当“统一边界卫士”,通过共享实例确保所有叶子节点逻辑一致(黑色),简化了算法实现并维护了平衡性。其核心价值在于消除`nullptr`的不确定性,使插入、删除等操作高效可靠。在工程实践中(如C++ STL或Java TreeMap),哨兵节点是红黑树稳定性的基石[^4][^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值