目录

1.AVL树的相关概念

2.平衡因子

3.平衡二叉搜索树节点的定义

4.插入时失衡及其调整

5.AVL树的四种旋转方式

6.AVL树的插入

7.AVL树的删除

8.其他相关函数


1.AVL树的相关概念

二叉搜索树在一定程度上可以提高搜索效率,但是当序列是有序时:如果所示

此时二叉搜索树退化成单链表,搜索效率退化为O(N)。为了解决这个问题科学家引入了AVL树,又称平衡搜索二叉树

AVL简称平衡二叉树。由前苏联的数学家 Adelse-Velskil 和 Landis 在 1962 年提出的高度平衡的二叉树,根据科学家的英文名也称为 AVL 树。它具有如下几个性质:

  1. 可以是空树。
  2. 假如不是空树,任何一个结点的左子树与右子树都是平衡二叉树,并且高度之差的绝值不超过 1。

平衡之意,如天平,即两边的分量大约相同。

非平衡搜索二叉树

平衡搜索二叉树:

2.平衡因子

定义:某节点的左子树与右子树的高度(深度)差即为该节点的平衡因子(BF,Balance Factor),平衡二叉树中不存在平衡因子大于 1 的节点。在一棵平衡二叉树中,节点的平衡因子只能取 0 、1 或者 -1 ,分别对应着左右子树等高,左子树比较高,右子树比较高。

3.平衡二叉搜索树节点的定义

struct AVLTreeNode {AVLTreeNode(const pair<K,V>&kv):_left(nullptr),_right(nullptr),_kv(kv),h(1)//一个节点的高度为1{}AVLTreeNode<K, V>* _left;//左指针AVLTreeNode<K, V>* _right;//右指针pair<K, V> _kv;int h;.//高度};

4.插入时失衡及其调整

在平衡搜索二叉树中插入一个节点1

树结构变为:

此时3和4这个节点失去平衡,在这里3

最小失衡树:在新插入的结点向上查找,以第一个平衡因子的绝对值超过 1 的结点为根的子树称为最小不平衡子树。也就是说,一棵失衡的树,是有可能有多棵子树同时失衡的。而这个时候,我们只要调整最小的不平衡子树,就能够将不平衡的树调整为平衡的树。

AVL树种解决失衡问题通过旋转最小失衡树来使整棵树达到平衡,旋转的目的就是减少高度,通过降低整棵树的高度来平衡。哪边的树高,就把那边的树向上旋转。

5.AVL树的四种旋转方式

5.1左旋(处理LL型违规)

5.2右旋(处理RR型违规)

5.3左右双旋(处理LR型违规)

5.4右左双旋(处理RL型违规)

5.1右旋

当插入节点1时树的结构变为:

此时树的平衡性被破坏此时从插入节点往上查,发现3这个节点不平衡原因是它的左树的高度太高,因此我们只需要对3这个节点进行右旋。

右旋的基本步骤:

(1)节点的左孩子代表此节点

(2)节点的左孩子的右子树变为节点的左子树

(3)将此节点作为左孩子节点的右子树。

对应代码:

    Node* rightRotate(Node* cur) {Node* left = cur->_left;cur->_left = left->_right;left->_right = cur;cur->h = max((cur->_left != nullptr ? cur->_left->h : 0), (cur->_right != nullptr ? cur->_right->h : 0)) + 1;//更新高度left->h = max((left->_left != nullptr ? left->_left->h : 0), (left->_right != nullptr ? left->_right->h : 0)) + 1;//更新高度return left;//返回新的头}

左单旋:

(1)节点的右孩子替代此节点位置

(2)右孩子的左子树变为该节点的右子树

(3)节点本身变为右孩子的左子树

T,S,R均为抽象模型,代表对应的子树数字代表其高度。当我们在这颗树上插入节点10时,会使得8这个节点不平衡,此时是他的右树不平衡,此时我们只需要对8这个节点进行左单旋

对应代码:

    Node* leftRotate(Node* cur) {Node* rightNode = cur->_right;cur->_right = rightNode->_left;rightNode->_left = cur;cur->h = max((cur->_left != nullptr ? cur->_left->h : 0), (cur->_right != nullptr ? cur->_right->h : 0)) + 1;//更新高度rightNode->h = max((rightNode->_left != nullptr ? rightNode->_left->h : 0), (rightNode->_right != nullptr ? rightNode->_right->h : 0)) + 1;//更新高度return rightNode;//返回新头部}

双旋

在上述过过程中的旋转都是直线过来的,因此经过一次旋转就可以使其达到平衡。但是如果是折线过来又改怎么旋转呢。

外面的数字代表树的高度,字母代表子树。在这里10这个节点的平衡性被破坏,但此时是因为10这个节点的左树的右树太高导致的。所以我们只要先对7进行左旋在对10进行右旋即可

注意当同时满足LL型违规又满足LR型违规此时只能进行右旋而不能进行左右双旋。我们来看这个例子

此时整棵树既是LL型违规又是LR型违规,此时我们若只对10进行一个右旋是可以使整棵树带到平衡的。如图所示

但是如果我们使用LR(左右双旋)不能使整棵树达到平衡,如图所示:

旋转完成之后我们发现树并不平衡

右左双旋:

先对7进行右旋在对 10进行左旋

对应调整代码:

Node* maintain(Node* cur) {if (cur == nullptr) {return nullptr;}int leftHeight = cur->_left != nullptr ? cur->_left->h : 0;//计算出cur左树的高度int rightHeight = cur->_right != nullptr ? cur->_right->h : 0;//计算出cur右树的高度if (abs(leftHeight - rightHeight) > 1) {//出现不平衡if (leftHeight > rightHeight) {//如果是左树高//把左树的左右子树的高度来出来比较看到底是左边高还是右边高int leftLeftHeight = cur->_left != nullptr && cur->_left->_left != nullptr ? cur->_left->_left->h : 0;int leftRightHeight = cur->_left != nullptr && cur->_left->_right != nullptr ? cur->_left->_right->h : 0;if (leftLeftHeight >= leftRightHeight) {//注意想等时只能右旋cur = rightRotate(cur);}else {//左右双旋cur->_left = leftRotate(cur->_left);cur = rightRotate(cur);}}else {int rightLeftHeight = cur->_right != nullptr && cur->_right->_left != nullptr ? cur->_right->_left->h: 0;int rightRightHeight = cur->_right != nullptr && cur->_right->_right != nullptr ? cur->_right->_right->h : 0;if (rightRightHeight >= rightLeftHeight) {cur = leftRotate(cur);}else {//右左双旋cur->_right = rightRotate(cur->_right);cur = leftRotate(cur);}}}return cur;//返回调整好的新头}

6.AVL树的插入

AVL树的插入和搜索二叉树的插入基本一样,与搜索二叉树不同的是:AVL树插入之后要从插入的节点往上查它的祖先是否出现不平衡

对应代码:

ode* add(Node* cur, const pair<K, V>& kv){if (cur == nullptr) {//空树直接new出节点并返回return new Node(kv);}else {if (cur->_kv.first < kv.first) {//比当前节点大cur->_right = add(cur->_right, kv);//去cur的右边插入并将新的头部返回}else {cur->_left = add(cur->_left, kv);//去cur的左边插入并将新的头部返回}cur->h = max(cur->_left != nullptr ? cur->_left->h : 0, cur->_right != nullptr ? cur->_right->h : 0)+1;//重新计算高度return maintain(cur);//对cur这个树进行调整}}bool insert(const pair<K, V>& kv) {Node* lastNode = findLastIndex(kv.first);if (lastNode && lastNode->_kv.first == kv.first) {//已经存在插入失败return false;}else {_size++;_root = add(_root, kv);return true;}}

7.AVL树的删除

AVL树的删除和搜索二叉树基本一样,只不过比搜索二叉树多了平衡性的调整

删除一共有五种情况:
第一种情况:没找到删除的节点,遍历到空节点直接返回了
(找到删除的节点)
第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点

第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点

第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点

第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。

但上述的第二种情况可以归类成第三种情况或者第四种情况,我们删除的原则是删除节点之后它还是一颗搜索二叉树。

删除叶子节点:

只有左孩子:

只有右孩子

既有左孩子又有右孩子

对应代码:

Node* Delete(Node* cur, K key) {if (cur->_kv.first < key) {cur->_right = Delete(cur->_right, key);//去我的右边删除并且返回新的头部}else if (cur->_kv.first > key) {cur->_left = Delete(cur->_left, key);//去我的左边删除并返回新的头部}else {if (cur->_left==nullptr && cur->_right==nullptr) {//叶子节点左右都为空delete cur;return nullptr;}else if (!cur->_left && cur->_right) {//左为空但右不为空Node* subR = cur->_right;delete cur;cur = subR;}else if (cur->_left && !cur->_right) {//右为空但左不为空Node* subL = cur->_left;delete cur;cur = subL;}else {//左右都不为空采用替换法删除既采用值的方式替代Node* des = cur->_right;//找到右树最左节点或者找到左树的最右节点都可以while (des->_left){des = des->_left;}pair<K, V>Kv = des->_kv;//记录对应的值cur->_right = Delete(cur->_right, des->_kv.first);//找到我先不删调用Delete 将des删掉并将树调整好cur->_kv = Kv;//替换}}if (cur) {//重新计算高度cur->h = max(cur->_left ? cur->_left->h : 0, cur->_right ? cur->_right->h : 0) + 1;}return maintain(cur);//调整cur这颗树}void Erase(K key) {if (_root == nullptr) {return;}if (containsKey(key)) {--_size;_root = Delete(_root, key);}}//查找树中是否有key的这个节点public bool containsKey(K key) {if(_root==nullptr) {return false;}Node* lastNode = findLastIndex(key);return lastNode && key==lastNode->_kv.first ? true : false;}

8.其他相关函数

//找到最离key最近的节点Node* findLastIndex(K key) {Node* pre = _root;//记录前一个节点Node* cur = _root;while (cur) {pre = cur;if (cur->_kv.first == key) {break;}else if (cur->_kv.first > key) {cur = cur->_left;}else {cur = cur->_right;}}return pre;}//找到离key最近的最小节点Node* findLastNoSmallIndex(K key) {Node* ans = nullptr;Node* cur = _root;while (cur) {if (cur->_kv.first == key) {ans = cur;break;}else if (cur->_kv.first > key) {ans = cur;cur = cur->_left;}else {cur = cur->_right;}}return ans;}//找到离key最近的最大节点Node* findLastNoBigIndex(K key) {Node* ans = nullptr;Node* cur = _root;while (cur) {if (cur->_kv.first == key) {ans = cur;break;}else if (cur->_kv.first > key) {cur = cur->_left;}else {ans = cur;cur = cur->_right;}}return ans;}//查找树中是否有key的这个节点public bool containsKey(K key) {if(_root==nullptr) {return false;}Node* lastNode = findLastIndex(key);return lastNode && key==lastNode->_kv.first ? true : false;}int maxDepth(Node* root)//求高度{if (root == NULL){return 0;}int leftDepth = maxDepth(root->_left);int rightDepth = maxDepth(root->_right);return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;}bool _isBalanced(Node* root)//判断是否平衡{if (root == nullptr){return true;}int leftHight = maxDepth(root->_left);int rightHight = maxDepth(root->_right);return abs(leftHight - rightHight) < 2&& _isBalanced(root->_left)&& _isBalanced(root->_right);}bool isBalanced(){return _isBalanced(_root);}void _Inorder(Node* root) {//中序遍历if (!root)return;_Inorder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_Inorder(root->_right);}void Inorder() {_Inorder(_root);}size_t size() {return _size;}

对应总代码:

#include<iostream>
using namespace std;template<class K,class V>struct AVLTreeNode {AVLTreeNode(const pair<K,V>&kv):_left(nullptr),_right(nullptr),_kv(kv),h(1)//一个节点的高度为1{}AVLTreeNode<K, V>* _left;//左指针AVLTreeNode<K, V>* _right;//右指针pair<K, V> _kv;int h;//高度};template<class K, class V>class AVLTree {public:typedef struct AVLTreeNode<K, V> Node;AVLTree(int size = 0, Node* root = nullptr):_size(size), _root(root){}Node* add(Node* cur, const pair<K, V>& kv){if (cur == nullptr) {//空树直接new出节点并返回return new Node(kv);}else {if (cur->_kv.first < kv.first) {//比当前节点大cur->_right = add(cur->_right, kv);//去cur的右边插入并将新的头部返回}else {cur->_left = add(cur->_left, kv);//去cur的左边插入并将新的头部返回}cur->h = max(cur->_left != nullptr ? cur->_left->h : 0, cur->_right != nullptr ? cur->_right->h : 0)+1;//重新计算高度return maintain(cur);//对cur这个树进行调整}}bool insert(const pair<K, V>& kv) {Node* lastNode = findLastIndex(kv.first);if (lastNode && lastNode->_kv.first == kv.first) {return false;}else {_size++;_root = add(_root, kv);return true;}}Node* Delete(Node* cur, K key) {if (cur->_kv.first < key) {cur->_right = Delete(cur->_right, key);//去我的右边删除并且返回新的头部}else if (cur->_kv.first > key) {cur->_left = Delete(cur->_left, key);//去我的左边删除并返回新的头部}else {if (cur->_left==nullptr && cur->_right==nullptr) {//叶子节点左右都为空delete cur;return nullptr;}else if (!cur->_left && cur->_right) {//左为空但右不为空Node* subR = cur->_right;delete cur;cur = subR;}else if (cur->_left && !cur->_right) {//右为空但左不为空Node* subL = cur->_left;delete cur;cur = subL;}else {//左右都不为空采用替换法删除既采用值的方式替代Node* des = cur->_right;//找到右树最左节点或者找到左树的最右节点都可以while (des->_left){des = des->_left;}pair<K, V>Kv = des->_kv;//记录对应的值cur->_right = Delete(cur->_right, des->_kv.first);//找到我先不删调用Delete 将des删掉并将树调整好cur->_kv = Kv;//替换}}if (cur) {//重新计算高度cur->h = max(cur->_left ? cur->_left->h : 0, cur->_right ? cur->_right->h : 0) + 1;}return maintain(cur);//调整cur这颗树}void Erase(K key) {if (_root == nullptr) {return;}if (containsKey(key)) {--_size;_root = Delete(_root, key);}}//找到最离key最近的节点Node* findLastIndex(K key) {Node* pre = _root;//记录前一个节点Node* cur = _root;while (cur) {pre = cur;if (cur->_kv.first == key) {break;}else if (cur->_kv.first > key) {cur = cur->_left;}else {cur = cur->_left;}}return pre;}//找到离key最近的最小节点Node* findLastNoSmallIndex(K key) {Node* ans = nullptr;Node* cur = _root;while (cur) {if (cur->_kv.first == key) {ans = cur;break;}else if (cur->_kv.first > key) {ans = cur;cur = cur->_left;}else {cur = cur->_right;}}return ans;}//找到离key最近的最大节点Node* findLastNoBigIndex(K key) {Node* ans = nullptr;Node* cur = _root;while (cur) {if (cur->_kv.first == key) {ans = cur;break;}else if (cur->_kv.first > key) {cur = cur->_left;}else {ans = cur;cur = cur->_right;}}return ans;}//查找树中是否有key的这个节点public bool containsKey(K key) {if(_root==nullptr) {return false;}Node* lastNode = findLastIndex(key);return lastNode && key==lastNode->_kv.first ? true : false;}int maxDepth(Node* root)//求高度{if (root == NULL){return 0;}int leftDepth = maxDepth(root->_left);int rightDepth = maxDepth(root->_right);return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;}bool _isBalanced(Node* root)//判断是否平衡{if (root == nullptr){return true;}int leftHight = maxDepth(root->_left);int rightHight = maxDepth(root->_right);return abs(leftHight - rightHight) < 2&& _isBalanced(root->_left)&& _isBalanced(root->_right);}bool isBalanced(){return _isBalanced(_root);}void _Inorder(Node* root) {//中序遍历if (!root)return;_Inorder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_Inorder(root->_right);}void Inorder() {_Inorder(_root);}size_t size() {return _size;}Node* rightRotate(Node* cur) {Node* left = cur->_left;cur->_left = left->_right;left->_right = cur;cur->h = max((cur->_left != nullptr ? cur->_left->h : 0), (cur->_right != nullptr ? cur->_right->h : 0)) + 1;//更新高度left->h = max((left->_left != nullptr ? left->_left->h : 0), (left->_right != nullptr ? left->_right->h : 0)) + 1;//更新高度return left;//返回新的头}Node* leftRotate(Node* cur) {Node* rightNode = cur->_right;cur->_right = rightNode->_left;rightNode->_left = cur;cur->h = max((cur->_left != nullptr ? cur->_left->h : 0), (cur->_right != nullptr ? cur->_right->h : 0)) + 1;//更新高度rightNode->h = max((rightNode->_left != nullptr ? rightNode->_left->h : 0), (rightNode->_right != nullptr ? rightNode->_right->h : 0)) + 1;//更新高度return rightNode;//}Node* maintain(Node* cur) {if (cur == nullptr) {return nullptr;}int leftHeight = cur->_left != nullptr ? cur->_left->h : 0;//计算出cur左树的高度int rightHeight = cur->_right != nullptr ? cur->_right->h : 0;//计算出cur右树的高度if (abs(leftHeight - rightHeight) > 1) {//出现不平衡if (leftHeight > rightHeight) {//如果是左树高//把左树的左右子树的高度来出来比较看到底是左边高还是右边高int leftLeftHeight = cur->_left != nullptr && cur->_left->_left != nullptr ? cur->_left->_left->h : 0;int leftRightHeight = cur->_left != nullptr && cur->_left->_right != nullptr ? cur->_left->_right->h : 0;if (leftLeftHeight >= leftRightHeight) {//注意想等时只能右旋cur = rightRotate(cur);}else {//左右双旋cur->_left = leftRotate(cur->_left);cur = rightRotate(cur);}}else {int rightLeftHeight = cur->_right != nullptr && cur->_right->_left != nullptr ? cur->_right->_left->h: 0;int rightRightHeight = cur->_right != nullptr && cur->_right->_right != nullptr ? cur->_right->_right->h : 0;if (rightRightHeight >= rightLeftHeight) {cur = leftRotate(cur);}else {//右左双旋cur->_right = rightRotate(cur->_right);cur = leftRotate(cur);}}}return cur;//返回调整好的新头}private:Node* _root = nullptr;int _size = 0;};

对应迭代代码:

由于迭代不能和递归一样向上返回因此迭代中加了一个父亲指针和平衡因子,老铁可以自己下去研究,思路一样的。

#pragma once
using namespace std;
#include <iostream>
#include<queue>
template<class K, class V>
struct AVLTreeNode
{AVLTreeNode(const pair<K, V>& kv = pair<K, V>()): _left(nullptr), _right(nullptr), _parent(nullptr), _bf(0), _kv(kv){}AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;int _bf;//balance factor 平衡因子pair<K, V> _kv;
};template<class K, class V>
class AVLTree
{
public:typedef struct AVLTreeNode<K, V>  Node;//右单旋void RotateR(Node* parent){Node* cur = parent->_left;Node* curR = cur->_right;//cur的右子树Node* pparent = parent->_parent;//保存parent的父亲节点//将cur右子树链接到parent的左侧parent->_left = curR;if (curR)curR->_parent = parent;//将parent连接到cur的右侧cur->_right = parent;parent->_parent = cur;//将cur与pparent链接起来if (pparent == nullptr)//cur变成新的根{_root = cur;cur->_parent = nullptr;}else//pparent不为根{cur->_parent = pparent;if (parent == pparent->_left)//parent在父亲节点的左侧{pparent->_left = cur;}else{pparent->_right = cur;}}//平衡因子更新parent->_bf = 0;cur->_bf = 0;}//左单旋void RotateL(Node* parent)//左旋{Node* cur = parent->_right;//右变高,不可能为空Node* curL = cur->_left;Node* pprent = parent->_parent;//curL连接到parent上parent->_right = curL;if (curL)curL->_parent = parent;//parent连接到cur上cur->_left = parent;parent->_parent = cur;//cur链接到pprent上if (pprent == nullptr)//根{_root = cur;cur->_parent = nullptr;}else//不为根{cur->_parent = pprent;//判断链接在哪一侧if (pprent->_left == parent){pprent->_left = cur;}else{pprent->_right = cur;}}//平衡因子的更新parent->_bf = 0;cur->_bf = 0;}//左右双旋void RotateLR(Node* parent){Node* cur = parent->_left;Node* curR = cur->_right;//此时不可能为空,因为右子树高int bf = curR->_bf;//保存一份平衡因子RotateL(cur);//先左旋RotateR(parent);//再右旋//左旋、右旋会将平衡因子全部处理成0,因此要对平衡因子进行更改if (bf == 1)//在curR的右侧插入{curR->_bf = 0;cur->_bf = -1;parent->_bf = 0;}else if (bf == -1)//在curR左侧插入{curR->_bf = 0;cur->_bf = 0;parent->_bf = 1;}}//右左双旋void RotateRL(Node* parent){Node* cur = parent->_right;Node* curL = cur->_left;int bf = curL->_bf;RotateR(cur);//先右旋RotateL(parent);//再左旋//平衡因子出来if (bf == 1)//在subRL右侧插入时{curL->_bf = 0;parent->_bf = -1;cur->_bf = 0;}else if (bf == -1)//在左侧插入时{curL->_bf = 0;parent->_bf = 0;cur->_bf = 1;}}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 (kv.first < cur->_kv.first)//K值比较,小于往左边走{parent = cur;cur = cur->_left;}else if (kv.first > cur->_kv.first)//往右走{parent = cur;cur = cur->_right;}else//相等,不进行插入{return false;}}//此时已经找到插入的位置了,判断插入在parent的左边还是右边cur = new Node(kv);if (parent->_kv.first > kv.first)//插在左边{parent->_left = cur;cur->_parent = parent;//三叉链,cur父指针回指}else//插在右边{parent->_right = cur;cur->_parent = parent;//三叉链,cur父指针回指}//更新平衡因子while (parent)//不为空{if (parent->_left == cur)//cur在parent左侧{parent->_bf--;}else//cur在parent右侧{parent->_bf++;}if (parent->_bf == 0)//当前树是平衡的,停止更新break;else if (parent->_bf == 1 || parent->_bf == -1)//继续往上面走{cur = parent;parent = parent->_parent;}else if (parent->_bf == 2 || parent->_bf == -2)//需要进行旋转处理{if (parent->_bf == -2)//左边高{if (cur->_bf == -1)//是在当前节点的左侧插入了节点 ->右单旋{RotateR(parent);}else//cur->_bf=1 ->曲线影响,需要进行左右双旋{RotateLR(parent);}}else//右边高{if (cur->_bf == 1)//在当前节点的右侧插入了节点 ->  左单旋{RotateL(parent);}else//cur->_bf=-1 曲线影响{RotateRL(parent);}}break;//旋转过后当前的树就是平衡的了,退出}else//0 1 2 -> 不可能走到这一步,走到这里说明发生了逻辑错误{exit(-1);}}return true;}//删除函数bool Erase(const K& key){//用于遍历二叉树Node* parent = nullptr;Node* cur = _root;//用于标记实际的删除结点及其父结点Node* delParentPos = nullptr;Node* delPos = nullptr;while (cur){if (key < cur->_kv.first) //所给key值小于当前结点的key值{//往该结点的左子树走parent = cur;cur = cur->_left;}if (key > cur->_kv.first) //所给key值小于当前结点的key值{//往该结点的右子树走parent = cur;cur = cur->_right;}else //找到了待删除结点{if (cur->_left == nullptr) //待删除结点的左子树为空{if (cur == _root) //待删除结点是根结点{_root = _root->_right; //让根结点的右子树作为新的根结点//如果此时_root为空,此时寻找去父亲指针会对空指针解引用而报错,因此需要判断if (_root)_root->_parent = nullptr;delete cur; //删除原根结点return true; //根结点无祖先结点,无需进行平衡因子的更新操作}else{delParentPos = parent; //标记实际删除结点的父结点delPos = cur; //标记实际删除的结点}break; //删除结点有祖先结点,需更新平衡因子}else if (cur->_right == nullptr) //待删除结点的右子树为空{if (cur == _root) //待删除结点是根结点{_root = _root->_left; //让根结点的左子树作为新的根结点//如果此时_root为空,此时寻找去父亲指针会对空指针解引用而报错,因此需要判断if (_root)_root->_parent = nullptr;delete cur; //删除原根结点return true; //根结点无祖先结点,无需进行平衡因子的更新操作}else{delParentPos = parent; //标记实际删除结点的父结点delPos = cur; //标记实际删除的结点}break; //删除结点有祖先结点,需更新平衡因子}else //待删除结点的左右子树均不为空{//替换法删除//寻找待删除结点右子树当中key值最小的结点作为实际删除结点Node* minParent = cur;Node* minRight = cur->_right;while (minRight->_left){minParent = minRight;minRight = minRight->_left;}cur->_kv.first = minRight->_kv.first; //将待删除结点的key改为minRight的keycur->_kv.second = minRight->_kv.second; //将待删除结点的value改为minRight的valuedelParentPos = minParent; //标记实际删除结点的父结点delPos = minRight; //标记实际删除的结点break; //删除结点有祖先结点,需更新平衡因子}}}if (delParentPos == nullptr) //delParentPos没有被修改过,说明没有找到待删除结点{return false;}//记录待删除结点及其父结点Node* del = delPos;Node* delP = delParentPos;//更新平衡因子while (delPos != _root){//判断删除的是在父亲的哪一边,然后更新平衡因子if (delPos == delParentPos->_left){delParentPos->_bf++;}else if (delPos == delParentPos->_right){delParentPos->_bf--;}//当前树是平衡的,停止更新if (delParentPos->_bf == -1 || delParentPos->_bf == 1){break; //delParent树的高度没有发生变化,不会影响其父结点及以上结点的平衡因子}else if (delParentPos->_bf == 0)//需要继续往上更新平衡因子{//delParentPos树的高度变化,会影响其父结点的平衡因子,需要继续往上更新平衡因子delPos = delParentPos;delParentPos = delParentPos->_parent;}else if (delParentPos->_bf == -2 || delParentPos->_bf == 2) //需要进行旋转处理{if (delParentPos->_bf == -2)//左边高{if (delParentPos->_left->_bf == -1)//左边高,进行右单旋{Node* tmp = delParentPos->_left; //记录delParentPos右旋转后新的根结点RotateR(delParentPos); //右单旋delParentPos = tmp; //更新根结点}else if (delParentPos->_left->_bf == 1)//曲线影响,需要进行左右双旋{Node* tmp = delParentPos->_left->_right; //记录delParentPos左右旋转后新的根结点RotateLR(delParentPos); //左右双旋delParentPos = tmp; //更新根结点}else //delParentPos->_left->_bf == 0{Node* tmp = delParentPos->_left; //记录delParentPos右旋转后新的根结点RotateR(delParentPos); //右单旋delParentPos = tmp; //更新根结点//平衡因子调整delParentPos->_bf = 1;delParentPos->_right->_bf = -1;break;}}else //delParentPos->_bf == 2{if (delParentPos->_right->_bf == -1){Node* tmp = delParentPos->_right->_left; //记录delParentPos右左旋转后新的根结点RotateRL(delParentPos); //右左双旋delParentPos = tmp; //更新根结点}else if (delParentPos->_right->_bf == 1)//曲线影响,需要进行左右双旋{Node* tmp = delParentPos->_right; //记录delParentPos左旋转后新的根结点RotateL(delParentPos); //左单旋delParentPos = tmp; //更新根结点}else //delParentPos->_right->_bf == 0{Node* tmp = delParentPos->_right; //记录delParentPos左旋转后新的根结点RotateL(delParentPos); //左单旋delParentPos = tmp; //更新根结点//平衡因子调整delParentPos->_bf = -1;delParentPos->_left->_bf = 1;break;}}//继续往上更新平衡因子delPos = delParentPos;delParentPos = delParentPos->_parent;}else{exit(-1); //不会到这里,到这里说明出现了错误}}//进行实际删除if (del->_left == nullptr) //实际删除结点的左子树为空{if (del == delP->_left) //实际删除结点是其父结点的左孩子{delP->_left = del->_right;if (del->_right)del->_right->_parent = parent;}else //实际删除结点是其父结点的右孩子{delP->_right = del->_right;if (del->_right)del->_right->_parent = parent;}}else //实际删除结点的右子树为空{if (del == delP->_left) //实际删除结点是其父结点的左孩子{delP->_left = del->_left;if (del->_left)del->_left->_parent = parent;}else //实际删除结点是其父结点的右孩子{delP->_right = del->_left;if (del->_left)del->_left->_parent = parent;}}delete del; //实际删除结点return true;}//遍历的时候 root为private外面无法拿到//因此需要封装一层void _Inorder(Node* root){if (root == nullptr)return;_Inorder(root->_left);cout << root->_kv.first << " " << root->_kv.second << endl;_Inorder(root->_right);}//中序遍历void Inorder(){_Inorder(_root);}Node* Find(const K& k){Node* cur = _root;while (cur){if (cur->_kv.first > k)cur = cur->_left;else if (cur->_kv.first < k)cur = cur->_right;elsereturn cur;}return false;}//求树的深度int maxDepth(Node* root){if (root == NULL){return 0;}int leftDepth = maxDepth(root->_left);int rightDepth = maxDepth(root->_right);return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;}bool _isBalanced(Node* root){if (root == nullptr){return true;}int leftHight = maxDepth(root->_left);int rightHight = maxDepth(root->_right);return abs(leftHight - rightHight) < 2&& _isBalanced(root->_left)&& _isBalanced(root->_right);}bool isBalanced(){return _isBalanced(_root);}
private:Node* _root = nullptr;
};

最后如果觉得对你有帮助的话劳烦您动动您的小手点个赞

史上最详细的AVL树(含代码实现)相关推荐

  1. 史上最详细的AVL树的实现(万字+动图讲解旋转)

           

  2. 史上最详细的Pytorch版yolov3代码中文注释详解(四)

    史上最详细的Pytorch版yolov3代码中文注释详解(一):https://blog.csdn.net/qq_34199326/article/details/84072505 史上最详细的Pyt ...

  3. 史上最详细全中文 Cisco 3560交换机使用手册

    史上最详细全中文 Cisco 3560交换机使用手册 (末尾送交换机安全技术) 目 录 CISCO Catalyst 3560-E系列交换机的功能应用及安全解决方案 3 一.Cisco? Cataly ...

  4. 史上最详细的微生物扩增子数据库整理

    声明:文件所有链接内容来自"生信控"公众号,已经获作者向屿授权. 本人对每个数据库的使用目的和经验配导读,需要使用的小伙伴读点击链接跳转原文学习. "生信控"相 ...

  5. 史上最详细版Centos6安装详细教程

    镜像CentOS-6.8-x86_64-bin-DVD1.ISO 将下载好的镜像上传到服务器,并选择该镜像(详情请看上篇exsi镜像上传文章) 一.安装开始 开机选择第一项 这里询问我们是否要对光盘进 ...

  6. 史上最详细“截图”搭建Hexo博客——For Windows

    http://angelen.me/2015/01/23/2015-01-23-%E5%8F%B2%E4%B8%8A%E6%9C%80%E8%AF%A6%E7%BB%86%E2%80%9C%E6%88 ...

  7. 不仅有史上最详细Docker 安装Minio Client,还附带解决如何设置永久访问和永久下载链接!!(详图)绝对值得收藏的哈!!!!

    背景: 这两天在整理知识点,然后在学习Minio,一开始遇到更新,整了我不少时间,之前用的太久了,改了不少东西.用了之后发现不知道怎么设置成永久访问,就出了这篇文章. 史上最详细Docker安装最新版 ...

  8. 史上最详细Docker安装最新版Minio 带详解 绝对值得收藏!!! 让我们一起学会使用minio搭建属于自己的文件服务器!!走上白嫖之路!解决启动了但是浏览器访问不了的原因

    让我们一起学会使用minio搭建属于自己的文件服务器!!走上白嫖之路! WARNING: Console endpoint is listening on a dynamic port (34451) ...

  9. 史上最详细阿里云服务器上Docker部署War包项目 实战每一步都带详细图解!!!

    史上最详细阿里云服务器上Docker部署War包项目 实战每一步都带详细图解!!! 部署jar 包方式: https://blog.csdn.net/weixin_45821811/article/d ...

  10. 史上最详细的Android Studio系列教程四--Gradle基础

    史上最详细的Android Studio系列教程四--Gradle基础 转载于:https://www.cnblogs.com/zhujiabin/p/5125917.html

最新文章

  1. Python 进程之间共享数据(全局变量)
  2. C++ String转int
  3. webpack打包之clean-webpack-plugin插件 默认下载4.0.0版本的踩坑记录
  4. Angular之jwt令牌身份验证
  5. Error--解决使用Application Loader提交ipa包审核时的报错:ERROR ITMS-90168: The binary you uploaded was invalid....
  6. CSDN10大博客栏目火热评选中
  7. 微波网络插入反射系数与输入反射系数
  8. idea tab页签颜色不明显,自定义颜色解决。
  9. iOS app 启动 crash XCode 11 NSPOSIXErrorDomain Code=2 “No such file or directory“
  10. 工作类书籍之计算机相关
  11. JS--统一社会信用代码校验
  12. windbg 常用调试命令总结
  13. Linux入门(14)——系统启动修复
  14. 20090726选股
  15. windows-phone-power-tools [wp 安装部署xap]
  16. 微信小程序获取网络状态
  17. FIFO先进先出,FILO先进后出
  18. 流媒体服务器——Licode Janus-gateway Mediasoup Medooze 分析
  19. [附源码]java毕业设计校园志愿者服务管理系统
  20. windows11 安装Kibana

热门文章

  1. 次数分布和平均数、变异数间断性变数资料的整理
  2. Python20190206 解不定方程并绘制散点图标记解
  3. Python自学笔记系列一——认识python
  4. 称重系统中应用车牌识别实现无人值守称重系统解决方案
  5. Android微信支付集成流程及其常见错误
  6. (rear + maxSize - front) % maxSize 公式的理解(文图详解,手把手)
  7. uniapp表单验证手机号
  8. requirement
  9. Win10无法访问移动硬盘怎么解决
  10. 巧用Scrum与Kanban