红黑树的原理及代码实现

简介: 红黑树的原理及代码实现

红黑树的原理这篇博文讲解的很详细了,不熟悉红黑树的定义可以看看红黑树的详细原理讲解

咱们直接进入代码实现讲解

1.红黑树的节点

创建一棵红黑树,先从创建红黑树的节点开始:

1.红黑树首先是一个二叉树,所以需要一个左儿子节点的指针,右儿子节点的指针

2.红黑树是有BALCK和RED两种颜色的,所以每个节点应该有颜色

3.红黑树是Key-Value存储的

4.因为之后要进行旋转等调整,所以还需要一个父节点的信息

所以红黑树节点定义如下:

typedef int KEY_TYPE;
typedef struct _rbtree_node {
  unsigned char color;//红黑树的颜色
  struct _rbtree_node *right;//指向的左子树
  struct _rbtree_node *left;//指向的右子树
  struct _rbtree_node *parent;//父亲节点
  KEY_TYPE key;//key
  void *value;//value
} rbtree_node;

2.创建红黑树整体结构

红黑树有五个性质:

为了方便整棵红黑树的维护,我们默认叶子节点是空节点即所有实际叶子节点指向null节点,null结点是真正的叶子节点,且为黑色,之后默认条件5是满足的

typedef struct _rbtree {
  rbtree_node *root;//红黑树的根节点
  rbtree_node *nil;//NULL节点
} rbtree;

3.红黑树节点的旋转

红黑树的选择分为左旋跟右旋

1.左旋,如上图将x左旋,会变成什么样子?

x节点会作为y节点的左子树,y的左子树b,会变成x的右子树,x的父亲节点的儿子节点会变成y。

2.右旋,如上图将y右旋

y节点会作为x节点的右子树,x的右子树b,会变成y的左子树,y的父亲节点的儿子节点会变成x。

旋转操作一共更改上图3条线->一共更改6个指向,因为线是双向的

咱们对应上图结合代码看:

void rbtree_left_rotate(rbtree *T, rbtree_node *x) { //对应上图x节点左旋
  rbtree_node *y = x->right;  // x  --> y  ,  y --> x,   right --> left,  left --> right
  x->right = y->left; //将y的左子树变成x的右子树
  if (y->left != T->nil) { //如果y的左子树不是NULL,将它的父亲节点变成x
    y->left->parent = x;
  }
  y->parent = x->parent; //将y的父亲节点改成x的父亲节点
  if (x->parent == T->nil) { //如果x为根节点
    T->root = y;
  } else if (x == x->parent->left) {//如果x是x父亲节点的左节点
    x->parent->left = y;
  } else {
    x->parent->right = y;//如果x是x父亲节点的右节点
  }
  y->left = x; //y的左子树为x
  x->parent = y; //x的父亲节点为y
}

右旋只需要y和x,left和right取反

void rbtree_right_rotate(rbtree *T, rbtree_node *y) {
  rbtree_node *x = y->left;
  y->left = x->right;//将x的右子树变成y的左子树
  if (x->right != T->nil) {//如果x的右子树不是NULL,将它的父亲节点变成y
    x->right->parent = y;
  }
  x->parent = y->parent;//将x的父亲节点改成y的父亲节点
  if (y->parent == T->nil) {
    T->root = x;
  } else if (y == y->parent->right) {//如果y是y父亲节点的右节点
    y->parent->right = x;
  } else {
    y->parent->left = x;
  }
  x->right = y;//x的右子树为y
  y->parent = x;//y的父亲节点为x
}

4.红黑树的添加节点

红黑树添加节点一定添加到叶子节点(实际上NULL节点才是叶子节点)

我们在上面说了红黑树任何一个节点到儿孙节点的路径的黑高都相同,那插入的节点初始置位RED还是BALCK呢?

答案是RED,RED不会影响整体的红黑树的结构,保证红黑树还是红黑树。

void rbtree_insert(rbtree *T, rbtree_node *z) {
  rbtree_node *y = T->nil;//NULL节点
  rbtree_node *x = T->root;//根
  while (x != T->nil) {//直到叶子节点
    y = x;
    if (z->key < x->key) {
      x = x->left;
    } else if (z->key > x->key) {
      x = x->right;
    } else { //Exist
      return ;
    }
  }
  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;//置位红
  rbtree_insert_fixup(T, z);//红黑树的插入调整
}

5.红黑树的添加节点的调整

红黑树添加节点之后,可能会面临一个情况

不符合红黑树的约束4,可能刚刚插入的红色节点z它的父节点是红色!

这个时候要分4种情况看(为了描述方便我们默认z的父节点是z的祖父节点的左儿子,下面的情况描述都会默认,实际代码只要多考虑z的父亲节点是z的祖父节点的右儿子就行了):

1.刚刚添加的z节点它的父亲节点是黑色

这种情况下红黑树依旧是红黑树,红黑树是平衡的,这种情况无需管。

2.刚刚添加的z节点它的父亲节点是红色,叔父节点y是红色

因为添加z节点之前红黑树是红黑树满足红黑树的4约束,父亲节点是红色,所以z节点祖父节点是黑色

这种情况下,直接改变让z的父亲节点根叔父节点的颜色变为黑色,祖父节点颜色变成红色就行了

为什么呢?

从整体看来这样改变从任何一个点出发,黑高相同的性质没有改变。因为祖父节点的黑色,给父亲节点跟叔父节点了。又让红黑树满足了限制4

3.刚刚添加的z节点它的父亲节点是红色,叔父节点y是黑色,当前节点是左孩子

叔父节点是黑色的情况就不能巧妙的借叔父节点的力了,只能通过旋转加变色使红黑树维持平衡!

如图这种情况,只需要右旋z的祖父节点,之后将z的父亲节点变为黑色,z的祖父节点变为红色就行了

4.刚刚添加的z节点它的父亲节点是红色,叔父节点y是黑色,当前节点是右孩子

这种情况又会麻烦一点,不能直接对z的祖父节点右旋,这样之后红黑树的性质5将被破坏,怎么办呢?

只需将z的父亲节点左旋,变为情况3再做情况3的处理

代码如下:

void rbtree_insert_fixup(rbtree *T, rbtree_node *z) {
  while (z->parent->color == RED) { //z ---> RED
    if (z->parent == z->parent->parent->left) {//父亲节点是祖父节点左儿子
      rbtree_node *y = z->parent->parent->right;//叔父节点
      if (y->color == RED) {//情况2,叔父节点红色
        z->parent->color = BLACK;//叔父节点、父亲节点变成黑色,祖父节点变成红色
        y->color = BLACK;
        z->parent->parent->color = RED;
        z = z->parent->parent; //递归,z变成z的祖父节点
      } else {
        if (z == z->parent->right) {//情况4,先左旋父亲节点
          z = z->parent;
          rbtree_left_rotate(T, z);
        }
        //情况3
        z->parent->color = BLACK;
        z->parent->parent->color = RED;
        rbtree_right_rotate(T, z->parent->parent);
      }
    }else {//父亲节点是祖父节点右儿子
      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; //z --> RED
      } else {
        if (z == z->parent->left) {
          z = z->parent;
          rbtree_right_rotate(T, z);
        }
        z->parent->color = BLACK;
        z->parent->parent->color = RED;
        rbtree_left_rotate(T, z->parent->parent);
      }
    }
  }
  T->root->color = BLACK;//保证根是黑色
}

6.红黑树的查找

根据左子树小于当前节点,右子树大于当前节点的定义进行查找,然后返回,时间复杂度O(logn)

rbtree_node *rbtree_search(rbtree *T, KEY_TYPE key) {
  rbtree_node *node = T->root;
  while (node != T->nil) {
    if (key < node->key) {
      node = node->left;
    } else if (key > node->key) {
      node = node->right;
    } else {
      return node;
    } 
  }
  return T->nil;//返回NULL节点
}

7.红黑树的中序遍历

红黑树中序遍历能够按key从小到大的顺序输出

void rbtree_traversal(rbtree *T, rbtree_node *node) {
  if (node != T->nil) {
    rbtree_traversal(T, node->left);
    printf("key:%d, color:%d\n", node->key, node->color);
    rbtree_traversal(T, node->right);
  }

8.查询红黑树最小的KEY对应的节点

找到左子树的最左边就行了

rbtree_node *rbtree_mini(rbtree *T, rbtree_node *x) {
  while (x->left != T->nil) {
    x = x->left;
  }
  return x;
}

9.查询红黑树最大的KEY对应的节点

rbtree_node *rbtree_maxi(rbtree *T, rbtree_node *x) {
  while (x->right != T->nil) {
    x = x->right;
  }
  return x;
}

10.整体功能实现代码

整体功能代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define RED       1
#define BLACK       2
typedef int KEY_TYPE;
typedef struct _rbtree_node {
  unsigned char color;
  struct _rbtree_node *right;
  struct _rbtree_node *left;
  struct _rbtree_node *parent;
  KEY_TYPE key;
  void *value;
} rbtree_node;
typedef struct _rbtree {
  rbtree_node *root;
  rbtree_node *nil;
} rbtree;
rbtree_node *rbtree_mini(rbtree *T, rbtree_node *x) {
  while (x->left != T->nil) {
    x = x->left;
  }
  return x;
}
rbtree_node *rbtree_maxi(rbtree *T, rbtree_node *x) {
  while (x->right != T->nil) {
    x = x->right;
  }
  return x;
}
rbtree_node *rbtree_successor(rbtree *T, rbtree_node *x) {//中序遍历的后继节点
  rbtree_node *y = x->parent;
  if (x->right != T->nil) {
    return rbtree_mini(T, x->right);
  }
  while ((y != T->nil) && (x == y->right)) {
    x = y;
    y = y->parent;
  }
  return y;
}
void rbtree_left_rotate(rbtree *T, rbtree_node *x) {
  rbtree_node *y = x->right;  // x  --> y  ,  y --> x,   right --> left,  left --> right
  x->right = y->left; //1 1
  if (y->left != T->nil) { //1 2
    y->left->parent = x;
  }
  y->parent = x->parent; //1 3
  if (x->parent == T->nil) { //1 4
    T->root = y;
  } else if (x == x->parent->left) {
    x->parent->left = y;
  } else {
    x->parent->right = y;
  }
  y->left = x; //1 5
  x->parent = y; //1 6
}
void rbtree_right_rotate(rbtree *T, rbtree_node *y) {
  rbtree_node *x = y->left;
  y->left = x->right;
  if (x->right != T->nil) {
    x->right->parent = y;
  }
  x->parent = y->parent;
  if (y->parent == T->nil) {
    T->root = x;
  } else if (y == y->parent->right) {
    y->parent->right = x;
  } else {
    y->parent->left = x;
  }
  x->right = y;
  y->parent = x;
}
void rbtree_insert_fixup(rbtree *T, rbtree_node *z) {
  while (z->parent->color == RED) { //z ---> 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; //z --> RED
      } else {
        if (z == z->parent->right) {
          z = z->parent;
          rbtree_left_rotate(T, z);
        }
        z->parent->color = BLACK;
        z->parent->parent->color = RED;
        rbtree_right_rotate(T, z->parent->parent);
      }
    }else {
      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; //z --> RED
      } else {
        if (z == z->parent->left) {
          z = z->parent;
          rbtree_right_rotate(T, z);
        }
        z->parent->color = BLACK;
        z->parent->parent->color = RED;
        rbtree_left_rotate(T, z->parent->parent);
      }
    }
  }
  T->root->color = BLACK;
}
void rbtree_insert(rbtree *T, rbtree_node *z) {
  rbtree_node *y = T->nil;
  rbtree_node *x = T->root;
  while (x != T->nil) {
    y = x;
    if (z->key < x->key) {
      x = x->left;
    } else if (z->key > x->key) {
      x = x->right;
    } else { //Exist
      return ;
    }
  }
  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;
  rbtree_insert_fixup(T, z);
}
void rbtree_delete_fixup(rbtree *T, rbtree_node *x) {
  while ((x != T->root) && (x->color == BLACK)) {
    if (x == x->parent->left) {
      rbtree_node *w= x->parent->right;
      if (w->color == RED) {
        w->color = BLACK;
        x->parent->color = RED;
        rbtree_left_rotate(T, x->parent);
        w = x->parent->right;
      }
      if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
        w->color = RED;
        x = x->parent;
      } else {
        if (w->right->color == BLACK) {
          w->left->color = BLACK;
          w->color = RED;
          rbtree_right_rotate(T, w);
          w = x->parent->right;
        }
        w->color = x->parent->color;
        x->parent->color = BLACK;
        w->right->color = BLACK;
        rbtree_left_rotate(T, x->parent);
        x = T->root;
      }
    } else {
      rbtree_node *w = x->parent->left;
      if (w->color == RED) {
        w->color = BLACK;
        x->parent->color = RED;
        rbtree_right_rotate(T, x->parent);
        w = x->parent->left;
      }
      if ((w->left->color == BLACK) && (w->right->color == BLACK)) {
        w->color = RED;
        x = x->parent;
      } else {
        if (w->left->color == BLACK) {
          w->right->color = BLACK;
          w->color = RED;
          rbtree_left_rotate(T, w);
          w = x->parent->left;
        }
        w->color = x->parent->color;
        x->parent->color = BLACK;
        w->left->color = BLACK;
        rbtree_right_rotate(T, x->parent);
        x = T->root;
      }
    }
  }
  x->color = BLACK;
}
rbtree_node *rbtree_delete(rbtree *T, rbtree_node *z) {
  rbtree_node *y = T->nil;
  rbtree_node *x = T->nil;
  if ((z->left == T->nil) || (z->right == T->nil)) {
    y = z;
  } else {
    y = rbtree_successor(T, z);
  }
  if (y->left != T->nil) {
    x = y->left;
  } else if (y->right != T->nil) {
    x = y->right;
  }
  x->parent = y->parent;
  if (y->parent == T->nil) {
    T->root = x;
  } else if (y == y->parent->left) {
    y->parent->left = x;
  } else {
    y->parent->right = x;
  }
  if (y != z) {
    z->key = y->key;
    z->value = y->value;
  }
  if (y->color == BLACK) {
    rbtree_delete_fixup(T, x);
  }
  return y;
}
rbtree_node *rbtree_search(rbtree *T, KEY_TYPE key) {
  rbtree_node *node = T->root;
  while (node != T->nil) {
    if (key < node->key) {
      node = node->left;
    } else if (key > node->key) {
      node = node->right;
    } else {
      return node;
    } 
  }
  return T->nil;
}
void rbtree_traversal(rbtree *T, rbtree_node *node) {
  if (node != T->nil) {
    rbtree_traversal(T, node->left);
    printf("key:%d, color:%d\n", node->key, node->color);
    rbtree_traversal(T, node->right);
  }
}
int main() {
  int keyArray[20] = {24,25,13,35,23, 26,67,47,38,98, 20,19,17,49,12, 21,9,18,14,15};
  rbtree *T = (rbtree *)malloc(sizeof(rbtree));
  if (T == NULL) {
    printf("malloc failed\n");
    return -1;
  }
  T->nil = (rbtree_node*)malloc(sizeof(rbtree_node));
  T->nil->color = BLACK;
  T->root = T->nil;
  rbtree_node *node = T->nil;
  int i = 0;
  for (i = 0;i < 20;i ++) {
    node = (rbtree_node*)malloc(sizeof(rbtree_node));
    node->key = keyArray[i];
    node->value = NULL;
    rbtree_insert(T, node);
  }
  rbtree_traversal(T, T->root);
  printf("----------------------------------------\n");
  for (i = 0;i < 20;i ++) {
    rbtree_node *node = rbtree_search(T, keyArray[i]);
    rbtree_node *cur = rbtree_delete(T, node);
    free(cur);
    rbtree_traversal(T, T->root);
    printf("----------------------------------------\n");
  }
}


目录
相关文章
|
7月前
|
存储 关系型数据库 容器
一篇文章带你了解红黑树并将其模拟实现(上)
红黑树的概念和性质 1. 概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍,因而是接近平衡的
|
4月前
|
Java
数据结构 AVL树概念以及实现插入的功能(含Java代码实现)
数据结构 AVL树概念以及实现插入的功能(含Java代码实现)
55 0
|
4月前
|
算法
红黑树的原理及实现
红黑树的原理及实现
46 0
|
5月前
|
C++
【数据结构&C++】超详细一文带小白轻松全面理解 [ 二叉平衡搜索树-AVL树 ]—— [从零实现&逐过程分析&代码演示&简练易懂]
【数据结构&C++】超详细一文带小白轻松全面理解 [ 二叉平衡搜索树-AVL树 ]—— [从零实现&逐过程分析&代码演示&简练易懂]
|
6月前
|
算法
【算法与数据结构】二叉树的三种遍历代码实现(下)—— 非递归方式实现(大量图解)
【算法与数据结构】二叉树的三种遍历代码实现(下)—— 非递归方式实现(大量图解)
37 0
|
6月前
|
存储 算法
【算法与数据结构】二叉树的三种遍历代码实现(上)—— 用递归序知识点讲解
【算法与数据结构】二叉树的三种遍历代码实现(上)—— 用递归序知识点讲解
28 0
|
9月前
|
存储 算法
二叉搜索树:原理与应用
二叉搜索树:原理与应用
83 0
|
算法 Java
一天一个算法——>红黑树JAVA实现
一天一个算法——>红黑树JAVA实现
55 0
|
存储 Java Linux
C++ 第八节&数据结构 第七节 ——二叉搜索树 AVL树 红黑树(底层原理图+模拟实现)
每一个关键码key,都有与之对应的值Value,即&lt;Key, Value&gt;的键值对。该种方式在现实生活中非常常见:比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文&lt;word, chinese&gt;就构成一种键值对;
193 0
C++ 第八节&数据结构 第七节 ——二叉搜索树 AVL树 红黑树(底层原理图+模拟实现)