【零声】红黑树C/C++

注意:红黑树的使用的时候只有这两种情况:

  1. 作key,value --> 通过key查找value
  2. 利用中序遍历,是顺序的

一、红黑树用在哪里?

1.STL中的map???
map是一种数据结构,不能说成是红黑树的用途,只能说是红黑树的封装
2.nginx???
3.定时器???
4.进程调度的cfs
5.内存管理
红黑树在海量数据查询才有优势

二、红黑树的性质

  1. 每个结点是红的或者黑的–>红、黑代表的是逻辑颜色,也可用0,1或黑、白表示
  2. 根结点是黑的
  3. 每个叶子结点是黑的
  4. 如果一个结点是红的,则它的两个儿子都是黑的–>两个红节点不能相邻
  5. 对每个结点,从该结点到其子孙结点的所有路径上的包含相同数目的黑结点–>红黑树的平衡性,不是平衡所有结点,而是黑高

image.png

只有2是红黑树,1、3黑高不同,4相邻结点为红色
这些约束强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长(左右子树相差最大为2n-1倍)。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例 O(logn),这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,和 AVL 树通过约束左右子树高度不同,红黑树是通过它的四条性质来实现 “平衡状态”,在插入结点或者删除结点时,可能会造成某个结点违反了上述的某条性质,那么红黑树的做法就是通过 “重新着色” 和 “旋转” 两种方式使之重新符合性质。

为什么叫红黑树,为什么两种颜色?

红黑代表的是两种逻辑状态,目的是辨认黑高,每一个分支拥有一样的黑高,实现一种平衡

三、红黑树的实现

3.1结点的实现

typedef int KEY_TYPE;	//KEY_TYPE没有写死,留一个接口方便日后修改
                    	//无论是double还是数据结构作key都可以

typedef struct _rbtree_node {
    int color; 
    struct _rbtree_node* left;	//记得加struct
    struct _rbtree_node* right;
    struct _rbtree_node* parent;	//parent主要用于旋转、调整 
    
    KEY_TYPE key;	
    void* data;	//void* 表示可以接受任何类型的数据
    
    } rbtree_node, *prbtree_node;
#include <iodstream>
using namespace std;
using KEY_TYPE = int;	//等价于typedef int KEY_TYPE;

enum {red = 0, black = 1};

struct Node{
    int color;
	Node* left;
	Node* right;
	Node* parent;

	KEY_TYPE key;
	void* data;
}

3.2红黑树的定义

typedef struct _rbtree {
    prbtree_node root;
    prbtree_node nil;	//定义一个通用的叶子结点,方便判断是不是叶子结点
} rbtree, *prbtree;

3.3红黑树的旋转

注意:

  1. 旋转不是红黑树特有的,只要是二叉树都有
  2. 左旋、右旋和颜色没关系,是为了作结点而进行的旋转

旋转的目的:把旋转做成原语(最基本的,最独立的)操作目的是为了更好的实现红黑树,即左旋、右旋是实现红黑树最基础的原语操作
无论左旋还是右旋,只需动3个方向,六根指针动完就🆗

3.3.1左旋

image.png

  1. y为x的右子树
  2. x的右子树指向y的左子树b
    1. 如果y的左子树b不是叶子,则y的左子树的parent指向x
  3. y->parent指向x的parent
    1. 如果x->key比x->parent->key小,x是x->parent的左子树,则x->parent->left指向y
    2. 否则x->key比x->parent->key大,x是x->parent的右子树,则x->parent->right指向y
  4. x->parent指向y
    1. y的左子树指向x
//旋转:三个方向,6根指针
void _left_rotate(prbtree T,prbtree_node x) {
    prbtree_node y = x->right;

    x->right = y->left;
    if(y->left != T->nil) {
        y->left->parent = x;	//parent = x而不是x->right
    }

    y->parent = x->parent;
    if(x->parent == T->root) {	//所谓根节点是指有个根节点指针指向它而且它的parent也指向根节点,
        //所以此处判断的也是x->parent而不是x
        T->root = y;
    }
    else if(x == x->parent->left) {	//这里判断是x而不是x->parent
        x->parent->left = y;
    }
    else {
        x->parent->right = y;
    }

    y->left = x;
    x->parent = y;
}


3.3.1右旋

image.png
把左旋的代码复制过来,然后left改成right,x改成y,再检查一遍逻辑

//旋转:三个方向,6根指针
void _right_rotate(prbtree T,prbtree_node y) {
    prbtree_node x = y->left;

    y->left = x->right;
    if(x->right != T->nil) {
        x->right->parent = y;	//parent = x而不是x->right
    }

    x->parent = y->parent;
    if(y->parent == T->root) {	//所谓根节点是指有个根节点指针指向它而且它的parent也指向根节点,
        //所以此处判断的也是x->parent而不是x
        T->root = x;
    }
    else if(y == y->parent->right) {	//这里判断是x而不是x->parent
        y->parent->right = x;
    }
    else {
        y->parent->left = x;
    }

    x->right = y;
    y->parent = x;
}


3.4插入

  1. 定义一个循环变量x,初始化指向根结点
  2. 当x不是叶子结点则进行循环
    1. 如果插入结点的key比当前结点key小,则当循环变量往左走
    2. 如果插入结点的key比当前结点key大,则当循环变量往右走
    3. 如果出现相等情况,应根据应用场景而定:
      1. 如果是定时器,丢掉相当丢掉了一个任务 可以增加1微秒,对实际情况影响不打又保证key的唯一性
      2. 如果是学生信息以学号或时间为key,则直接返回,防止重复插入
  3. 把新节点插入,并赋初值

3.4.1二叉树的插入

二叉树将结点插入到最底层的非叶子结点

int key_compare(KEY_TYPE* key1,KEY_TYPE* key2) {
    //大于返回正数,等于返回0,小于返回负数    
    //只要把key_compare函数留出来就可以把红黑树做成模板
}

void rbtree_insert(prbtree T, prbtree_node z) {
    prbtree x = T->root;	//x是循环变量相当属于for循环的i
    prbtree y = T->nil;
    while(x != T->nil) {
        y = x;	//y一直是x的父节点,慢一步
        if(z->key < x->key) {	//有的对象(人)需要多个维度衡量,key比较写成函数,
            					//调用key_compare
            x = x->left;
        } else if(z->key > x->key) {
            x = x->right;
        } else {
            return;
        }
    }

    //退出循环后x指向的是叶子结点,所以要找到x当前位置得需要x结点的父节点
    //所以需要定义一个父节点,用来找到当前位置
    z->parent = y;
    if(y == T.nil) {	//插入的第一个结点
        T->root = z;
    } else if(z->key < y->key) {
        y->left = z;
    } else {
        y->right = z;
    }
    z->left = T->nil;
    z->right = T->nil;
}

3.4.2红黑树的插入

二叉树的方法已把结点插入到树中,那么这个树是红色还是黑色呢???要看父节点
注意:在插入之前,原本的树就是红黑树,满足红黑树的性质
为什么新插入的结点需要初始化为红色的???

  1. 首先黑高不好判断
  2. 因为原树满足红黑树性质,加入黑结点后黑高肯定不同,每次都要旋转
  3. 为什么是红色?红色好判断,不影响黑高
  4. 初始化为红色只有父结点为红色才需要调整(性质4)

  1. 通过二叉树方法插入新节点
  2. 给新节点赋初值为红色
  3. 如果父节点为红色需要进行循环递归调整
    1. 如果叔父节点也是红色,采用“重新着色”方法进行调整
      1. 令父结点和叔父结点为黑色,祖父结点为红色
      2. 贸然将祖父节点该成红色可能导致与祖父节点的父结点颜色冲突,所以将表示当前结点的指针指向祖父结点,从第3步进行循环递归
    2. 否则叔父结点为黑色,因为原来树就是红黑树,所以叔父结点只能为叶子结点才能满足黑高
      1. 判断父结点是祖父结点的左支还是右支
      2. 如果父结点是祖父结点的左支image.png
        1. 判断新结点是父结点的左支还右支
        2. 如果是右支
          1. 需要将指向 新结点的指针 指向 新结点的指针的父结点,并以新结点的父结点进行“左旋”
          2. 旋转后的结果:父结点在新结点左支,新结点在祖父结点的左支,即与“新结点是父结点的左支“相同,指向当前节点的指针也相同
        3. 令父结点为黑,令祖父节点为红
        4. 以祖父结点进行”右旋“
      3. 如果父结点是祖父结点的右支image.png
        1. 将上面”如果父结点是祖父结点的左支“代码复制,将left 该成right,将right改成left
        2. 即,以下内容
        3. 判断新结点是父结点的左支还右支
        4. 如果是左支
          1. 需要将指向 新结点的指针 指向 新结点的指针的父结点,并以新结点的父结点进行“右旋”
          2. 旋转后的结果:父结点在新结点右支,新结点在祖父结点的右支,即与“新结点是父结点的右支“相同,指向当前节点的指针也相同
        5. 令父结点为黑,令祖父节点为红
        6. 以祖父结点进行”左旋“
  4. 新插入的节点是根结点或者调整的时候可能将根节点变成红色,所以最后把根节点致黑
#define RED 0	//谁是0谁是1无所谓,只要不同就行
#define BLACK 1

int key_compare(KEY_TYPE* key1,KEY_TYPE* key2) {
    //大于返回正数,等于返回0,小于返回负数    
    //只要把key_compare函数留出来就可以把红黑树做成模板
}

//——————————————————以上内容最好写在上面,外部接口————————————————
void rbtree_insert_fixup(prbtree T, prbtree_node z)	{	//对红黑树进行调整
	//if(z->parent->color == RED) {	//贸然改变祖父结点颜色为红色,可能会导致与祖父的
    								//父结点冲突,需要用循环递归的思想
    while(z->parent->color == RED) {
           if(z->parent == z->parent->parent->left) {
               rbtree_node *y = z->parent->parent->right;	//定义叔父结点
               if(y->color == RED) {
                   z->parent->color = BLACK;
                   y->color = BLACK;
                   z->parent->parent->color = RED;
                    
                   //祖父结点一下现在都满足红黑树性质,
                   //将当前结点改为祖父结点,递归解决可能存在的祖父结点与其父结点的冲突
                   z = z->parent->parent;
               } else {	//祖父结点若为黑色只能是叶子结点才能使原树满足红黑树性质
                   if(z == z->parent->right) {
                       z = z->parent;
                       _left_rotate(T, z);	//旋转后变成一条直线
                   }
                   z->parent->color = BLACK;
                   z->parent->parent->color = RED;
                   _right_rotate(T, z->parent->parent ); 
               }
           } else {	//插入结点z的父节点是祖父结点的右支
                    //直接将左支代码复制,然后left改成right,right改成left
               rbtree_node *y = z->parent->parent->left;	//定义叔父结点
               if(y->color == RED) {
                   z->parent->color = BLACK;
                   y->color = BLACK;
                   z->parent->parent->color = RED;
                    
                   //祖父结点一下现在都满足红黑树性质,
                   //将当前结点改为祖父结点,递归解决可能存在的祖父结点与其父结点的冲突
                   z = z->parent->parent;
               } else {	//祖父结点若为黑色只能是叶子结点才能使原树满足红黑树性质
                   if(z == z->parent->left) {
                       z = z->parent;
                       _right_rotate(T, z);	//旋转后变成一条直线
                   }
                   z->parent->color = BLACK;
                   z->parent->parent->color = RED;
                   _left_rotate(T, z->parent->parent ); 
               }
               
           }
    }
    T->root->color = BLACK;	//递归可能会将根结点变成红色
}

void rbtree_insert(prbtree T, prbtree_node z) {
    prbtree x = T->root;	//x是循环变量相当属于for循环的i
    prbtree y = T->nil;
    while(x != T->nil) {
        y = x;	//y一直是x的父节点,慢一步
        if(z->key < x->key) {	//有的对象(人)需要多个维度衡量,key比较写成函数,
            					//调用key_compare
            x = x->left;
        } else if(z->key > x->key) {
            x = x->right;
        } else {
            return;
        }
    }

    //退出循环后x指向的是叶子结点,所以要找到x当前位置得需要x结点的父节点
    //所以需要定义一个父节点,用来找到当前位置
    z->parent = y;
    if(y == T.nil) {	//插入的第一个结点
        T->root = z;
    } else if(z->key < y->key) {
        y->left = z;
    } else {
        y->right = z;
    }
    z->left = T->nil;
    z->right = T->nil;
    z->color = RED;	//新节点初始化为红色
} 

思考题

案例一、服务器端高并发IO的keep alive方案,满足一下几个需求

  1. 每个IO都是自己的时间戳
  2. 每个IO收到自己的beat后,重置自己的定时器
  3. 若IO定时没有收到beat,则执行IO的回调函数,并重置定时器
  4. 若再次没有收到beat,销毁IO,注销定时器

案例二、设计一个线程或者进程的运行体R与运行体调度器s的结构体

  1. 运行体R:包含运行状态{新建、准备、挂起{IO等待、IO等待写、睡眠、延时}、退出},运行体回调函数,回调参数
  2. 调度器s:包含栈指针,栈大小,当前运行体
  3. 调度器s:包含执行集合{就绪、延时、睡眠、等待}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这是一段简单的代码C:#include<stdio.h> struct node { int key; struct node *left, *right; int color; }; // A Red-Black tree node structure struct node *newNode(int key) { struct node *temp = (struct node *)malloc(sizeof(struct node)); temp->key = key; temp->left = temp->right = NULL; temp->color = 1; // 1 for red, 0 for black return temp; } // A utility function to right rotate subtree rooted with y // See the diagram given above. struct node *rightRotate(struct node *y) { struct node *x = y->left; struct node *T2 = x->right; // Perform rotation x->right = y; y->left = T2; // Return new root return x; } // A utility function to left rotate subtree rooted with x // See the diagram given above. struct node *leftRotate(struct node *x) { struct node *y = x->right; struct node *T2 = y->left; // Perform rotation y->left = x; x->right = T2; // Return new root return y; } // This function fixes the Red-Black tree void fixViolation(struct node *temp) { struct node *parent_temp = NULL; struct node *grand_parent_temp = NULL; while ((temp != root) && (temp->color != 0) && (temp->parent->color == 1)) { parent_temp = temp->parent; grand_parent_temp = temp->parent->parent; /* Case : A Parent of temp is left child of Grand-parent of temp */ if (parent_temp == grand_parent_temp->left) { struct node *uncle_temp = grand_parent_temp->right; /* Case : 1 The uncle of temp is also red Only Recoloring required */ if (uncle_temp != NULL && uncle_temp->color == 1) { grand_parent_temp->color = 1; parent_temp->color = 0; uncle_temp->color = 0; temp = grand_parent_temp; } else { /* Case : 2 temp is right child of its parent Left-rotation required */ if (temp == parent_temp->right) { temp = parent_temp; leftRotate(temp); } /* Case : 3 temp is left child of its parent Right-rotation required */ rightRotate(grand_parent_temp); swap(parent_temp->color, grand_parent_temp->color); temp = parent_temp; } } /* Case : B Parent of temp is right child of Grand-parent of temp */ else { struct node *uncle_temp = grand_parent_temp->left; /* Case : 1 The uncle of temp is also red Only Recoloring required */ if ((uncle_temp != NULL) && (uncle_temp->color == 1)) { grand_parent_temp->color = 1; parent_temp->color = 0; uncle_temp->color = 0; temp = grand_parent_temp; } else { /* Case : 2 temp is left child of its parent Right-rotation required */ if (temp == parent_temp->left) { temp = parent_temp; rightRotate(temp); } /* Case : 3 temp is right child of its parent Left-rotation required */ leftRotate(grand_parent_temp); swap(parent_temp->color, grand_parent_temp->color); temp = parent_temp; } } } root->color = 0; } ### 回答2: (Red-Black Tree)是一种自平衡的二叉搜索,它的每个节点都具有一个颜色属性,可以是色或色。具有以下几个性质: 1. 每个节点要么是色,要么是色。 2. 根节点是色。 3. 叶子节点(NIL节点,空节点)是色。 4. 如果一个节点是色,则它的两个子节点都是色(不能出现连续的色节点)。 5. 对于每个节点,从该节点到其子孙节点的所有路径上包含相同数目的色节点。 以下是一个简单实现的C代码: ```c #include <stdio.h> #include <stdlib.h> enum Color {RED, BLACK}; // 节点结构定义 typedef struct node { int data; enum Color color; struct node *left, *right, *parent; } Node; // 结构定义 typedef struct rbtree { Node *root; Node *nil; } RBTree; // 左旋操作 void leftRotate(RBTree *tree, Node *x) { Node *y = x->right; x->right = y->left; if (y->left != tree->nil) { y->left->parent = x; } y->parent = x->parent; if (x->parent == tree->nil) { tree->root = y; } else if (x == x->parent->left) { x->parent->left = y; } else { x->parent->right = y; } y->left = x; x->parent = y; } // 右旋操作 void rightRotate(RBTree *tree, Node *y) { Node *x = y->left; y->left = x->right; if (x->right != tree->nil) { x->right->parent = y; } x->parent = y->parent; if (y->parent == tree->nil) { tree->root = x; } else if (y == y->parent->left) { y->parent->left = x; } else { y->parent->right = x; } x->right = y; y->parent = x; } // 插入操作的修复 void insertFixup(RBTree *tree, Node *z) { while (z->parent->color == RED) { if (z->parent == z->parent->parent->left) { Node *y = z->parent->parent->right; if (y->color == RED) { z->parent->color = BLACK; y->color = BLACK; z->parent->parent->color = RED; z = z->parent->parent; } else { if (z == z->parent->right) { z = z->parent; leftRotate(tree, z); } z->parent->color = BLACK; z->parent->parent->color = RED; rightRotate(tree, z->parent->parent); } } else { Node *y = z->parent->parent->left; if (y->color == RED) { z->parent->color = BLACK; y->color = BLACK; z->parent->parent->color = RED; z = z->parent->parent; } else { if (z == z->parent->left) { z = z->parent; rightRotate(tree, z); } z->parent->color = BLACK; z->parent->parent->color = RED; leftRotate(tree, z->parent->parent); } } } tree->root->color = BLACK; } // 插入操作 void insert(RBTree *tree, int data) { Node *z = (Node *) malloc(sizeof(Node)); z->data = data; z->color = RED; z->left = tree->nil; z->right = tree->nil; Node *y = tree->nil; Node *x = tree->root; while (x != tree->nil) { y = x; if (z->data < x->data) { x = x->left; } else { x = x->right; } } z->parent = y; if (y == tree->nil) { tree->root = z; } else if (z->data < y->data) { y->left = z; } else { y->right = z; } insertFixup(tree, z); } // 中序遍历 void inorderTraversal(RBTree *tree, Node *root) { if (root != tree->nil) { inorderTraversal(tree, root->left); printf("%d ", root->data); inorderTraversal(tree, root->right); } } int main() { RBTree tree; Node nilNode; tree.nil = &nilNode; tree.nil->color = BLACK; tree.root = tree.nil; // 插入节点 insert(&tree, 5); insert(&tree, 2); insert(&tree, 7); insert(&tree, 1); insert(&tree, 3); insert(&tree, 6); insert(&tree, 8); // 中序遍历输出 inorderTraversal(&tree, tree.root); printf("\n"); return 0; } ``` 以上代码实现了的插入操作,并通过中序遍历的方式输出了插入后的节点数据。可以根据需要修改插入数据和遍历方式来进行测试。 ### 回答3: 是一种自平衡的二叉搜索,其在插入和删除操作后通过一系列旋转和重新着色操作来保持平衡。 下面是一个简单的的 C 代码实现: ```c #include <stdio.h> #include <stdlib.h> // 定义的节点结构 typedef struct Node { int data; // 节点存储的数据 enum { RED, BLACK } color; // 节点的颜色 struct Node *parent; // 指向父节点的指针 struct Node *left; // 指向左子节点的指针 struct Node *right; // 指向右子节点的指针 } Node; // 全局变量,表示的根节点 Node *root = NULL; // 左旋操作 void leftRotate(Node *x) { Node *y = x->right; x->right = y->left; if (y->left != NULL) { y->left->parent = x; } y->parent = x->parent; if (x->parent == NULL) { root = y; } else if (x == x->parent->left) { x->parent->left = y; } else { x->parent->right = y; } y->left = x; x->parent = y; } // 右旋操作 void rightRotate(Node *x) { Node *y = x->left; x->left = y->right; if (y->right != NULL) { y->right->parent = x; } y->parent = x->parent; if (x->parent == NULL) { root = y; } else if (x == x->parent->left) { x->parent->left = y; } else { x->parent->right = y; } y->right = x; x->parent = y; } // 插入操作 void insert(int data) { Node *node = (Node *)malloc(sizeof(Node)); node->data = data; node->left = NULL; node->right = NULL; node->color = RED; Node *y = NULL; Node *x = root; while (x != NULL) { y = x; if (data < x->data) { x = x->left; } else { x = x->right; } } node->parent = y; if (y == NULL) { root = node; } else if (data < y->data) { y->left = node; } else { y->right = node; } insertFixup(node); } // 插入修正操作 void insertFixup(Node *z) { while (z->parent != NULL && z->parent->color == RED) { if (z->parent == z->parent->parent->left) { Node *y = z->parent->parent->right; if (y != NULL && y->color == RED) { z->parent->color = BLACK; y->color = BLACK; z->parent->parent->color = RED; z = z->parent->parent; } else { if (z == z->parent->right) { z = z->parent; leftRotate(z); } z->parent->color = BLACK; z->parent->parent->color = RED; rightRotate(z->parent->parent); } } else { Node *y = z->parent->parent->left; if (y != NULL && y->color == RED) { z->parent->color = BLACK; y->color = BLACK; z->parent->parent->color = RED; z = z->parent->parent; } else { if (z == z->parent->left) { z = z->parent; rightRotate(z); } z->parent->color = BLACK; z->parent->parent->color = RED; leftRotate(z->parent->parent); } } } root->color = BLACK; } // 中序遍历输出 void inorderTraversal(Node *node) { if (node != NULL) { inorderTraversal(node->left); printf("%d\n", node->data); inorderTraversal(node->right); } } int main() { insert(5); insert(3); insert(8); insert(1); insert(4); insert(6); insert(9); printf("中序遍历结果:\n"); inorderTraversal(root); return 0; } ``` 该代码实现了的基本操作,包括左旋、右旋、插入和插入修正等。每个节点包含数据、颜色和指向父节点、左子节点和右子节点的指针。插入操作使用了插入修正来保持的平衡性。 通过在 `main` 函数中插入一些数据,即可得到生成的,并通过中序遍历输出中节点的数据,以验证结构是否正确。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值