AVL树/红黑树介绍及插入操作实现
Posted DR5200
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AVL树/红黑树介绍及插入操作实现相关的知识,希望对你有一定的参考价值。
文章目录
一.AVL树概念
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
它的左右子树都是AVL树
左右子树高度之差(简称平衡因子)的绝对值不超过1( 可以为-1/0/1 )
二.AVL树模拟实现
AVL树接口总览
template<class K,class V>
struct AVLTreeNode
AVLTreeNode(const pair<K,V>& kv)
:_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_bf(0)
,_kv(kv)
AVLTreeNode* _left; // 指向左子树节点
AVLTreeNode* _right; // 指向右子树节点
AVLTreeNode* _parent; // 指向父节点
int _bf; // 平衡因子
pair<K, V> _kv; // 节点数据
;
template<class K,class V>
class AVLTree
typedef AVLTreeNode<K, V> Node;
public:
AVLTree()
:_root(nullptr)
~AVLTree();
Node* Find(const K& key);
V& operator[](const K& key);
bool IsAVLTree();
void InOrder();
bool Insert(const pair<K, V>& kv);
void RotateL(Node* parent);
void RotateR(Node* parent);
void RotateLR(Node* parent);
void RotataRL(Node* parent);
private:
Node* _root;
;
当插入一个新节点的时候,首先根据二叉搜索树的性质,比根节点小的去左子树查找,比根节点大的去右子树查找,循环找到新节点的位置,如果是二叉搜索树的话,插入操作到此就完成了,但AVL树需要保证平衡因子的绝对值不超过1,所以我们还需要更新 新节点沿着父节点的路径上的平衡因子,如果有绝对值超过2的平衡因子,则需要进行旋转操作
右单旋 :
void RotateR(Node* parent)
Node* subL = parent->_left;
Node* subLR = subL->_right;
Node* parentParent = parent->_parent;
parent->_left = subLR;
subL->_right = parent;
if (subLR)
subLR->_parent = parent;
if (parent == _root)
_root = subL;
_root->_parent = nullptr;
else
if (parentParent->_right == parent) parentParent->_right = subL;
else parentParent->_left = subL;
subL->_parent = parentParent;
parent->_parent = subL;
subL->_bf = parent->_bf = 0;
左单旋 :
void RotateL(Node* parent)
Node* subR = parent->_right;
Node* subRL = subR->_left;
Node* parentParent = parent->_parent;
parent->_right = subRL;
subR->_left = parent;
if (subRL)
subRL->_parent = parent;
if (parent == _root)
_root = subR;
_root->_parent = nullptr;
else
if (parentParent->_right == parent) parentParent->_right = subR;
else parentParent->_left = subR;
subR->_parent = parentParent;
parent->_parent = subR;
subR->_bf = parent->_bf = 0;
左右双旋 :
void RotateLR(Node* parent)
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(subL);
RotateR(parent);
if (bf == 1)
parent->_bf = 0;
subLR->_bf = 0;
subL->_bf = -1;
else if (bf == -1)
parent->_bf = 1;
subLR->_bf = 0;
subL->_bf = 0;
else if (bf == 0)
parent->_bf = 0;
subLR->_bf = 0;
subL->_bf = 0;
右左双旋 :
void RotateRL(Node* parent)
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(subR);
RotateL(parent);
if (bf == 1)
parent->_bf = -1;
subR->_bf = 0;
subRL->_bf = 0;
else if (bf == -1)
parent->_bf = 0;
subR->_bf = 1;
subRL->_bf = 0;
else if (bf == 0)
parent->_bf = 0;
subR->_bf = 0;
subRL->_bf = 0;
插入操作
pair<Node*, bool> Insert(const pair<K, V>& kv)
// 完成插入操作
if (_root == nullptr)
_root = new Node(kv);
return pair<Node*,bool>(_root,true);
Node* parent = _root, * cur = _root;
while (cur)
if (kv.first < cur->_kv.first)
parent = cur;
cur = cur->_left;
else if (kv.first > cur->_kv.first)
parent = cur;
cur = cur->_right;
else
return pair<Node*,bool>(cur, false);
cur = new Node(kv);
if (parent->_kv.first < cur->_kv.first) parent->_right = cur;
else parent->_left = cur;
cur->_parent = parent;
Node* tmp = cur;
// 更新平衡因子
while (cur != _root)
if (parent->_kv.first < cur->_kv.first) ++parent->_bf;
else --parent->_bf;
if (parent->_bf == 0) break;
else if (parent->_bf == 1 || parent->_bf == -1)
cur = parent;
parent = cur->_parent;
else if (parent->_bf == 2 || parent->_bf == -2)
if (parent->_bf == -2)
// 右单旋
if (cur->_bf == -1) RotateR(parent);
// 左右双旋
else if (cur->_bf == 1) RotateLR(parent);
else if (parent->_bf == 2)
// 左单旋
if (cur->_bf == 1) RotateL(parent);
// 右左双旋
else if (cur->_bf == -1) RotateRL(parent);
break;
return pair<Node*,bool>(tmp, true);
查找操作,根据key值进行查找即可
Node* Find(const K& key)
Node* cur = _root;
while (cur)
if (key < cur->_kv.first) cur = cur->_left;
else if (key > cur->_kv.first) cur = cur->_right;
else return cur;
return nullptr;
operator[]
如果AVL树中已经存在相同的key值,返回与之相同的key值的val,若不存在,则新插入一个节点,返回该节点的val值
V& operator[](const K& key)
pair<Node*, bool> ret = Insert(make_pair(key,V()));
return ret.first->_kv.second;
检验AVL树是否平衡
(1). 检验右子树高度 - 左子树高度是否等于平衡因子(bf)
(2). 检验右子树高度 - 左子树高度的绝对值是否小于2
// 求子树高度
int _Height(Node* root)
if (root == nullptr) return 0;
int LeftHeight = _Height(root->_left);
int RightHeight = _Height(root->_right);
// 左右子树中更高的树的高度 + 1
return LeftHeight > RightHeight ? LeftHeight + 1 : RightHeight + 1;
bool _IsBalance(Node* root)
if (root == nullptr) return true;
int LeftHeight = _Height(root->_left);
int RightHeight = _Height(root->_right);
// 检验右子树高度 - 左子树高度是否等于平衡因子(bf)
if (RightHeight - LeftHeight != root->_bf)
cout << "平衡因子异常 : " << root->_kv.first << endl;
return false;
// 检验右子树高度 - 左子树高度的绝对值是否小于2并去递归检测左右子树
return abs(RightHeight - LeftHeight) < 2 && _IsBalance(root->_left) && _IsBalance(root->_right);
bool IsAVLTree()
return _IsBalance(_root);
三.红黑树概念
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出俩倍(最长路径不能超过最短路径的2倍),因而是接近平衡的。
(1). 每个结点不是红色就是黑色
(2). 根节点是黑色的
(3). 如果一个节点是红色的,则它的两个孩子结点是黑色的 (没有连续的红色节点)
(4). 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点 (每条路径上都包含相同数量的黑色节点)
(5). 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)
满足以上性质,最短路径为全为黑色节点的路径,最长路径为一黑一红节点交替的路径,保证了最长路径为最短路径的2倍
四.红黑树模拟实现
红黑树接口总览
enum Colour
RED,
BLACK,
;
template<class K, class V>
struct RBTreeNode
RBTreeNode(const pair<K, V>& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
,_col(RED)
, _kv(kv)
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
pair<K, V> _kv;
Colour _col;
;
template<class K, class V>
class RBTree
typedef RBTreeNode<K, V> Node;
public:
pair<Node*, bool> Insert(const pair<K, V>& kv);
bool IsBalance();
private:
Node* _root;
插入操作 :
首先我们默认插入的节点是红色的,这是因为如果插入黑色节点的话,会影响某条路径上的黑色节点数量,而红黑树要求每条路径上的黑色节点数量一致,所以插入黑色节点操控起来难度更大
(1). 按照二叉搜索树的规则找到节点应该插入的位置
(2). 如果插入节点的父亲是黑色节点,并没有违反红黑树的规定,不用进行操作
(3). 如果插入节点的父亲是红色节点,说明祖父节点一定是黑色节点(如果祖父不是黑色节点,那在插入之前就已经不是红黑树了,因为祖父节点和父亲节点都是红色节点),所以此时就需要看叔叔节点了,叔叔节点分为以下3种情况
一. uncle节点存在且为红
将parent节点和uncle节点变为黑色节点,grandfather节点变为红色节点,然后循环 cur = grandparent,parent = cur->parent,直到parent为空
二. uncle节点不存在/存在且为黑(单旋转 + 变色)
uncle不存在
uncle存在,以grandfather为根进行右单旋,parent变为黑,grandfather变为红
uncle存在,以grandfather为根进行左单旋,parent变为黑,grandfather变为红
三. uncle节点不存在/存在且为黑(双旋转 + 变色)
uncle不存在,以parent为旋转点,进行左单旋,以grandfather为旋转点进行右单旋
uncle存在,以parent为旋转点,进行左单旋,以grandfather为旋转点进行右单旋
插入操作实现
pair<Node*, bool> Insert(const pair<K, V>& kv)
// 插入第一个节点
if (_root == nullptr)
_root = new Node(kv);
_root->_col = BLACK;
return make_pair(_root, true);
// 找到对应的位置进行插入
Node* parent = _root, * cur = _root;
while (cur)
if (kv.first < cur->_kv.first)
parent = cur;
cur = cur->_left;
else if (kv.first > cur->_kv.first)
parent = cur;
cur = cur->_right;
else
return make_pair(cur, false);
cur = new Node(kv);
if (parent->_kv.first < cur->_kv.first) parent->_right = cur;
else parent->_left = cur;
cur->_parent = parent;
// parent是红色节点的几种情况
while (parent && parent->_col == RED)
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
Node* uncle = grandfather->_right;
// 情况1 : uncle存在且为red
if (uncle && uncle->_col == RED)
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
// 情况2 + 3 : uncle不存在/uncle存在且为黑
else
if (cur == parent->_left)
RotateR(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
else
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
break;
else
Node* uncle = grandfather->_left;
if (uncle && uncle->_col == RED)
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
else
if (cur == parent->_right)
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
else
以上是关于AVL树/红黑树介绍及插入操作实现的主要内容,如果未能解决你的问题,请参考以下文章