目录
1.AVL树介绍:
- AVL树是一种自平衡二叉搜索树,它确保在进行插入和删除操作后,树的高度差(即平衡因子)不会超过1,从而维持了二叉搜索树的平衡。进而降低树的高度,以此来减少平均搜索长度。
- AVL树的时间复杂度为log(n);
2.节点介绍(平衡因子):
- AVL树一般包含左右子树指针,父节点指针,平衡因子,自身节点值。
- 平衡因子计算公式为:balanceFactor(N)=height(N.left)−height(N.right)。
- 新建的节点的平衡因子为0;
- 如果平衡因子为0,表示以当前节点为根节点的树的左右子树高度相同,节点是平衡的。
- 如果平衡因子为1,表示左子树比右子树高一层,节点是平衡的。
- 如果平衡因子为-1,表示右子树比左子树高一层,节点是平衡的。
- 如果平衡因子为2或-2,表示节点不平衡,需要通过旋转操作进行调整。
3.插入一个新节点,如何更新平衡因子?
- 按照二叉搜索树的规则将节点插入到AVL树中。可能会影响祖先,但是不会影响祖先的兄弟节点。如果新增的节点在左子树,高度差变小,父亲节点的平衡因子要--;如果新增的节点在右子树,高度差变大,父亲节点的平衡因子要++;
- 在更新平衡因子之后,如果父亲节点的平衡因子变为0,就不用向上更新了,插入完成;如果父亲的平衡因子变为1或者-1,父亲两个子树产生高度差,要继续向上更新平衡因子。当向上更新后某个祖先节点的平衡因子变为2或者-2,就要旋转调整AVL树。
4.插入的实现:
- AVL树要满足搜索二叉树性质,插入时要先从根节点向下找到合适的位置来插入新增节点。
bool Insert(const pair<K,V>& 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); cur->_parent = parent; if (parent->_kv.first < kv.first) { parent->_right = cur; } else { parent->_left = cur; } return true; } }
- 插入结点之后,开始更新平衡因子。更新之后根据新的平衡因子判断是否继续向上更新平衡因子。
- 如果平衡因子为0,无需再向上更新平衡因子。AVL树已经平衡。
- 如果平衡因子为 1/-1,继续向上更新平衡因子。
- 如果平衡因子为 2/-2,找到了不平衡的子树的根节点,开始翻转调整到平衡。调整后树的高度降低,无需再向上继续调整平衡因子。
while (parent)//极限情况是更行到根 { if (cur == parent->_left)//插在左树,高度差减小,平衡因子要从大于0趋近于0,平衡因子减小 parent->_bf--; else//插在右树,高度差增大,平衡因子增大 parent->_bf++; if (parent->_bf == 0)//如果更新后的父亲的平衡因子变为0,停止向上更新平衡因子 break; else if (parent->_bf == 1 || parent->_bf == -1)//如果平衡因子为1或-1,继续向上更新 { cur = parent; parent = cur->_parent; } else if (parent->_bf == 2 || parent->_bf == 2)//平衡因子为2或者-2的情况,翻转 { //处理单左旋情况 if (cur->_bf == 1 && parent->_bf == 2) { RoteL(parent); } //处理单右旋情况 else if (cur->_bf == -1 && parent->_bf == -2) { RoteR(parent); } //处理完旋转,就不需要再向上调整,直接跳出即可 break; } //如果平衡因子出现-2,-1,0,1,2以外的数,说明出错了 else { assert(false); } }
5.旋转调整AVL树:
5.1单左旋:
- 更新平衡因子之后,当parent的平衡因子为2,cur的平衡因子为1时,使用单左旋。
- 如上图,将b节点调整到值为30的节点的右子树,将值为60的节点作为新的父节点,将值为30的节点作为值为60的节点的左子树节点。其他不用调整。
void _RotateL(Node Parent) { Node* sub=parent->_right; Node* subL=sub->_left; parent->_right=subL; if(subL) subL->_parent=parent; Node* pp=parent->_parent; parent->_parent=sub; sub->_left=parent; if(_root==parent) { _root=sub; sub->_parent=nullptr; } else { if(pp->_left==parent) pp->_left=sub; else pp->_right=sub; sub->_parent=pp; } parent->_bf=sub->_bf=0; }
5.2单右旋:
- 更新平衡因子之后,当parent的平衡因子为-2,cur的平衡因子为-1时,使用单右旋。
void _RotateR(Node Parent) { Node* sub=parent->_left; Node* subR=sub->_right; parent->_left=subR; if(subR) subR->_parent=parent; Node* pp=parent->_parent; parent->_parent=sub; sub->_right=parent; if(_root==parent) { _root=sub; sub->_parent=nullptr; } else { if(pp->_left==parent) pp->_left=sub; else pp->_right=sub; sub->_parent=pp; } parent->_bf=sub->_bf=0; }
5.3右+左旋转:
- 更新平衡因子之后,当parent的平衡因子为2,cur的平衡因子为-1时,在cur右旋,然后在parent左旋。
void RoteRL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; int bf = subRL->_bf; RoteR(parent->_right);//在cur节点右旋 RoteL(parent);//在parent位置左旋 //旋转完成,对三个节点的平衡因子进行更新 if (bf == 0) { parent->_bf = subR->_bf = subRL->_bf = 0; } else if (bf == -1) { parent->_bf = 0; subRL->_bf = 0; subR->_bf = 1; } else if (bf == 1) { parent->_bf = -1; subR->_bf = 0; subRL->_bf = 0; } else { assert(false); } }
5.4左+右旋转:
- 更新平衡因子之后,当parent的平衡因子为-2,cur的平衡因子为1时,在cur左旋,然后在parent右旋。
void RoteLR(Node* parent) { Node* subL = parent->_left; Node* subLR = subR->_right; int bf = subLR->_bf; RoteL(parent->_left);//在cur节点左旋 RoteR(parent);//在parent位置右旋 //旋转完成,对三个节点的平衡因子进行更新 if (bf == 0) { parent->_bf = subL->_bf = subLR->_bf = 0; } else if (bf == -1) { parent->_bf = 1; subLR->_bf = 0; subL->_bf = 0; } else if (bf == 1) { parent->_bf = 0; subL->_bf = -1; subLR->_bf = 0; } else { assert(false); } }