0
点赞
收藏
分享

微信扫一扫

红黑树概念和旋转实现


文章目录

  • ​​一、红黑树的概念​​
  • ​​二、红黑树的定义代码​​
  • ​​三、红黑树的左旋转和右旋转代码​​

一、红黑树的概念

不是一颗平衡树,节点的左右子树高度大的不超过高度小的两倍。在满足红黑树性质的前提下,旋转的次数要远小于AVL树,虽然红黑树不想AVL树那样平衡,也不会像BST树一样,有序插入会形成一个链表

而AVL树是平衡树,任意节点左右子树高度差不超过1,从根节点到任意叶子节点的距离都是相当的,即 查询效率高。然后AVL树旋转的次数和高度相关,有可能从失衡节点往根节点回溯的过程中有很多失衡节点,这都需要进行旋转,时间复杂度为。而红黑树remove后最多旋转3次

红黑树概念和旋转实现_红黑树_02


红黑树特点:

  • 树的每一个节点不是黑色就是红色
  • root是黑色
  • null是黑色
  • 从根节点到每一个叶子节点的所有路径上,不能出现连续的红色节点。即父亲是红色,孩子一定全是黑色;如果孩子有红色,父亲一定是黑色
  • 从任一节点到其每个叶子节点的所有路径上,黑色节点的数量是相同的

操作

AVL

红黑树

平衡树



增删查时间复杂度

O(logn)

O(logn)

insert最多旋转的次数

2

2

remove最多旋转的次数

O(logn)

3

如果remove的场景少,可以考虑使用AVL树,如果多,就使用红黑树。C++的map、multimap、set、multiset的底层数据结构就是红黑树,这是因为我们使用容器的时候,增删改查都需要用到,而且频率相当

思考题:在红黑树中,节点的左右子树的高度差最多不能超过多少?

左右子树高度差最多不超过两倍

红黑树概念和旋转实现_平衡树_03

二、红黑树的定义代码

在红黑树中,访问节点的时候,要访问到它的父亲,爷爷和叔叔,需要方便的访问到父节点

在递归的过程中就不方便了,而且对于红黑树来说,插入操作最多旋转2次,局部解决完之后,局部没有改变红黑树的性质,全局自然就维护了红黑树的性质,局部解决好了,就不用向上回溯了,所以我们不需要使用递归,递归的话要从插入的地方回溯到根节点,效率低

template<typename T>
class RBTree {
public:
RBTree():root_(nullptr){}


private:
enum Color {
RED,
BLACK
};

struct Node {
Node(T data=T(), Color color=BLACK, Node* parent=nullptr, Node* left=nullptr, Node* right=nullptr)
: data_(data)
, color_(color)
, parent_(parent)
, left_(left)
, right_(right)
{}

T data_;
Color color_;
Node* left_;
Node* right_;
Node* parent_; // 用于向上访问
};

Node* root_;
};

三、红黑树的左旋转和右旋转代码

AVL树的左旋转代码

红黑树概念和旋转实现_数据结构_04


AVL树的旋转代码只改了孩子域,父亲域是回溯时利用返回值改的,我们写红黑树代码是非递归形式的,所以需要手动改6个地址域

红黑树概念和旋转实现_链表_05


红黑树的旋转和AVL树的旋转一样的,只是多了一个parent,代码基于AVL树改即可

void leftRotate(Node* node) {
Node* child = node->right_;
// 1
child->parent_ = node->parent_;
if (node->parent_ == nullptr) {
// node的父节点为空,说明node为root,此时child转上来,child成为root
root_ = child;
}
else {
// node不是root
if (node->parent_->left_ == node) {
// node是左孩子 2
node->parent_->left_ = child;
}
else {
// node是右孩子
node->parent_->right_ = child;
}
}

// 3
node->right_ = child->left_;
if (child->left_ != nullptr) {
// 4
child->left_->parent_ = node;
}
// 5
child->left_ = node;
// 6
node->parent_ = child;
}

AVL树的右旋转代码

红黑树概念和旋转实现_平衡树_06


红黑树概念和旋转实现_数据结构_07

// 以node为轴进行右旋
void rightRotate(Node* node) {
Node* child = node->left_;
child->parent_ = node->parent_; // 1
if (node->parent_ == nullptr) {
// node就是root
root_ = child;
}
else {
// node不是root
if (node->parent_->left_ == node) {
// node是左孩子
node->parent_->left_ = child; // 2
}
else {
// node是右孩子
node->parent_->right_ = child;
}
}

node->left_ = child->right_; // 3
if (child->right_ != nullptr) {
child->right_->parent_ = node; // 4
}
child->right_ = node; // 5
node->parent_ = child; // 6
}

红黑树插入1…10的过程如下:

红黑树概念和旋转实现_数据结构_08

红黑树插入1…39的结果如下:

红黑树概念和旋转实现_红黑树_09


举报

相关推荐

0 条评论