赞
踩
二叉搜索树虽然可以提高搜索效率,但如果数据接近有序的话搜索二叉树的效率退化为链表了。为了解决这个问题,提出了AVL树。
向平衡二叉树中插入新节点,保证每个节点的高度差的绝对值小于等于1。降低树的高度,提高搜索效率。这种树称为AVL树
为了确定插入或者删除节点后,这棵树的高度差的绝对值,这里给树的每一个节点引入平衡因子。
每个节点的平衡因子=这个节点的右子树高度-这个节点的左子树高度。
所以根据AVL树的定义:当平衡因子变为2或-2时,需要调整AVL树
情况一:右单旋(新节点插入较高左子树的左侧)
插入新节点后,b节点平衡因子为-1,a节点平衡因子为-2。
调整步骤:将a节点连接到b右节点,将原来b右子树连接到a左子树上,这样a,b节点的平衡因子又变回0
情况二:左单旋(新节点插入较高右子树的右侧)
插入新节点后,b节点平衡因子为1,a节点平衡因子为2
旋转思路:将bL连接到a的右树上,a再连接到b的左树上。调整平衡因子a,b平衡因子都变为0,调整a,b节点父指针,调整树的根节点,处理子树的情况,细节和右单旋类似。这样a,b节点的平衡因子又变回0
情况三:左右双旋(新节点插入较高左子树的右侧)
观察上图,相当于把c节点拆开c做根节点,c的左子树给b右树,c的右子树给a的左树。c的左子树连接b,c的右子树连接a。从而达到降低树的高度的目的。
根据上面的分析可知c在旋转后平衡因子一定为0.
如果新节点插入的位置在c的左子树上,经过旋转会到b的右子树上,b的平衡因子为0,a的平衡因子为1.
如果新节点插入的位置在c的右子树上,经过旋转会到a的左子树上,b的平衡因子为-1,a的平衡因子为0(如上图).
特殊情况:当b节点没有子树时,即这颗树只有cba这三个节点,此时a,b,c这三个节点的平衡因子都为0
情况四:右左双旋(新节点插入较高右子树的左侧)
由上图可以看到a节点的平衡因子为2,b节点的平衡因子为-1时要进行右左双旋。
特点为:b节点进行右旋,a节点进行左旋。
如果树高为h,最坏情况下,查找需要对比h次
平衡二叉树上任一结点的左子树和右子树的高度之差不超过1。
假设以n(h)表示深度为h的平衡树中含有的最少结点数。
n(0)=0此时是空树,n(1)=1此时树只有根节点,n(2)=2……
高度为h的平衡二叉树,最少节点个数为n(h)=n(h-1)+n(h-2)+1
所以9个节点的平衡二叉树i有四层,最坏需要对比4次就可以查找到对应元素
所以平衡二叉树的事件复杂度为树高O(logN)
AVL树的删除操作与插入类似;
AVL树的删除,关键就是平衡因子的调整。调整完毕后,根据平衡因子的值,选择旋转方式,并向上进行传递。
我这里使用:每次删除后,从删除根节点开始调整平衡因子,如果失去了AVL树形状,进行反转,之后依次向上递归调整。类似后续遍历的方式。
从整棵树的最下方从头开始调整。
这个方法有个弊端,就是每次删除必须向上判断平衡因子是否正确,效率比较低
BalanceTree类
#include <iostream> #include <vector> #include <algorithm> struct Node { int _bf; Node *_left; Node *_right; Node *_parent; int _val; Node(int val) : _bf(0), _left(nullptr), _right(nullptr), _parent(nullptr), _val(val) {} }; class BalanceTree { Node *root; public: ~BalanceTree() {} void InorderDisplay() { _DisPlay(root); std::cout << "\n"; } BalanceTree(const std::vector<int> buff) { for (auto &num : buff) { insert(num); } } bool insert(int val) { // 不允许值重复 if (root == nullptr) { root = new Node(val); return true; } else { Node *prev = nullptr; Node *cur = root; while (cur != nullptr) { prev = cur; if (cur->_val < val) { cur = cur->_right; } else if (cur->_val > val) { cur = cur->_left; } else { // 数据冗余 return false; } } // cur==nullptr cur = new Node(val); if (prev->_val > val) prev->_left = cur; else prev->_right = cur; cur->_parent = prev; // 更新平衡因子 while (prev != nullptr) { // 判断插入位置 if (prev->_left == cur) prev->_bf--; else if (prev->_right == cur) prev->_bf++; if (prev->_bf == 0) { break; // 插入后仍然是AVL树 } else if (prev->_bf == -1 || prev->_bf == 1) { cur = prev; prev = prev->_parent; // 这次插入影响高度,需要向上调整 } else if (prev->_bf == -2 || prev->_bf == 2) { // 不是AVL树需要调整高度 if (prev->_bf == -2) { if (cur->_bf == -1) { // 右单旋 TurnRight(prev); } else if (cur->_bf == 1) { // 左右双旋 TurnLeftRight(prev); } } else if (prev->_bf == 2) { if (cur->_bf == -1) { // 右左双旋 TurnRightLeft(prev); } else if (cur->_bf == 1) { // 左单旋 TurnLeft(prev); } } } } return true; } } // 判断一棵树是否是AVL树 bool isAVLTree() { return _isAVLTree(root); } void erase(int val) { _erase(val, root); _adjust(root); } private: void _adjust(Node *node) { // 后续遍历调整搜索二叉树为AVL树 if (node == nullptr) { return; } _adjust(node->_left); _adjust(node->_right); int leftHeight = _height(node->_left); int rightHeight = _height(node->_right); node->_bf = rightHeight - leftHeight; if (node->_bf == 2 || node->_bf == -2) { Node *left = node->_left; Node *right = node->_right; if (node->_bf == -2) { if ((left != nullptr && left->_bf == -1) || (right != nullptr && right->_bf == -1)) { // 右旋 TurnRight(node); } else if ((left != nullptr && left->_bf == 1) || (right != nullptr && right->_bf == 1)) { // 左右双旋 TurnLeftRight(node); } } else if (node->_bf == 2) { if ((left != nullptr && left->_bf == -1) || (right != nullptr && right->_bf == -1)) { // 右左旋 TurnRightLeft(node); } else if ((left != nullptr && left->_bf == 1) || (right != nullptr && right->_bf == 1)) { // 左旋 TurnLeft(node); } } } } // 搜索二叉树删除方式 void _erase(int val, Node *&node) { // 删除节点值为val的节点,并且返回删除后这个节点的指针 if (node == nullptr) { return; } else { if (node->_val > val) { _erase(val, node->_left); } else if (node->_val < val) { _erase(val, node->_right); } else { if (node->_left == nullptr) { Node *del = node; node = node->_right; if (node != nullptr) node->_parent = del->_parent; delete del; } else if (node->_right == nullptr) { Node *del = node; node = node->_left; if (node != nullptr) node->_parent = del->_parent; delete del; } else { // 找要删除节点的后继 Node *right_min_node = node->_right; while (right_min_node->_left != nullptr) { right_min_node = right_min_node->_left; } // 记录right_min_node这个节点的值,吧这个节点的值和node节点交换,在删除right_min_node这个节点即可 // right_min_node这个节点的左子树一定为空,在上面顶点流程会处理 int tmp = right_min_node->_val; _erase(tmp, node->_right); node->_val = tmp; } } } } void _DisPlay(Node *root) { if (root == nullptr) return; _DisPlay(root->_left); std::cout << root->_val << " "; _DisPlay(root->_right); } int _height(Node *node) { if (node == nullptr) return 0; int leftHeight = _height(node->_left); int rightHeight = _height(node->_right); return std::max(leftHeight, rightHeight) + 1; } bool _isAVLTree(Node *node) { if (node == nullptr) { return true; } int leftHigh = _height(node->_left); int rightHigh = _height(node->_right); if (node->_bf != rightHigh - leftHigh) { std::cout << "ERROR:BF " << node->_bf << std::endl; return false; } return std::abs(rightHigh - leftHigh) < 2 && _isAVLTree(node->_left) && _isAVLTree(node->_right); } /** * 旋转代码结合博客流程对照 */ // 右单旋 void TurnRight(Node *node) { Node *left = node->_left; Node *right = left->_right; node->_left = right; if (right != nullptr) { right->_parent = node; } left->_right = node; Node *parent = node->_parent; // 原父母 node->_parent = left; // 修改平衡因子 node->_bf = 0; left->_bf = 0; if (node == root) { // 旋转根节点 root = left; root->_parent = parent; } else { left->_parent = parent; if (parent->_left == node) { parent->_left = left; } else { parent->_right = left; } } } // 左单旋 void TurnLeft(Node *node) { Node *right = node->_right; Node *left = right->_left; node->_right = left; if (left != nullptr) { left->_parent = node; } right->_left = node; Node *parent = node->_parent; node->_parent = right; node->_bf = 0; right->_bf = 0; if (node == root) { node = right; right->_parent = parent; } else { right->_parent = parent; if (parent->_left == node) { parent->_left = right; } else { parent->_right = right; } } } // 左右双旋 void TurnLeftRight(Node *node) { Node *left = node->_left; Node *right = left->_right; TurnLeft(left); TurnRight(node); // 调整平衡因子 if (right->_bf == 1) { // 新节点插入到right节点的右子树 right->_bf = 0; left->_bf = -1; node->_bf == 0; } else if (right->_bf == -1) { // 新节点插入到right节点的左子树上 right->_bf = 0; left->_bf = 0; node->_bf = 1; } else if (right->_bf = 0) { right->_bf = 0; left->_bf = 0; node->_bf = 0; } } // 右左双旋 void TurnRightLeft(Node *node) { Node *right = node->_right; Node *left = right->_left; TurnRight(right); TurnLeft(node); if (left->_bf == 1) { left->_bf = 0; right->_bf = 0; node->_bf = -1; } else if (left->_bf == -1) { left->_bf = 0; right->_bf = 1; node->_bf = 0; } else if (left->_bf = 0) { left->_bf = 0; right->_bf = 0; node->_bf = 0; } } };
测试代码:
#include "BalanceTree.h" using namespace std; int main(int argc, char const *argv[]) { BalanceTree tree({3, 4, 2, 5, 6, 1, 7}); tree.InorderDisplay(); std::cout << tree.isAVLTree() << std::endl; tree.erase(4); tree.InorderDisplay(); std::cout << tree.isAVLTree() << std::endl; tree.erase(5); tree.InorderDisplay(); std::cout << tree.isAVLTree() << std::endl; return 0; }
运行结果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。