赞
踩
目录
AVL树和红黑树都可以用来作为树形关联式容器的底层实现。但最终采用的是红黑树,虽然他们在时间复杂度上是一样的,但在某些场景下,红黑树更通用。他们依然是二叉搜索树,只不过实现二叉搜索树的方式不同,而被称做平衡二叉搜索树。
AVL树是由两名数学家(G.M.Adelson-Velskii和E.M.Landis)发明出来的一种解决二叉搜索树缺陷的方法,该树的名称也是这两位科学家的简称而得。
AVL树解决问题概念:当向二叉搜索树中插入新结点后,如果能保证每个节点的左右子树高度之差的绝对值不超过1(若超过,需要进行调整,待后续讲解),即可降低树的高度,从而减少平均搜索长度。
AVL树性质:
平衡因子= 右子树高度-左子树高度,也可以左减右,没有明确规定,那我采用的是右减左。
注意:是用高度之差计算出平衡因子,不是用平衡因子计算出平衡因子。其次平衡因子并不是必须的,他只是帮助我们更便捷的控制树
思考:既然了解了AVL树的规则,那为什么其高度差是不超过1,而不是0?
因为节点是一个一个插入的,有些情况无法做到左右子树高度相等,比如,2个节点的树,4个节点的树
更全面的平衡二叉搜索树如图:平衡因子记录高度差
AVL树的高度是平衡的,高度可保持在log n,搜索时间复杂度为O(log N)。
节点中包括左指针,右指针,指向父节点的指针,平衡因子,要存储的数据值。
- //节点
- template<class T>
- struct AVLTreeNode
- {
- AVLTreeNode(const T& data = T())
- :_left(nullptr)
- ,_right(nullptr)
- ,_parent(nullptr)
- ,_bf(0)
- ,_data(data)
- {
-
- }
- AVLTreeNode<T>* _left;
- AVLTreeNode<T>* _right;
- AVLTreeNode<T>* _parent;
- T _bf;//平衡因子
- T _data;//要存储的值
- };

- template<class T>
- class AVLTree
- {
- typedef AVLTreeNode<T> Node;
- typedef Node* PNode;
- public:
- AVLTree()
- :_Root(nullptr)
- {}
-
- private:
- PNode _Root;
- };
AVL树的就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么AVL树的插入过程可以分为两步:
1.按照二叉搜索树的方式插入新节点
2.调整节点的平衡因子
-
- bool Insert(const T& data)
- {
- if (_Root == nullptr)
- {
- _Root = new Node(data);
- return true;
- }
- PNode cur = _Root;
- PNode parent = nullptr;
- //找插入点
- while (cur)
- {
- if (data < cur->_data)
- {
- parent = cur;
- cur = cur->_left;
-
- }
- else if (data > cur->_data)
- {
- parent = cur;
- cur = cur->_right;
-
- }
- else
- return false;
- }
- //插入
- PNode node = new Node(data);
- if (data < parent->_data)
- {
- parent->_left = node;
- }
- else
- {
- parent->_right = node;
- }
- node->_parent = parent;
- return ture;
- }

这里就有点漫长了,首先来了解一点,当我们插入节点时,会影响哪些节点的平衡因子?
会影响祖先的平衡因子,有时候是部分的,有时候是全部。至于为什么是这样,这就要来了解平衡因子的更新原则,如图:
由上图可知根据节点的插入位置,来决定哪些祖先的平衡因子需要改变。
那么现在就要来重点讲一讲旋转问题了,当发生不平衡时,根据节点插入位置的不同,AVL树的旋转分为四种:
对于其插入,可以通过抽象图来做代表,通过具象图来进行更好的分析。
根据图的分析,我们可将他转化为代码,其中要注意的是,当旋转完后,也要更新节点中指向父 节点的指针:
- //左单旋
- void RotateL(PNode parent)
- {
- PNode subR = parent->_right;
- PNode subRL = subR->_left;
- PNode pparent = parent->_parent;
-
- if (parent == _Root)//更新根节点
- {
- _Root = subR;
- }
- else
- {
- //更新parent的父节点指向
- if (parent == pparent->_left)
- {
- subR->_parent = pparent;
- pparent->_left = subR;
- }
- else
- {
- subR->_parent = pparent;
- pparent->_right = subR;
- }
- }
-
- //parent的右指针指向subRL,subRL的父节点指向parent
- parent->_right = subR->_left;
- if(subRL)//subR的左节点可能不存在
- subRL->_parent = parent;
-
- //subR的左指针指向parent,parent的父节点指向subR
- subR->_left = parent;
- parent->_parent = subR;
-
- //更新平衡因子
- parent->_bf = subR->_bf = 0;
- }

根据图的分析,我们可将他转化为代码:
- //右单旋
- void RotateR(PNode parent)
- {
- PNode subL = parent->_left;
- PNode subLR = subL->_right;
- PNode pparent = parent->_parent;
-
- if (_Root == parent)
- {
- _Root = subL;
- subL->_parent = nullptr;
- }
- else
- {
- //更新parent的父节点指向
- if (pparent->_left == parent)
- {
- pparent->_left = subL;
- }
- else
- {
- pparent->_right = subL;
- }
- subL->_parent = pparent;
- }
- //parent的左指针指向subLR,subLR的父节点指向parent
- parent->_left = subLR;
- if(subLR)//subR的右节点可能不存在
- subLR->_parent = parent;
- //subL的右指针指向parent,parent的父节点指向subL
- subL->_right = parent;
- parent->_parent = subL;
-
- //更新平衡因子
- parent->_bf = subL->_bf = 0;
- }

有了上述的抽象图了解,这里就不在进行具象图分析,而是直接在抽象图上分析。
当在右侧插入时,其也分为两种情况,为什么要区分这两种情况?
因为他们影响平衡因子的情况不同,所以要做区分。
将上述情况转换为代码:
- //左右单旋
- void RotateLR(PNode parent)
- {
- PNode subL = parent->_left;
- PNode subLR = subL->_right;
- int bf = subLR->_bf;//记录下来,为了后面更新平衡因子
-
- RotateL(parent->_left);//左单旋
- RotateR(parent);//右单旋
-
- 因为左单旋和右单旋已经破坏了原本的平衡因子,所以需要手动更新
- //那么这里以原本subRL的平衡因子来做衡量
- if (bf == -1)
- {
- parent->_bf = 1;
- subL->_bf = 0;
- subLR->_bf = 0;
- }
- else if (bf == 1)
- {
- parent->_bf = 0;
- subL->_bf = -1;
- subLR->_bf = 0;
- }
- else if (bf == 0)//表示新插入节点
- {
- parent->_bf = subL->_bf = subLR->_bf = 0;
- }
- }

将上述情况转换为代码:
- //右左单旋
- void RotateRL(PNode parent)
- {
- PNode subR = parent->_right;
- PNode subRL = subR->_left;
- int bf = subRL->_bf;//记录下来,为了后面更新平衡因子
- RotateR(parent->_right);//右单旋
- RotateL(parent);//左单旋
-
- //因为右单旋和左单旋已经破坏了原本的平衡因子,所以需要手动更新
- //那么这里以原本subRL的平衡因子来做衡量
- if (bf == 1)
- {
- parent->_bf = -1;
- subR->_bf = 0;
- subRL->_bf = 0;
- }
- else if (bf == -1)
- {
- parent->_bf = 0;
- subR->_bf = 1;
- subRL->_bf = 0;
- }
- else if (bf == 0)//表示新插入节点
- {
- parent->_bf = subR->_bf = subRL->_bf = 0;
- }
- }

当插入新结点时,更新祖先结点的平衡因子,并一直往上判断,根据祖先结点的平衡因子判断是否需要进行旋转。
- bool Insert(const T& data)
- {
- if (_Root == nullptr)
- {
- _Root = new Node(data);
- return true;
- }
- PNode cur = _Root;
- PNode parent = nullptr;
- //找插入点
- while (cur)
- {
- if (data < cur->_data)
- {
- parent = cur;
- cur = cur->_left;
-
- }
- else if (data > cur->_data)
- {
- parent = cur;
- cur = cur->_right;
-
- }
- else
- return false;
- }
- //插入
- PNode node = new Node(data);
- if (data < parent->_data)
- {
- parent->_left = node;
- }
- else
- {
- parent->_right = node;
- }
- node->_parent = parent;
- cur = node;
-
-
- while (cur)
- {
- if (parent == nullptr)
- break;
- //更新平衡因子
- if (cur == parent->_left)
- parent->_bf--;
- else
- 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 (cur == parent->_right)
- {
- if (cur->_bf == 1)
- {
- //左单旋
- RotateL(parent);
- }
- else if (cur->_bf == -1)
- {
- //右左单旋
- RotateRL(parent);
- }
- }
- else
- {
- if (cur->_bf == 1)
- {
- //左右单旋
- RotateLR(parent);
-
- }
- else if (cur->_bf == -1)
- {
- //右单旋
- RotateR(parent);
- }
- }
- break;
- }
- else
- {
- assert(false);//插入之前就有问题
- }
- }
- return true;
-
- }

因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将结点删除,然后更新平衡因子,只不过,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。
对于AVL树的删除,在这里就不在模拟实现了,因为有了插入的了解,就足够我们进行对AVL树的学习认识。
当然,如果想要进一步了解,可参考<<算法导论>>或<<数据结果-用面向南对象方法与C++描述>>殷人昆版。
AVL树是一颗绝对平衡的二叉搜索树,弥补了普通版本的二叉搜索树的缺陷。其要求每个节点的左右子树高度差的绝对值不超过1,这样就可以保证查询时高效的时间复杂度,即log N。但是如果要
对AVL树做一些结构修改的操作,性能非常低下,
比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。
因此 :如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会变),可以考虑AVL树,但一个结构经常修改,就不太适合。
- //AVLTree.h
- #include <iostream>
- #include <vector>
- #include <assert.h>
- using namespace std;
-
- namespace bit
- {
- //节点
- template<class T>
- struct AVLTreeNode
- {
- AVLTreeNode(const T& data = T())
- :_left(nullptr)
- ,_right(nullptr)
- ,_parent(nullptr)
- ,_bf(0)
- ,_data(data)
- {}
- AVLTreeNode<T>* _left;
- AVLTreeNode<T>* _right;
- AVLTreeNode<T>* _parent;
- T _bf;//平衡因子
- T _data;
- };
-
-
- template<class T>
- class AVLTree
- {
- typedef AVLTreeNode<T> Node;
- typedef Node* PNode;
- public:
- AVLTree()
- :_Root(nullptr)
- {}
-
- bool Insert(const T& data)
- {
- if (_Root == nullptr)
- {
- _Root = new Node(data);
- return true;
- }
- PNode cur = _Root;
- PNode parent = nullptr;
- //找插入点
- while (cur)
- {
- if (data < cur->_data)
- {
- parent = cur;
- cur = cur->_left;
-
- }
- else if (data > cur->_data)
- {
- parent = cur;
- cur = cur->_right;
-
- }
- else
- return false;
- }
- //插入
- PNode node = new Node(data);
- if (data < parent->_data)
- {
- parent->_left = node;
- }
- else
- {
- parent->_right = node;
- }
- node->_parent = parent;
- cur = node;
-
-
- while (cur)
- {
- if (parent == nullptr)
- break;
- //更新平衡因子
- if (cur == parent->_left)
- parent->_bf--;
- else
- 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 (cur == parent->_right)
- {
- if (cur->_bf == 1)
- {
- //左单旋
- RotateL(parent);
- }
- else if (cur->_bf == -1)
- {
- //右左单旋
- RotateRL(parent);
- }
- }
- else
- {
- if (cur->_bf == 1)
- {
- //左右单旋
- RotateLR(parent);
-
- }
- else if (cur->_bf == -1)
- {
- //右单旋
- RotateR(parent);
- }
- }
- break;
- }
- else
- {
- assert(false);//插入之前就有问题
- }
- }
- return true;
-
- }
-
- //左单旋
- void RotateL(PNode parent)
- {
- PNode subR = parent->_right;
- PNode subRL = subR->_left;
- PNode pparent = parent->_parent;
-
- if (parent == _Root)//更新根节点
- {
- _Root = subR;
- subR->_parent = nullptr;
- }
- else
- {
- //更新parent的父节点指向
- if (parent == pparent->_left)
- {
- pparent->_left = subR;
- }
- else
- {
- pparent->_right = subR;
- }
- subR->_parent = pparent;
-
- }
-
- //parent的右指针指向subRL,subRL的父节点指向parent
- parent->_right = subR->_left;
- if(subRL)//subR的左节点可能不存在
- subRL->_parent = parent;
-
- //subR的左指针指向parent,parent的父节点指向subR
- subR->_left = parent;
- parent->_parent = subR;
-
- //更新平衡因子
- parent->_bf = subR->_bf = 0;
- }
-
- //右单旋
- void RotateR(PNode parent)
- {
- PNode subL = parent->_left;
- PNode subLR = subL->_right;
- PNode pparent = parent->_parent;
-
- if (_Root == parent)
- {
- _Root = subL;
- subL->_parent = nullptr;
- }
- else
- {
- //更新parent的父节点指向
- if (pparent->_left == parent)
- {
- pparent->_left = subL;
- }
- else
- {
- pparent->_right = subL;
- }
- subL->_parent = pparent;
- }
- //parent的左指针指向subLR,subLR的父节点指向parent
- parent->_left = subLR;
- if(subLR)//subR的右节点可能不存在
- subLR->_parent = parent;
- //subL的右指针指向parent,parent的父节点指向subL
- subL->_right = parent;
- parent->_parent = subL;
-
- //更新平衡因子
- parent->_bf = subL->_bf = 0;
- }
-
- //左右单旋
- void RotateLR(PNode parent)
- {
- PNode subL = parent->_left;
- PNode subLR = subL->_right;
- int bf = subLR->_bf;//记录下来,为了后面更新平衡因子
-
- RotateL(parent->_left);//左单旋
- RotateR(parent);//右单旋
-
- 因为左单旋和右单旋已经破坏了原本的平衡因子,所以需要手动更新
- //那么这里以原本subRL的平衡因子来做衡量
- if (bf == -1)
- {
- parent->_bf = 1;
- subL->_bf = 0;
- subLR->_bf = 0;
- }
- else if (bf == 1)
- {
- parent->_bf = 0;
- subL->_bf = -1;
- subLR->_bf = 0;
- }
- else if (bf == 0)//表示新插入节点
- {
- parent->_bf = subL->_bf = subLR->_bf = 0;
- }
- }
-
- //右左单旋
- void RotateRL(PNode parent)
- {
- PNode subR = parent->_right;
- PNode subRL = subR->_left;
- int bf = subRL->_bf;//记录下来,为了后面更新平衡因子
- RotateR(parent->_right);//右单旋
- RotateL(parent);//左单旋
-
- //因为右单旋和左单旋已经破坏了原本的平衡因子,所以需要手动更新
- //那么这里以原本subRL的平衡因子来做衡量
- if (bf == 1)
- {
- parent->_bf = -1;
- subR->_bf = 0;
- subRL->_bf = 0;
- }
- else if (bf == -1)
- {
- parent->_bf = 0;
- subR->_bf = 1;
- subRL->_bf = 0;
- }
- else if (bf == 0)//表示新插入节点
- {
- parent->_bf = subR->_bf = subRL->_bf = 0;
- }
- }
-
- void InOrder()
- {
- _InOrder(_Root);
- cout << endl;
- }
- int Height()
- {
- return _Height(_Root);
- }
- int _Height(PNode Root)
- {
- if (Root == nullptr)
- return 0;
- int _LeftHeight = _Height(Root->_left)+ 1;
- int _RightHeight = _Height(Root->_right) + 1;
- return max(_LeftHeight, _RightHeight);
- }
- bool _IsBalance()
- {
- return _IsBalanceTree(_Root);
- }
- bool _IsBalanceTree(PNode Root)
- {
- if (Root == nullptr)
- return true;
- int _LeftHeight = _Height(Root->_left);
- int _RightHeight = _Height(Root->_right);
- int sub =_RightHeight - _LeftHeight ;
- if (sub != Root->_bf)
- {
- cout << Root->_data << ":" << "平衡因子异常" << endl;
- return false;
- }
-
- if (sub == 1 || sub == -1 || sub == 0)
- {
- return _IsBalanceTree(Root->_left) && _IsBalanceTree(Root->_right);
- }
- else
- return false;
-
- }
-
- PNode Find(size_t data)
- {
- PNode cur = _Root;
- while (cur)
- {
- if (cur->_data < data)
- {
- cur = cur->_left;
- }
- else if (cur->_data > data)
- {
- cur = cur->_right;
- }
- else
- {
- return cur;
- }
- }
-
- return NULL;
- }
-
-
- size_t Size()
- {
- return _Size(_Root);
- }
- size_t _Size(PNode cur)
- {
- if (cur == nullptr)
- return 0;
-
- return _Size(cur->_left) + _Size(cur->_right) + 1;
- }
-
- private:
- void _InOrder(PNode Root)
- {
- if (Root == nullptr)
- return;
-
- _InOrder(Root->_left);
- cout << Root->_data<< endl;
- _InOrder(Root->_right);
- }
- PNode _Root;
- };
- void AVLTreeTest()
- {
- //int arr[] = { 16,3,7,11,9,26,18,14,15};
- int arr[] = { 4,2,6,1,3,5,15,7,16,14 };
- AVLTree<int> avl;
-
- for (auto e : arr)
- {
-
- avl.Insert(e);
- }
- avl.InOrder();
- cout <<avl._IsBalance();
- }
- void TestAVLTree2()
- {
- const int N = 9;
- vector<int> v;
- v.reserve(N);
- srand(time(0));
-
- for (size_t i = 0; i < N; i++)
- {
- v.push_back(rand() + i);
- //cout << v.back() << endl;
- }
-
- size_t begin2 = clock();
- AVLTree<int> t;
- for (auto e : v)
- {
- t.Insert(e);
- cout << "Insert:" << e << "->" << t._IsBalance() << endl;
- }
- size_t end2 = clock();
-
- cout << "Insert:" << end2 - begin2 << endl;
-
- cout << t._IsBalance() << endl;
-
- cout << "Height:" << t.Height() << endl;
- cout << "Size:" << t.Size() << endl;
-
- size_t begin1 = clock();
- // 确定在的值
- for (auto e : v)
- {
- t.Find(e);
- }
-
- // 随机值
- for (size_t i = 0; i < N; i++)
- {
- t.Find((rand() + i));
- }
-
- size_t end1 = clock();
-
- cout << "Find:" << end1 - begin1 << endl;
- }
- }

- //test.cpp
- #include "AVLTree.h"
-
- int main()
- {
- bit::AVLTreeTest();
- //bit::TestAVLTree2();
-
- return 0;
- }
输出结果:
- //test.cpp
- #include "AVLTree.h"
-
- int main()
- {
- //bit::AVLTreeTest();
- bit::TestAVLTree2();
-
- return 0;
- }
输出结果:
end~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。