AVL树的基本实现
来源:互联网 发布:泰牛程序员官网 编辑:程序博客网 时间:2024/05/17 23:47
AVL树(高度平衡的二叉搜索树),它可以保持二叉树的高度平衡,尽量降低二叉树的高度,减少树的平均搜索长度
AVL树的性质
1,左子树和右子树的高度之差的绝对值不超过1
2,树中的每个左子树和右子树都是AVL树
3,每个结点都有一个平衡因子,任意一个结点的平衡因子是-1,0,1(每个结点的平衡因子等于右子树的高度减去左子树的高度)
AVL树的效率:一颗AVL树有N个结点,它的高度可以保持在lgN,其中插入删除查找的时间复杂度也为lgN(需要注意的是lg在这里表示的是以2为底)
首先我们定义AVLTreeNode的结点
在这里我们定义的是一个三叉链,作用后面会用到
template<class K,class V>struct AVLTreeNode{ AVLTreeNode<K,V>* _left; AVLTreeNode<K,V>* _right; AVLTreeNode<K,V>* _parent; V _value; K _key; int _bf; AVLTreeNode(const K& key,const V& value) :_left(NULL) , _right(NULL) , _parent(NULL) , _key(key) , _bf(0) , _value(value) {}};
我们先看一下插入的四种旋转的方式:
1,左单旋
我们在图中可以看出来,parent的平衡因子是2所以我们需要对它进行旋转,我们将subRL给parent的_right,将parent给subR的_left,这样就旋转好了吗?
当然没有我们并不知道原来的parent是根节点还是一颗子树,所以我们要在parent给subR的_left之前要定义一个变量parentparent保存parent 的_parent,如果parentparent为空,那么就说明之前parent是根节点,那么旋转结束之后subR就是旋转结束后这棵树的根节点,如果parentparent不为空,那么就说明这棵树只是一颗子树,如果parent在parentparent的_left那么就把subR给parentparent的_left,反之将subR给parentparent的_right。(这里要记住这是三叉链,我们要把每个都链起来)
代码实现:
void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; parent->_right = subRL; if (subRL) subRL->_parent = parent; subR->_left = parent; Node* parentparent = parent->_parent; parent->_parent = subR; if (parentparent == NULL) { _root = subR; subR->_parent = NULL; } else { if (parentparent->_left == parent) { parentparent->_left = subR; subR->_parent = parentparent; } else { parentparent->_right = subR; subR->_parent = parentparent; } } subR->_bf = parent->_bf = 0; }
2,右单旋
思想和左单旋相同,如果理解了左单旋,右单旋自然也就理解了
代码实现:
void RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; parent->_left = subLR; if (subLR) subLR->_parent = parent; subL->_right = parent; Node* parentparent = parent->_parent; parent->_parent = subL; if (parentparent == NULL) { _root = subL; subL->_parent = NULL; } else { if (parentparent->_left == parent) { parentparent->_left = subL; subL->_parent = parentparent; } else { parentparent->_right = subL; subL->_parent = parentparent; } } subL->_bf = parent->_bf = 0; }
3,左右双旋
我们从图中可以看到,左右双旋进行了一次左单旋,进行了一次右单旋
那么它的平衡因子是怎么调整的呢,
我们把平衡因子的调整分为三种情况
1,subLR没有左孩子
2,subLR没有右孩子
3,subLR有左孩子和右孩子或者左右孩子都没有
代码实现
void RotateLR(Node* parent) { Node* subL = parent->_left; Node* subLR = parent->_right; int bf = subLR->_bf; RotateL(parent->_left); RotateR(parent); if (bf == 1)//subLR没有左孩子 { parent->_bf = 0; subL->_bf = -1; } else if (bf == -1)//subLR没有右孩子 { subL->_bf = 0; parent->_bf = 1; } else//subLR有左孩子和右孩子或者左右孩子都没有 { subL->_bf = parent->_bf = 0; } subLR->_bf = 0; }
4,右左双旋
右左双旋跟左右双旋的算法思想相同,如果理解了上一个,这个可以自己画图理解一下
代码实现:
void RotateRL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; int bf = subRL->_bf; RotateR(parent->_right); RotateL(parent); if (bf == 1)//subRL没有左孩子 { subR->_bf = 0; parent->_bf = -1; subRL->_bf = 0; } else if (bf == -1)////subRL没有右孩子 { subR->_bf = 0; parent->_bf = 1; subRL->_bf = 0; } else//subRL有左孩子和右孩子或者左右孩子都没有 { parent->_bf = subRL->_bf = subR->_bf = 0; } }
插入的实现:
思想:
1,先遍历这棵树,查找要插入的地方
2,new一个新的结点,并连接起来
3,调节平衡因子:
我们在左边添加结点,该结点的父亲的平衡因子要减1
在右边添加结点,该结点的父亲的平衡因子要加1;
那么我们怎么样看插入就结点是否影响祖父呢,我们在这里可以找到平衡因子的规律:如果该结点的父亲的平衡因子从0变到了1或者-1,那么一定会影响祖父,如果该结点的父亲的平衡因子变为了0,那么该结点的插入只会影响父亲的平衡因子。
如果结点的平衡因子变为了2或者-2,那么该AVL树需要旋转,怎么判断该树是哪种旋转呢?我们在这里也可以通过平衡因子判断。
我们通过这张图parent为+2并且他的右孩子(subR)为正那么就是左单旋,如果它的右孩子(subR)为负,那么就是右左双旋。
如果parent为-2并且它的左孩子(subL)为负,那么它就是右单旋
如果它的左孩子(subL)为正,那么它就是左右双旋。
总结一下就是,同号是单旋,异号是双旋。
bool Insert(const K& key,const V& value) { if (_root == NULL) { _root = new Node(key,value); return true; } Node* cur = _root; Node* parent = NULL; while (cur) { if (cur->_key < key) { parent = cur; cur = cur->_right; } else if (cur->_key > key) { parent = cur; cur = cur->_left; } else { return false; } } //添加结点 cur = new Node(key,value); if (parent->_key < key) { parent->_right = cur; cur->_parent = parent; } else { parent->_left = cur; cur->_parent = parent; } //平衡因子 while (parent) { 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 = cur->_parent; } else { //2或-2,就要旋转 if (parent->_bf == -2) { if (cur->_bf == -1) { RotateR(parent); } else { RotateLR(parent); } } else { if (cur->_bf == 1) { RotateL(parent); } else { RotateRL(parent); } } break; } } return true; }
我们插入完成了,那么如何让判断这棵树是否是AVL树呢?
在这里我们写一个函数来实现:
算法思想:
判断是否是AVL树,我们必须要让这棵树满足AVL树的性质,即左子树和右子树的高度差的绝对值不能超过1,并且它的左子树和右子树也是AVL树。
我们首先实现一个函数求树的高度,然后让每棵树的右子树减去它的左子树。判断他们高度差的绝对值是否是不超过1.
int Depth() { return _Depth(_root); } int _Depth(Node* root) { if (root == NULL) { return 0; } int left = _Depth(root->_left); int right = _Depth(root->_right); return left > right ? left + 1 : right + 1; } bool IsBalance() { return _IsBalance(_root); } bool _IsBalance(Node* root)//时间复杂度为N*N { if (root == NULL) { return 0; } int left = _Depth(root->_left); int right = _Depth(root->_right); return abs(right - left) < 2 && _IsBalance(root->_left) && _IsBalance(root->_right); }
我们可以看到用递归实现判断是否是AVL树它的时间复杂度为N*N。
他把这棵树在求高度的时候遍历了一遍,递归的时候还在遍历,这样就使得该函数遍历了两遍,那么我们能否将这棵树的时间复杂度优化到O(N),我们可以想到斐波那契树时,我们倒着往上遍历,时间复杂度会低很多,那么我们的AVL树也可以先找到最左结点,然后倒着向上判断
,把每一层判断的高度的结果返回给上一层,所以我们的depth要加上引用。
代码实现:
//优化到O(N),把高度返回给上一层 bool _IsBalance(Node* root, int& depth) { if (root == NULL)//先判断是否为空 { depth = 0; return true; } int leftDepth = 0; if (_IsBalance(root->_left, leftDepth) == false)//如果它不平衡直接return false return false; int rightDepth = 0; if (_IsBalance(root->_right, rightDepth) == false) return false; if ((rightDepth - leftDepth) != root->_bf) { cout << "bf 异常" << root->_key << endl; //return false; } depth = rightDepth > leftDepth ? rightDepth + 1 : leftDepth + 1; return abs(rightDepth - leftDepth) < 2; }
完整代码:
#include<iostream>using namespace std;template<class K,class V>struct AVLTreeNode{ AVLTreeNode<K,V>* _left; AVLTreeNode<K,V>* _right; AVLTreeNode<K,V>* _parent; V _value; K _key; int _bf; AVLTreeNode(const K& key,const V& value) :_left(NULL) , _right(NULL) , _parent(NULL) , _key(key) , _bf(0) , _value(value) {}};template<class K,class V>class AVLTree{ typedef AVLTreeNode<K,V> Node;public: AVLTree() :_root(NULL) {} bool Insert(const K& key,const V& value) { if (_root == NULL) { _root = new Node(key,value); return true; } Node* cur = _root; Node* parent = NULL; while (cur) { if (cur->_key < key) { parent = cur; cur = cur->_right; } else if (cur->_key > key) { parent = cur; cur = cur->_left; } else { return false; } } //添加结点 cur = new Node(key,value); if (parent->_key < key) { parent->_right = cur; cur->_parent = parent; } else { parent->_left = cur; cur->_parent = parent; } //平衡因子 while (parent) { 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 = cur->_parent; } else { //2或-2,就要旋转 if (parent->_bf == -2) { if (cur->_bf == -1) { RotateR(parent); } else { RotateLR(parent); } } else { if (cur->_bf == 1) { RotateL(parent); } else { RotateRL(parent); } } break; } } return true; } void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; parent->_right = subRL; if (subRL) subRL->_parent = parent; subR->_left = parent; Node* parentparent = parent->_parent; parent->_parent = subR; if (parentparent == NULL) { _root = subR; subR->_parent = NULL; } else { if (parentparent->_left == parent) { parentparent->_left = subR; subR->_parent = parentparent; } else { parentparent->_right = subR; subR->_parent = parentparent; } } 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; subL->_right = parent; Node* parentparent = parent->_parent; parent->_parent = subL; if (parentparent == NULL) { _root = subL; subL->_parent = NULL; } else { if (parentparent->_left == parent) { parentparent->_left = subL; subL->_parent = parentparent; } else { parentparent->_right = subL; subL->_parent = parentparent; } } subL->_bf = parent->_bf = 0; } void RotateRL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; int bf = subRL->_bf; RotateR(parent->_right); RotateL(parent); if (bf == 1) { subR->_bf = 0; parent->_bf = -1; subRL->_bf = 0; } else if (bf == -1) { subR->_bf = 0; parent->_bf = 1; subRL->_bf = 0; } else { parent->_bf = subRL->_bf = subR->_bf = 0; } } void RotateLR(Node* parent) { Node* subL = parent->_left; Node* subLR = parent->_right; int bf = subLR->_bf; RotateL(parent->_left); RotateR(parent); if (bf == 1) { parent->_bf = 0; subL->_bf = -1; } else if (bf == -1) { subL->_bf = 0; parent->_bf = 1; } else { subL->_bf = parent->_bf = 0; } subLR->_bf = 0; } void InOrder() { _InOrder(_root); cout << endl; } void _InOrder(Node* root) { if (root == NULL) return; _InOrder(root->_left); cout << root->_key << " "; _InOrder(root->_right); } int Depth() { return _Depth(_root); } int _Depth(Node* root) { if (root == NULL) { return 0; } int left = _Depth(root->_left); int right = _Depth(root->_right); return left > right ? left + 1 : right + 1; } //bool IsBalance() //{ // return _IsBalance(_root); //} /*bool _IsBalance(Node* root)//时间复杂度为N*N { if (root == NULL) { return 0; } int left = _Depth(root->_left); int right = _Depth(root->_right); return abs(right - left) < 2 && _IsBalance(root->_left) && _IsBalance(root->_right); }*/ //优化到O(N),把高度返回给上一层 bool _IsBalance(Node* root, int& depth) { if (root == NULL)//先判断是否为空 { depth = 0; return true; } int leftDepth = 0; if (_IsBalance(root->_left, leftDepth) == false)//如果它不平衡直接return false return false; int rightDepth = 0; if (_IsBalance(root->_right, rightDepth) == false) return false; if ((rightDepth - leftDepth) != root->_bf) { cout << "bf 异常" << root->_key << endl; //return false; } depth = rightDepth > leftDepth ? rightDepth + 1 : leftDepth + 1; return abs(rightDepth - leftDepth) < 2; }private: Node* _root;};void test(){ int a[] = { 16, 30, 7, 11, 9, 26, 18, 14, 19 }; AVLTree<int, int> tree; for (int i = 0; i < sizeof(a) / sizeof(a[0]); ++i) { tree.Insert(a[i], i); } tree.InOrder();}
- AVL树的基本实现
- AVL树的基本实现
- Avl树的基本操作(c语言实现)
- AVL树的实现
- AVL树的实现
- AVL树的实现
- AVL 树的实现
- AVL树的实现
- AVL树的实现
- AVL树的实现
- AVL树的实现
- AVL树的实现
- AVL树的实现
- AVL树的实现
- AVL树的实现
- AVL树的实现
- AVL树的实现
- AVL树的实现
- 超实用的excel操作技巧,办公达人教你如何编辑文档来快、准、狠
- unity发布exe固定宽高比
- 设计模式学习总结:观察者模式(Observer Pattern)
- 南通青鸟java第一课笔记及心得
- PointWise.18.0.R4.20170925.Win64.&.Linux64.&.MacOSX 3CD
- AVL树的基本实现
- oracle监听服务开启错误
- 使用 man -k 有时查不到相关 api 解决办法
- jquery事件的切换,和多个事件的绑定
- 工单系列3------ Spring扫不到的数据库实体类
- zTree checkbox 异步加载
- 剑指offer---链表倒数K节点
- SecureCRT连接虚拟机中的Linux系统(Ubuntu)
- 设计模式--抽象工厂模式