1.AVL树的相关概念
二叉搜索树在一定程度上可以提高搜索效率,但是当序列是有序时:如果所示
此时二叉搜索树退化成单链表,搜索效率退化为O(N)。为了解决这个问题科学家引入了AVL树,又称平衡搜索二叉树
AVL简称平衡二叉树。由前苏联的数学家 Adelse-Velskil 和 Landis 在 1962 年提出的高度平衡的二叉树,根据科学家的英文名也称为 AVL 树。它具有如下几个性质:
可以是空树。
假如不是空树,任何一个结点的左子树与右子树都是平衡二叉树,并且高度之差的绝值不超过 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; } }
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; }
其他相关函数
//找到最离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; }
对应总代码:
#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的key cur->_kv.second = minRight->_kv.second; //将待删除结点的value改为minRight的value delParentPos = 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; else return 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; };
最后如果觉得对你有帮助的话劳烦您动动您的小手点个赞
版权归原作者 一个山里的少年 所有, 如有侵权,请联系我们删除。