二叉树结构
即有一个指向左孩子的tree类型指针,指向右孩子的一个tree类型指针
二叉排序树也是采用这种结构,但排序树规定
中序遍历得到的结点值域是有序的
因而我们可以规定即对于一个平衡二叉树的根节点,左子树的值域小于双亲结点的值域,右子树的值域大于全亲结点的值域
由于我们使用二叉排序树时,如果一开始给二叉排序树插入的数组是有序的,那么对于二叉排序树来说,就变成了一个单支树。
如插入数组{1,2,3,4,5,6}
就变成了一个只有右子树的单支树
这样我们遍历查找的效率和链表一样,那么我们设置成二叉排序树的意义就不是很大了
二叉排序树的平均查找长度
最优log2(n)最差 n
二叉平衡排序树
又称AVL树。
AVL树的性质:
满足排序树的性质。
对于任意一个结点,左子树右子树的高度之差不超过1。
模拟实现AVL树:
AVL树的结点结构:
AVL树的应用场景:数据数量固定之后,需要进行大量读操作的场景。
如果要对其进行结构上的修改操作,由于需要维护树的平衡,就会需要对树进行旋转,性能会非常低下
模拟实现AVL的插入
对于插入,主要分为四种类型的插入
即LL型 LR型 RL型 RR型
LL型,则只进行右单旋,
LR型
RR型与LL型相对应,LR型与RL型相对应
AVL树插入的核心思想
1. 每次插入的时候先去根据AVL树的特性(如果value值出现相等,则插入失败(下面的代码默认是不插入相同结点的))去找待插入结点的双亲结点并插入。
2.循环向上更新平衡因子,如果更新到某一个双亲结点为0说明子树已平衡,对树整体没有影响,则插入成功,或者双亲结点为根节点且没有出现失衡现象,插入成功退出。否则找到问题结点记为ppa
3.在找问题结点的同时把问题结点的插如新节点路径的子节点找到记为cur,在找到cur新节点路径上的子节点pa,根据ppa,cur,pa的关系判断出为四种旋转类型的哪一种然后进行旋转
4.我们可以把LR新旋转拆分为先左旋再右旋,因此我们只需要写两个函数即可完成4种类型的旋转,而这两个函数要做的无疑使对问题结点和问题结点路径方向的子节点的平衡因子进行更新,由且更新前要判断传入的“问题结点”是否为真正的问题结点(如上图LR型旋转,我们向左旋函数传递的问题结点为B,子节点为C,然后在进行右旋,传入问题结点A,子节点C)旋转结束前,要记得判断原问题结点是否为根节点,如果是根节点,要更新根节点指针指向。
ps:当然对于四种旋转也可以分开写,但逻辑思想都是一样的,合在一起还实现的代码的复用。
具体代码如下:
template<class T>
struct AvltreeNode {
AvltreeNode<T>* _left;
AvltreeNode<T>* _right;
AvltreeNode<T>* _parent;//指向双亲
T _value;//结点值
int _bf;//平衡因子
AvltreeNode()
:_left(nullptr)
, _right(nullptr)
, _parent(nullptr)
,_bf(0)
{}
};
//上面定义了一个AVL结构体
//下面定义AVL树,要满足AVL树的要求;即树的左右高度差绝对值小于等于1
template<class T1>
class AvlTree {
public:
typedef AvltreeNode<T1> Node;
AvlTree():_root(nullptr)
{}
AvlTree(T1 arr[]) {
T1* p=arr;
if (p) {
insert(*p);
p++;
}
}
void insert(T1 value) {
if (nullptr == _root) {
_root = new Node;
_root->_value = value;
}
else
insert(_root, value);
}
int size() {
return getsize(_root);
}
bool empty() {
if (_root==nullptr)
return true;
else
return false;
}
void PrePrint() {
PrePrint(_root);
}
void Print() {
Print(_root);
}
void Destory() {
Destory(_root);
_root = nullptr;
}
~AvlTree() {
Destory();
}
private:
Node* _root;
int getsize(Node* root) {
if (nullptr==root )
return 0;
else
return (1 + (getsize(root->_left) + getsize(root->_right)));
}
void Destory(Node* root) {
if (nullptr == root) {
return;
}
Destory(root->_left);
Destory(root->_right);
delete root;
}
void Print(Node* root) {
if (root == nullptr)
return;
Print(root->_left);
cout << root->_value << " ";
Print(root->_right);
}
void PrePrint(Node * root) {
if (nullptr == root)
return;
cout <<" " << root->_value << "[" << root->_bf << "]";
PrePrint(root->_left);
PrePrint(root->_right);
}
void insert(Node* root, T1 value)
{ //先按照二叉排序树进行插入
if (nullptr == root)
{
return;
}
if(root->_value>value)
if(root->_left)
insert(root->_left, value);
else
{
Node* node = new Node;
root->_left = node;
node->_parent = root;
node->_value = value;
//更新全亲结点平衡因子
update(node);
return;
}
else {
if (root->_right)
insert(root->_right, value);
else {
Node* node = new Node;
root->_right = node;
node->_parent = root;
node->_value = value;
//更新平衡因子
update(node);
return;
}
}
}
void update(Node* pa) {
Node* ppa = pa->_parent;
Node* cur = pa;
while (ppa) {
if (ppa->_left == cur)
ppa->_bf--;
else if (ppa->_right == cur)
ppa->_bf++;
//else//帮助调试代码
//cout << "链接出现问题" << endl;
if (0 == ppa->_bf)//双亲结点平衡因子为0不会对整棵树产生影响了,因为没有增加树的深度
break;
else if (-1 == ppa->_bf || 1 == ppa->_bf) {
ppa = ppa->_parent;
cur = cur->_parent;
}
else if (-2 == ppa->_bf||2==ppa->_bf)//ppa现在是问题结点
//出现问题,即不满足AVL树,需要进行旋转
{
//cur为问题结点的子节点;
while (pa->_parent != cur) {
pa = pa->_parent;
}//此时pa为问题结点的子节点的子节点;
if (ppa->_left==cur)//即说明左侧高
{
if (cur->_left == pa)
//说明为LL型进行右单旋
{
LeftRev(ppa,cur);
break;
}
else if (cur->_right==pa)
//说明为LR型
{
RightRev(cur, pa);
LeftRev(ppa, pa);
break;
}
}
else if (ppa->_right==cur) {
if (cur->_left == pa)
//说明为RL型先左单旋,再右单旋
{
LeftRev(cur, pa);
RightRev(ppa, pa);
break;
}
else if (cur->_right == pa)
//说明为RR型,进行左单旋
{
RightRev(ppa, cur);
break;
}
}
}
}
}//右旋
void LeftRev(Node* ppa,Node* cur) {
Node* curR = cur->_right;
//记录cur的右孩子的根节点
cur->_right = ppa;
cur->_parent = ppa->_parent;
ppa->_parent = cur;
ppa->_left = curR;
if (curR) {
curR->_parent = ppa;
}
//调整平衡因子
if (ppa->_bf == -2 && cur->_bf == -1)
{
cur->_bf = 0;
ppa->_bf = 0;
}
//为了使LR和RL能够复用该函数,因而对ppa和cur的值进行了判断
//对于LR型的第一步
else if (ppa->_bf == -1)
{
if (cur->_bf == 1)//cur右侧高
{
cur->_bf = 2;
ppa->_bf = 0;
}
else if (cur->_bf == -1)
{
cur->_bf = 1;
ppa->_bf = 1;
}
else if (cur->_bf == 0)
{
cur->_bf = 1;
ppa->_bf = 0;
}
}
//对于RL型的第二步
else if (cur->_bf == -2) {
ppa->_bf = 1;
cur->_bf = 0;
}
//将调整后的问题子树与树进行链接
if (cur->_parent) {
Node* pppa = cur->_parent;
if (pppa->_left == ppa) {
pppa->_left = cur;
}
else if (pppa->_right == ppa)
{
pppa->_right = cur;
}
}
else
_root = cur;
}
void RightRev(Node* ppa, Node* cur) {
Node* curL = cur->_left;
cur->_left = ppa;
cur->_parent = ppa->_parent;
ppa->_parent = cur;
ppa->_right = curL;
if (curL) {
curL->_parent = ppa;
}
//调整平衡因子
if (2 == ppa->_bf && 1 == cur->_bf)
{
cur->_bf = 0;
ppa->_bf = 0;
}
//对于RL型第一步
else if (ppa->_bf == 1)
{
if (cur->_bf == 1) {
ppa->_bf = -1;
cur->_bf = -1;
}
else if (cur->_bf == -1)
{
ppa->_bf = 1;
cur->_bf = -2;
}
else {
ppa->_bf = 0;
cur->_bf = -1;
}
}
//对于LR型的第二步
else if (cur->_bf == 2) {
cur->_bf = 0;
ppa->_bf = -1;
}
//将调整后的问题子树与树进行链接
if (cur->_parent) {
Node* pppa = cur->_parent;
if (pppa->_left == ppa) {
pppa->_left = cur;
}
else if (pppa->_right == ppa)
{
pppa->_right = cur;
}
}
else
_root = cur;
}
};