目录
1. 二叉搜索树
1.1 二叉搜索树的概念
1.2 二叉搜索树类模板
1.3 二叉搜索树的操作
1.3.1 查找
1.3.2 插入
1.3.3 删除
1.4 二叉搜索树的性能分析
2. 平衡二叉树
2.1 平衡二叉树的概念
2.2 平衡二叉树类模板
2.3 二叉搜索树的插入
3. 红黑树
3.1 红黑树的概念
3.2 红黑树类模板
二叉搜索树(Binary Search Tree)又称二叉查找树、二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
根据二叉排序树的定义,左子树结点值<根结点值<右子树结点值,因此对二叉排序树进行中序遍历,可以得到一个递增的有序序列。
//二叉搜索树结点
template
struct BSTreeNode
{
BSTreeNode* _left;
BSTreeNode* _right;
K _key;
BSTreeNode(const K& key)
:_left(nullptr)
, _right(nullptr)
, _key(key)
{}
};
//二叉搜索树
template
class BSTree
{
typedef BSTreeNode Node;
public:
//查找
bool Find(const K& key)
{
//……
}
//插入
bool Insert(const K& key)
{
//……
}
//删除
bool Erase(const K& key)
{
//……
}
private:
Node* _root = nullptr;
};
bool Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
cur = cur->_right;
}
else if (cur->_key > key)
{
cur = cur->_left;
}
else
{
return true;
}
}
return false;
}
bool Insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(key);
//链接
if (parent->_key < key)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
return true;
}
首先查找元素是否在二叉搜索树中,如果不存在,则返回; 否则要删除的结点可能分下面四种情
况:
Node的直接后继:在中序遍历下,Node的后一个结点。在二叉搜索树中,Node的直接后继就是在它的右子树中关键码最小的结点。
Node的直接前驱:在中序遍历下,Node的前一个结点。在二叉搜索树中,Node的直接前驱就是在它的左子树中关键码最大的结点。
实际情况1可以与情况2或者3合并起来,删除结点就分为3种情况。
bool Erase(const K& key)
{
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_key < key)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_key > key)
{
parent = cur;
cur = cur->_left;
}
else
{
//1、左为空
if (cur->_left == nullptr)
{
if (cur == _root)
{
_root = cur->_right;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
}
delete cur;
}
//2、右为空
else if (cur->_right == nullptr)
{
if (cur == _root)
{
_root = cur->_left;
}
else
{
if (parent->_left == cur)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
}
delete cur;
}
//3、左、右都不为空
else
{
//找右子树最小结点替代,也可以是左树最大结点替代
Node* pminRight = cur;
Node* minRight = cur->_right;
while (minRight->_left)
{
pminRight = minRight;
minRight = minRight->_left;
}
cur->_key = minRight->_key;
if (pminRight->_left == minRight)
{
pminRight->_left = minRight->_right;
}
else
{
pminRight->_right = minRight->_right;
}
delete minRight;
}
return true;
}
}
return false;
}
插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能,查找效率,主要取决于树的高度。
对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
最好情况下,二叉搜索树的左、右子树的高度之差的绝对值不超过1(平衡二叉树),时间复杂度为O(logn)。
最坏情况下,二叉搜索树是一个只有右(左)孩子的单支树(类似于有序的单链表),时间复杂度为O(n)。
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
//AVL树结点
template
struct AVLTreeNode
{
AVLTreeNode* _left;
AVLTreeNode* _right;
AVLTreeNode* _parent;
pair _kv;
int _bf;//balance factor
AVLTreeNode(const pair& kv)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _kv(kv)
, _bf(0)
{}
};
//AVL树
template
class AVLTree
{
typedef AVLTreeNode Node;
public:
//插入
bool Insert(const pair& kv)
{
//……
}
//中序遍历
void InOrder()
{
_InOrder(_root);
cout << endl;
}
//是否平衡
bool IsBalance()
{
return _IsBalance(_root);
}
//高度
int Height()
{
return _Height(_root);
}
private:
//高度
int _Height(Node* root)
{
if (root == NULL)
return 0;
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
//是否平衡
bool _IsBalance(Node* root)
{
if (root == NULL)
{
return true;
}
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
if (rightH - leftH != root->_bf)
{
cout << root->_kv.first << "结点平衡因子异常" << endl;
return false;
}
return abs(leftH - rightH) < 2
&& _IsBalance(root->_left)
&& _IsBalance(root->_right);
}
//左单旋转
void RotateL(Node* parent)
{
//……
}
//右单旋转
void RotateR(Node* parent)
{
//……
}
//左右双旋转
void RotateLR(Node* parent)
{
//……
}
//右左双旋转
void RotateRL(Node* parent)
{
//……
}
//中序遍历
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_kv.first << " ";
_InOrder(root->_right);
}
//成员变量
Node* _root = nullptr;
};
AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么
AVL树的插入过程可以分为两步:
如果在一棵原本是平衡的AVL树中插入一个新结点,可能造成不平衡,此时必须调整树的结构,
使之平衡化。根据结点插入位置的不同,AVL树的旋转分为四种:
1. 新结点插入较高左子树的左侧——左左:右单旋
上图在插入前,AVL树是平衡的,新结点插入到30的左子树(注意:此处不是左孩子)中,30左
子树增加了一层,导致以60为根的二叉树不平衡,要让60平衡,只能将60左子树的高度减少一层,右子树增加一层,即将左子树往上提,这样60转下来,因为60比30大,只能将其放在30的右子树,而如果30有右子树,右子树根的值一定大于30,小于60,只能将其放在60的左子树,旋转完成后,更新结点的平衡因子即可。在旋转过程中,有以下几种情况需要考虑:
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* ppnode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subL;
}
else
{
ppnode->_right = subL;
}
subL->_parent = ppnode;
}
subL->_bf = parent->_bf = 0;
}
2. 新结点插入较高右子树的右侧——右右:左单旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* ppnode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (ppnode == nullptr)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subR;
}
else
{
ppnode->_right = subR;
}
subR->_parent = ppnode;
}
parent->_bf = subR->_bf = 0;
}
3. 新结点插入较高左子树的右侧——左右:先左单旋再右单旋
先对30进行左单旋,然后再对90进行右单旋。
新结点究竟是插入b还是插入c还是60本身就是新结点,不影响旋转过程。
void RotateLR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
int bf = subLR->_bf;
RotateL(parent->_left);
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;
}
else
{
assert(false);
}
}
4. 新结点插入较高右子树的左侧——右左:先右单旋再左单旋
void RotateRL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
int bf = subRL->_bf;
RotateR(parent->_right);
RotateL(parent);
if (bf == 1)
{
subR->_bf = 0;
parent->_bf = -1;
subRL->_bf = 0;
}
else if (bf == -1)
{
subR->_bf = 1;
parent->_bf = 0;
subRL->_bf = 0;
}
else if (bf == 0)
{
subR->_bf = 0;
parent->_bf = 0;
subRL->_bf = 0;
}
else
{
assert(false);
}
}
总结:
假如以parent为根的子树不平衡,即parent的平衡因子为2或者-2,分以下情况考虑:
1. parent的平衡因子为2,说明parent的右子树高,设parent的右子树的根为subR:
当subR的平衡因子为1时,执行左单旋;
当subR的平衡因子为-1时,执行右左双旋。
2. parent的平衡因子为-2,说明parent的左子树高,设parent的左子树的根为subL:
当subL的平衡因子为-1时,执行右单旋;
当subL的平衡因子为1时,执行左右双旋。
旋转完成后,原parent为根的子树个高度降低,已经平衡,不需要再向上更新。
bool Insert(const pair& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(kv);
if (parent->_kv.first > kv.first)
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
// 更新平衡因子
while (parent)
{
if (cur == parent->_right)
{
parent->_bf++;
}
else
{
parent->_bf--;
}
if (parent->_bf == 1 || parent->_bf == -1)
{
// 继续更新
parent = parent->_parent;
cur = cur->_parent;
}
else if (parent->_bf == 0)
{
break;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
// 需要旋转处理 -- 1、让这颗子树平衡 2、降低这颗子树的高度
if (parent->_bf == 2 && cur->_bf == 1)
{
RotateL(parent);
}
else if (parent->_bf == -2 && cur->_bf == -1)
{
RotateR(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
RotateLR(parent);
}
else if (parent->_bf == 2 && cur->_bf == -1)
{
RotateRL(parent);
}
else
{
assert(false);
}
break;
}
else
{
assert(false);
}
}
return true;
}
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或
Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路
径会比其他路径长出俩倍,因而是接近平衡的。
红黑树有如下性质:
enum Colour
{
RED,
BLACK,
};
template
struct RBTreeNode
{
RBTreeNode* _left;
RBTreeNode* _right;
RBTreeNode* _parent;
T _data;
Colour _col;
RBTreeNode(const T& data)
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _data(data)
, _col(RED)
{}
};
template
struct __RBTreeIterator
{
typedef RBTreeNode Node;
typedef __RBTreeIterator Self;
Node* _node;
__RBTreeIterator(Node* node)
:_node(node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
Self& operator++()
{
if (_node->_right)
{
// 1、右不为空,下一个就是右子树的最左节点
Node* subLeft = _node->_right;
while (subLeft->_left)
{
subLeft = subLeft->_left;
}
_node = subLeft;
}
return *this;
}
};
// 仿函数
template
class RBTree
{
typedef RBTreeNode Node;
public:
~RBTree()
{
_Destroy(_root);
_root = nullptr;
}
public:
typedef __RBTreeIterator itertaor;
typedef __RBTreeIterator const_itertaor;
itertaor begin()
{
Node* cur = _root;
while (cur && cur->_left)
{
cur = cur->_left;
}
return itertaor(cur);
}
itertaor end()
{
return itertaor(nullptr);
}
Node* Find(const K& key)
{
Node* cur = _root;
KeyOfT kot;
while (cur)
{
if (kot(cur->_data) < key)
{
cur = cur->_right;
}
else if (kot(cur->_data) > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
bool Insert(const T& data)
{
if (_root == nullptr)
{
_root = new Node(data);
_root->_col = BLACK;
return true;
}
KeyOfT kot;
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kot(cur->_data) < kot(data))
{
parent = cur;
cur = cur->_right;
}
else if (kot(cur->_data) > kot(data))
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(data);
if (kot(parent->_data) > kot(data))
{
parent->_left = cur;
}
else
{
parent->_right = cur;
}
cur->_parent = parent;
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// p u
// c
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
//parent->_col = RED;
grandfather->_col = RED;
}
break;
}
}
else // (grandfather->_right == parent)
{
// g
// u p
// c
Node* uncle = grandfather->_left;
// 情况1:u存在且为红,变色处理,并继续往上处理
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上调整
cur = grandfather;
parent = cur->_parent;
}
else // 情况2+3:u不存在/u存在且为黑,旋转+变色
{
// g
// u p
// c
if (cur == parent->_right)
{
RotateL(grandfather);
grandfather->_col = RED;
parent->_col = BLACK;
}
else
{
// g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
bool IsBalance()
{
if (_root && _root->_col == RED)
{
cout << "根节点颜色是红色" << endl;
return false;
}
int benchmark = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK)
++benchmark;
cur = cur->_left;
}
// 连续红色节点
return _Check(_root, 0, benchmark);
}
int Height()
{
return _Height(_root);
}
private:
void _Destroy(Node* root)
{
if (root == nullptr)
{
return;
}
_Destroy(root->_left);
_Destroy(root->_right);
delete root;
}
int _Height(Node* root)
{
if (root == NULL)
return 0;
int leftH = _Height(root->_left);
int rightH = _Height(root->_right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
bool _Check(Node* root, int blackNum, int benchmark)
{
if (root == nullptr)
{
if (benchmark != blackNum)
{
cout << "某条路径黑色节点的数量不相等" << endl;
return false;
}
return true;
}
if (root->_col == BLACK)
{
++blackNum;
}
if (root->_col == RED
&& root->_parent
&& root->_parent->_col == RED)
{
cout << "存在连续的红色节点" << endl;
return false;
}
return _Check(root->_left, blackNum, benchmark)
&& _Check(root->_right, blackNum, benchmark);
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* ppnode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (ppnode == nullptr)
{
_root = subR;
_root->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subR;
}
else
{
ppnode->_right = subR;
}
subR->_parent = ppnode;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* ppnode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (ppnode->_left == parent)
{
ppnode->_left = subL;
}
else
{
ppnode->_right = subL;
}
subL->_parent = ppnode;
}
}
private:
Node* _root = nullptr;
};