> 文章列表 > C++语法(19)---- 模拟AVL树

C++语法(19)---- 模拟AVL树

C++语法(19)---- 模拟AVL树

C++语法(18)---- set和map_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/130228232?spm=1001.2014.3001.5501

 目录

1.AVL树的概念

2.节点定义

3.AVL树的类实现

1.类定义

2.insert

1.全代码实现

2.思考角度

3.平衡因子的情况

4.调节平衡的旋转

3.Print

4.Check

1. 验证其为二叉搜索树

2. 验证其为平衡树


1.AVL树的概念

1.普通搜索二叉树的插入可能会出现退化变为单支树,这样查找的效率就变为了O(N)

2.AVL树是当插入新的节点时,保证每个节点的左右子树的高度差绝对值不超过1

3.AVL树的平衡实现是通过调整高度得到的

特征

1.左右子树也是AVL树

2.左右高度差绝对值(简称平衡因子,右子树高度-左子树高度)不超过1

2.节点定义

template<class K,class V>
struct AVLTreeNode
{pair<K,V> _kv;AVLTreeNode* _left;AVLTreeNode* _right;AVLTreeNode* _parent;int _bf;AVLTreeNode(const pair<K, V>& kv):_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _bf(0){}
};

1.搜索二叉树实现只传入让K,V直接当数据

2.AVL树中使用pair来进行合并K和V数据

3.节点为三叉节点,指向孩子和父亲节点

4._bf为平衡因子,这样直观表示是否平衡,根据因子进行调整

5.要记得初始化节点,这样下面实现才能用

3.AVL树的类实现

1.类定义

template<class K, class V>
class AVLTree
{typedef AVLTreeNode<K,V> Node;
private:Node* _root = nullptr;
};

2.insert

1.先跟搜索二叉树的实现一样,插入到对应的位置

2.更新平衡因子

3.通过因子判断是否进行平衡调节 

1.全代码实现

bool Insert(const pair<K, V>& kv)
{//1.先跟搜索二叉树的实现一样,插入到对应的位置if (_root == nullptr){_root = new Node(kv);return true;}//父子节点确定插入的位置Node* parent = nullptr;Node* cur = _root;while (cur){//比cur要小,往cur的左边走if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}//比cur要大,往cur的右边走else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}//相同,不插入elsereturn false;}//走到这cur就是要插入的位置//cur要连接parent,parent也要连接cur---判断靠kv的大小cur = new Node(kv);if (parent->_kv.first > cur->_kv.first){parent->_left = cur;cur->_parent = parent;}else{parent->_right = cur;cur->_parent = parent;}//2.更新平衡因子while (parent){//左减右加if (parent->_left == cur){parent->_bf--;}else if (parent->_right == cur){parent->_bf++;}//情况一_bf如果等于0,说明之前是 1/-1,说明现在加进去不会影响原来的平衡,退出就行if (parent->_bf == 0)break;//如果等于1/-1,说明之前没有树,加进去就多一个高度 -- 判断祖先节点的_bf(通过循环)else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;parent = parent->_parent;}//如果等于2/-2,说明这个节点下面要调整else if (parent->_bf == 2 || parent->_bf == -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;
}

2.思考角度

思想的前提:不要认为这个算法是用来使得已有搜索二叉树变平衡,而是加一个节点进行调节每一次加入该树都是平衡搜索二叉树。

3.平衡因子的情况

情况一子树的高度不变,上面的也不会影响

情况二可能会更新到跟节点,所以我们可能需要持续的更新,所以需要向上检测

4.调节平衡的旋转

1.旋转的目的

1.必须将这颗子树变成左右高度差绝对值不超过1

2.不允许其他位置的高度

3.更新平衡因子

4.降低子树的高度

2.旋转的情况

1.左单旋

60右边变成30左边,60左边变成30;60和30的平衡因子变为0

void _RotateL(Node*& parent)
{Node* pparent = parent->_parent;Node* SubR = parent->_right;Node* SubRL = SubR->_left;if (pparent == nullptr){_root = SubR;SubR->_parent = nullptr;}else{if (pparent->_left == parent)pparent->_left = SubR;elsepparent->_right = SubR;SubR->_parent = pparent;}parent->_parent = SubR;SubR->_left = parent;parent->_right = SubRL;if (SubRL != nullptr)SubRL->_parent = parent;parent->_bf = 0;SubR->_bf = 0;
}

2.右单旋

void _RotateR(Node*& parent)
{Node* pparent = parent->_parent;Node* SubL = parent->_left;Node* SubLR = SubL->_right;if (pparent == nullptr){_root = SubL;SubL->_parent = nullptr;}else{if (pparent->_left == parent)pparent->_left = SubL;elsepparent->_right = SubL;SubL->_parent = pparent;}parent->_parent = SubL;SubL->_right = parent;parent->_left = SubLR;if (SubLR != nullptr)SubLR->_parent = parent;parent->_bf = 0;SubL->_bf = 0;
}

 3.先右单旋再左单旋

虽说右单旋再左单旋就完成了对应的结构,但这些节点的平衡因子是被打乱的;

从结果上看,其实就是将最60的左右子树平分给它父节点和“爷爷”节点

60的平衡因子不管怎么样都是0是确定的

但是另外两个节点平衡因子是看原先60的平衡因子:

b为h的话,30平衡因子为0,90的平衡因子为1

c为h的话,30平衡因子为-1,90的平衡因子为0

因此,我们需要一个临时平衡因子tmp_bf存储60位置的平衡因子

60是新增节点,那么30和90的平衡因子都是0了

60的子数是新增,那需要看这个临时因子

void _RotateRL(Node*& parent)
{Node* SubR = parent->_right;Node* SubRL = SubR->_left;int tmp_bf = 0;if (SubRL->_left == nullptr && SubRL->_right == nullptr)tmp_bf = 0;elsetmp_bf = SubRL->_bf;_RotateR(SubR);_RotateL(parent);if (tmp_bf == 0)parent->_bf = SubRL->_bf = 0;else if (tmp_bf == -1){parent->_bf = 0;SubR->_bf = 1;}else{parent->_bf = -1;SubR->_bf = 0;}SubRL->_bf = 0;
}

4.先左单旋再右单旋

void _RotateLR(Node*& parent)
{Node* SubL = parent->_left;Node* SubLR = SubL->_right;int tmp_bf = 0;if (SubLR->_left == nullptr && SubLR->_right == nullptr)tmp_bf = 0;elsetmp_bf = SubLR->_bf;_RotateL(SubL);_RotateR(parent);if (tmp_bf == 0)parent->_bf = SubLR->_bf = 0;else if(tmp_bf == -1){parent->_bf = 1;SubL->_bf = 0;}else{parent->_bf = 0;SubL->_bf = -1;}SubLR->_bf = 0;
}

3.Print

搜索二叉树顺序打印是通过中序遍历得到的

void Print()
{_Print(_root);
}private:
void _Print(Node*& cur)
{if (cur == nullptr)return;_Print(cur->_left);cout << cur->_kv.first << endl;_Print(cur->_right);
}

4.Check

1. 验证其为二叉搜索树

如果中序遍历可得到一个有序的序列,就说明为二叉搜索树

2. 验证其为平衡树

每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)
节点的平衡因子是否计算正确

bool Check()
{return _Check(_root, 0);
}private:
int Height(Node* root)
{if (root == nullptr)return 0;int Left = 1 + Height(root->_left);int Right = 1 + Height(root->_right);return (Left > Right ? Left : Right);
}bool _Check(Node* root,int num)
{if (root == nullptr)return true;int leftHeight = Height(root->_left);int rightHeight = Height(root->_right);int diff = rightHeight - leftHeight;if (diff != root->_bf){cout << "_bf false";return false;}if (diff < -1 || diff > 1){cout << "diff false";return false;}return _Check(root->_left, leftHeight) && _Check(root->_right, rightHeight);
}