赞
踩
目录
二叉搜索树可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于再顺序表中搜索元素,效率低下。
因此,两位俄罗斯数学家便发明了一种解决上述问题的方法:
当向二叉搜索树中插入新节点后,如果能保证每个结点左右子树高度之差的绝对值不超过1(需要对树的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
一颗AVL树或者是空树,或者是具有以下性质的二叉搜索树:
如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有 n 个结点,其高度可保持在 logN,搜索的时间复杂度为 logN。
二叉搜索树结点的定义与 AVL 树结点的定义在于:
其数据的存储方式我们采用 K-value 模型。
AVL 本质就是二叉搜索树,只不过 AVL 树是在二叉搜索树的基础上引入了平衡因子。即,在 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);
- if (parent->_kv.first < kv.first)
- {
- parent->_right = cur;
- }
- else
- {
- parent->_left = cur;
- }
- //因为增加了父域,所以还要处理cur的父域
- cur->_parent = parent;
- }
以上我们完成了节点的插入,但是插入了节点后,树可能会出现以下情况:
所以,为了防止以上情况出现,我们在插入后要调整平衡。
为了维持平衡,我们引入了平衡因子来控制树的平衡,所以我们每次插入后要更新平衡因子,如果出现失去平衡的情况,我们则要进行旋转来维持平衡。
更新平衡因子的规则:
- 新增在右边,则会让父平衡因子++,新增在左边,父平衡因子 -- ;
- 更新后,如果 parent->bf == 0,说明 parent 插入前的平衡因子是 1 or -1,插入填上了矮的一边,parent 的子树高度不变,不需要继续往上更新。
- 更新后,如果 parent->bf 为 1 或 -1, 说明parent插入前的平衡因子是0,说明左右子树高度相等,插入后有一边高,parnet 高度变了,需要继续往上更新。
- 更新后,如果 parent->bf == 2 或 -2 ,说明 parent 插入前的平衡因子是 1 or -1,已经到达平衡临界值,parent 子树进行旋转处理将树保持平衡。
- 更新后,如果 parent->bf >2 或 <-2 ,则说明插入前树已经失去的平衡,要进行代码的检查。
现在,我们要对祖先路径更新平衡因子,直到更新到根节点结束。
注释我写的非常清楚,结合上图和代码一起理解。
当规则4出现时,我们则要进行旋转来调整平衡,旋转共分为 4 种情况,左左右单旋、右右左单旋、左右左右双旋、右左右左双旋我们一一进行解决。
现在我们要发现左单旋的执行规律:
1.树的右子树失去平衡(parent->bf == 2),并且新插入的节点位于右子树的右侧(cur->bf == 1)。
2. 将 cur->left 交由 parent->right 抚养,再调整父子关系,使 cur->left 指向 parent ,即完成旋转。
3.平衡因子归零。观察上图,旋转后,60 的左右子树高度相同(a、b高度为h,c的高度为 h+1,注意:60左子树的高度也为h+1,因为存在 30 节点)
接下来我们编写代码
现在我们要发现右单旋的执行规律:
1.树的左子树失去平衡(parent->bf == -2),并且新插入的节点位于左子树的左侧(cur->bf == -1)。
2. 将 cur->right 交由 parent->left 抚养,再调整父子关系,使 cur->right 指向 parent ,即完成旋转。
- //情况2:左左右单旋
- void RotateR(Node * parent)
- {
- Node* subL = parent->_left;
- Node* subLR = subL->_right;
-
- parent->_left = subLR;
- if (subLR)
- subLR->_parent = parent;
-
- Node* ppNode = parent->_parent;
- subL->_right = parent;
- parent->_parent = subL;
- if (ppNode)
- {
- if (ppNode->_left == parent)
- {
- ppNode->_left = subL;
- }
- else
- {
- ppNode->_right = subL;
- }
- subL->_parent = ppNode;
- }
- else
- {
- _root = subL;
- subL->_parent = nullptr;
- }
- subL->_bf = parent->_bf = 0;
- }
上面我们解决了4种情况中的两种情况,对于平衡的调整还有左右左右双旋和右左右左双旋这两种情况。
其调整的条件是根的左子树的右侧插入了新的节点。
这种插入情况我们还要再细分为三种插入情况,其平衡因子会出现不同的情况:
- 在30的右子树 60 左侧进行插入。
- 在30的右子树 60 右侧进行插入。
- 30的右子树为空,插入的节点为60本身。
先分析第一种情况,如图:
此时 60 节点的左侧插入节点导致 90 节点下的左右子树高度失衡,所以我们接下来要对 90 节点下进行旋转处理。
旋转的步骤分为两步:
先对30进行左单旋,然后再对90进行右单旋
1.先对 30 进行左单旋,这样,30节点的左右子树便恢复了平衡。
2.因为 60 左子树高度高于右子树,以 90节点视角来看,符合右单旋的条件(左高右低),所以90节点进行右单旋
然后对 90 进行右单旋
以上情况是在 60 的左边进行插入,同样也有在 60 的右边进行插入,两种插入会导致 90、30的 平衡因子出现不同的情况。注意观察下图平衡因子的变化,与上图是不同的。
然后对90进行右单旋
此时我们解决的都是 30 有左右子树,60有左右子树,还有一种可能,h为0,即60本身为新增。
然后进行旋转
好的,三种情况我们都分析完了,发现其实无论哪种情况,其步骤都经历的左单旋和右单旋,只是平衡因子因情况的不同而不同。
而这三种情况的不同都是由 60 影响的,所以我们根据 60 平衡因子的不同分别进行调整即可。
同样的,右左右左双旋和左右左右双旋几乎一致,就不做过多讲解了,结合画图,仿照左右双旋,很容易写出代码。
代码如下:
- else if (parent->_bf == 2 && cur->_bf == -1)
- {
- //右左双旋
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
- int bf = subRL->_bf;
-
- //旋转
- RotateR(parent->_right);
- RotateL(parent);
-
- subRL->_bf = 0;
- if (bf == 1)
- {
- subR->_bf = 0;
- parent->_bf = -1;
- }
- else if (bf == -1)
- {
- subR->_bf = 1;
- parent->_bf = 0;
- }
- else if (bf == 0)
- {
- subR->_bf = 0;
- parent->_bf = 0;
- }
- else
- {
- assert(false);
- }
- }
实现了插入我们来测试一下代码。
好的,程序能正常运行,但程序能正常运行就说明我们的 AVL 就是成功的吗?我们的 AVL 树就是平衡的吗?
所以现在我们要使用检查树的高度的方式来检查树是否是平衡的。即,如果左右子树的高度差不超过1,则说明该树是平衡的,接下来我们来编写成员函数。
- int _Height(Node* root)
- {
- if (root == nullptr)
- return 0;
- return max(_Height(root->_left), _Height(root->_right)) + 1;
- }
有了测量高度的函数后我们来编写平衡判断函数。
- bool _IsBalance(Node* root)
- {
- if (root == nullptr)
- return true;
- int leftHeight = _Height(root->_left);
- int rightHeight = _Height(root->_right);
- int diffHeight = abs(leftHeight - rightHeight);
-
- // |diffHeight|小于2 则当前树平衡。
- // 还要去判断左右子树是否平衡
- return abs(diffHeight) < 2
- && _IsBalance(root->_left)
- && _IsBalance(root->_right);
- }
好的,接下来往树中插入一万个随机数据,然后来判断其是否平衡。
14层的平衡二叉树大约有16万个节点,所以高度计算正确,通过 IsBalance 函数也判定当前树为平衡。
最后我们来玩一下AVL树,往树中插入一亿个数据,然后找到77777处的数据。结果如下:
一亿个数据,插入的效率是O(n*logN),想找到其中任意一个值,最多查找高度处。
- #include <iostream>
- #include <map>
- #include <utility>
- #include <assert.h>
- #pragma once
- using namespace std;
- //AVL树结点定义
- template<class K, class V>
- struct AVLTreeNode
- {
- AVLTreeNode <K, V>* _left;
- AVLTreeNode <K, V>* _right;
- AVLTreeNode <K, V>* _parent;
-
- pair<K, V> _kv; //key-value模型
- int _bf; //banlance factor 平衡因子
-
- AVLTreeNode(const pair<K, V>& kv)
- :_left(nullptr), _right(nullptr), _parent(nullptr)
- , _kv(kv)
- , _bf(0)
- {}
- };
-
- template<class K, class V>
- struct AVLTree
- {
- typedef AVLTreeNode<K, V> Node;
- public:
- 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);
- if (parent->_kv.first < kv.first)
- {
- parent->_right = cur;
- }
- else
- {
- parent->_left = cur;
- }
-
- cur->_parent = parent;
-
- // 控制平衡
- // 1、更新平衡因子
- while (parent)
- {
- if (cur == parent->_right)
- {
- parent->_bf++;
- }
- else
- {
- parent->_bf--;
- }
-
- if (parent->_bf == 0)
- {
- break;
- }
- else if (abs(parent->_bf) == 1)
- {
- parent = parent->_parent;
- cur = cur->_parent;
- }
- else if (abs(parent->_bf) == 2)
- {
- // 说明parent所在子树已经不平衡了,需要旋转处理
- 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;
- }
- void Inorder()
- {
- _Inorder(_root);
- }
- int Height()
- {
- return _Height(_root);
- }
- bool IsBalance()
- {
- return _IsBalance(_root);
- }
-
- pair<K, V> Find(const K& key)
- {
- Node* cur = _root;
- while (cur)
- {
- if (cur->_kv.first > key)
- {
- cur = cur->_left;
- }
- else if (cur->_kv.first < key)
- {
- cur = cur->_right;
- }
- else
- {
- return cur->_kv;
- }
- }
- return pair<int, int>(0, 0);
- }
- private:
-
- bool _IsBalance(Node* root)
- {
- if (root == nullptr)
- return true;
- int leftHeight = _Height(root->_left);
- int rightHeight = _Height(root->_right);
- int diffHeight = abs(leftHeight - rightHeight);
-
- // |diffHeight|小于2 则当前树平衡。
- // 还要去判断左右子树是否平衡
- return abs(diffHeight) < 2
- && _IsBalance(root->_left)
- && _IsBalance(root->_right);
- }
-
- int _Height(Node* root)
- {
- if (root == nullptr)
- return 0;
- return max(_Height(root->_left), _Height(root->_right)) + 1;
- }
-
- void _Inorder(Node* root)
- {
- if (root)
- {
- _Inorder(root->_left);
- cout << root->_kv.first << ":" << root->_kv.second << endl;
- _Inorder(root->_right);
- }
- }
- void RotateL(Node* parent)
- {
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
-
- parent->_right = subRL;
- if (subRL)
- subRL->_parent = parent;
-
- Node* ppNode = parent->_parent;
-
- subR->_left = parent;
- parent->_parent = subR;
-
- if (_root == parent)
- {
- _root = subR;
- subR->_parent = nullptr;
- }
- else
- {
- if (ppNode->_left == parent)
- {
- ppNode->_left = subR;
- }
- else
- {
- ppNode->_right = subR;
- }
-
- subR->_parent = ppNode;
- }
-
- subR->_bf = parent->_bf = 0;
- }
-
- void RotateR(Node* parent)
- {
- Node* subL = parent->_left;
- Node* subLR = subL->_right;
-
- parent->_left = subLR;
- if (subLR)
- {
- subLR->_parent = parent;
- }
-
- Node* ppNode = parent->_parent;
-
- subL->_right = parent;
- parent->_parent = subL;
-
- if (_root == parent)
- {
- _root = subL;
- subL->_parent = nullptr;
- }
- else
- {
- if (ppNode->_left == parent)
- {
- ppNode->_left = subL;
- }
- else
- {
- ppNode->_right = subL;
- }
-
- subL->_parent = ppNode;
- }
-
- subL->_bf = parent->_bf = 0;
- }
-
- void RotateLR(Node* parent)
- {
- Node* subL = parent->_left;
- Node* subLR = subL->_right;
- int bf = subLR->_bf;
-
- RotateL(parent->_left);
- RotateR(parent);
-
- subLR->_bf = 0;
- if (bf == 1)
- {
- parent->_bf = 0;
- subL->_bf = -1;
- }
- else if (bf == -1)
- {
- // 错的
- /*parent->_bf = 0;
- subL->_bf = 1;*/
-
- parent->_bf = 1;
- subL->_bf = 0;
- }
- else if (bf == 0)
- {
- parent->_bf = 0;
- subL->_bf = 0;
- }
- else
- {
- assert(false);
- }
- }
-
- void RotateRL(Node* parent)
- {
- Node* subR = parent->_right;
- Node* subRL = subR->_left;
-
- int bf = subRL->_bf;
-
- RotateR(parent->_right);
- RotateL(parent);
-
- subRL->_bf = 0;
- if (bf == 1)
- {
- subR->_bf = 0;
- parent->_bf = -1;
- }
- else if (bf == -1)
- {
- subR->_bf = 1;
- parent->_bf = 0;
- }
- else if (bf == 0)
- {
- parent->_bf = 0;
- subR->_bf = 0;
- }
- else
- {
- assert(false);
- }
- }
-
- private:
- Node* _root = nullptr;
- };
-
- void TestAVLTree1()
- {
- int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
- AVLTree<int, int> t1;
- for (auto e : a)
- {
- t1.Insert(make_pair(e, e));
- }
- t1.Inorder();
- cout << t1.Height() << endl;
- if (t1.IsBalance())
- cout << "平衡树" << endl;
- else
- cout << "非平衡树" << endl;
- }
-
-
- void TestAVLTree2()
- {
- //随机插入一万个数据
- size_t N = 10000;
- srand((unsigned int)time(NULL));
- AVLTree<int, int> t1;
- for (size_t i = 0; i < N; ++i)
- {
- int x = rand() % 100 + 1;
- t1.Insert(make_pair(i, x));
- bool IsBalance = t1.IsBalance();
- printf("%-4d:%-2d ", i, x);
- printf("Balance:%d\n", IsBalance);
- if (!IsBalance)
- {
- cout << i << endl;
- assert(false);
- }
- }
- //树的高度
- cout << "Tree Height:" << t1.Height() << endl;
-
- //判断平衡
- if (t1.IsBalance())
- cout << "IS AVL" << endl;
- else
- cout << "NOT AVL" << endl;
- }
-
- void TestAVLTree3()
- {
- size_t N = 100000000;
- srand((unsigned int)time(NULL));
- AVLTree<int, int> t1;
- for (size_t i = 0; i < N; ++i)
- {
- int x = rand() % 100 + 1;
- t1.Insert(make_pair(i, x));
- }
- cout << t1.Height() << endl;
- pair<int, int> result = t1.Find(777777);
- cout << result.first << ":" << result.second << endl;
- }
-
- int main()
- {
- //TestAVLTree1();
- //TestAVLTree2();
- TestAVLTree3();
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。