标签:
AVL树是高度平衡的二叉搜索树,按照二叉搜索树(Binary Search Tree)的性质,AVL首先要满足:
若它的左子树不为空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不为空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉搜索树。
AVL树的性质:
(每个节点的平衡因子bf 等于右子树的高度减去左子树的高度 )
//// AVL树的节点类 template<class K,class V> class AVLTreeNode { K _key; V _value; int _bf;//平衡因子 -1,0,1(每个节点的平衡因子等于右子树的高度减去左子树的高度) AVLTreeNode<K, V>* _parent; //指向父节点的指针 AVLTreeNode<K, V>* _left; //指向左孩子的指针 AVLTreeNode<K, V>* _right; //指向右孩子的指针 AVLTreeNode(const K& key = K(), const V& value = V()) :_key(key) , _value(value) , _bf(0) , _parent(NULL) , _left(NULL) , _right(NULL) {} };
插入数据以后,父节点的平衡因子必然会被改变!
首先判断父节点的平衡因子是否满足性质1(-1<= parent->_bf <=1),如果满足,则要回溯向上检查插入该节点是否影响了其它节点的平衡因子值!
旋转的目的是为了降低高度
旋转的一般形态:
旋转至少涉及三层节点,所以至少要向上回溯一层 ,才会发现非法的平衡因子并进行旋转
向上回溯校验时,需要进行旋转的几种情况:
1. 当前节点的父节点的平衡因子等于2时,说明父节点的右树比左树高:
2. 当前节点的父节点的平衡因子等于-2时,说明父节点的右树比左树低:
//*** AVL树的插入算法 template<class K, class V> bool AVLTree<K,V>::Insert(const K& key, const V& value) { //1.空树 if (_root == NULL) { _root = new AVLTreeNode<K, V>(key, value); return true; } //2.AVL树不为NULL AVLTreeNode<K, V>* parent = NULL; AVLTreeNode<K, V>* cur = _root; //找到数据插入位置 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 AVLTreeNode<K, V>(key, value); cur->_parent = parent; if (parent->_key > key) parent->_left = cur; else parent->_right = cur; //更新平衡因子 if (cur == parent->_left) parent->_bf--; else if (cur == parent->_right) parent->_bf++; //检验平衡因子是否合法 if (parent->_bf == 0) return true; else if (parent->_bf == -1 || parent->_bf == 1) { // 回溯上升 更新祖父节点的平衡因子并检验合法性 cur = parent; parent = cur->_parent; } else // 2 -2 平衡因子不合法 需要进行旋转 降低高度 { if (parent->_bf == 2) { if (cur->_bf == 1) _RotateL(parent); else _RotateRL(parent); } else if (parent->_bf==-2) { if (cur->_bf == -1) _RotateR(parent); else _RotateLR(parent); } } }
左旋的两种情况:
1.parent有两个孩子:没有插入节点c之前处于平衡状态,插入c之后,平衡被破坏,向上回溯检验祖父节点的平衡因子,当其bf=2 时,以此节点为轴进行左旋
2.parent有一个孩子:没有插入节点a之前处于平衡状态,插入节点a之后,parent节点的平衡因子bf=2不满足AVL树的性质,要以parent为轴进行左旋
//左旋 template<class K, class V> void AVLTree<K, V>::_RotateL(AVLTreeNode<K, V>*& parent) { AVLTreeNode<K, V>* subR = parent->_right; AVLTreeNode<K, V>* subRL = subR->_left; AVLTreeNode<K, V>* ppNode = parent->_parent; //标记祖先节点 //1.构建parent子树 链接parent和subRL parent->_right = subRL; if (subRL) subRL->_parent = parent; //2.构建subR子树 链接parent和subR subR->_left = parent; parent->_parent = subR; //3.链接祖先节点和subR节点 subR->_parent = parent->_parent; if (ppNode== NULL) {//如果祖先节点为NULL,说明目前的根节点为subR _root = subR; } else { //将祖先节点和subR节点链接起来 if (parent == ppNode->_left) ppNode->_left = subR; else ppNode->_right = subR; } //4.重置平衡因子 parent->_bf = 0; subR->_bf = 0; //5.更新subR为当前父节点 parent = subR; }
1. parent既有左孩子又有右孩子:插入c之前处于平衡态,插入c之后parent的平衡因子变为-2,这时要以parent为轴进行旋转
2. parent只有一个孩子:插入a之前处于平衡状态,插入之后subL与parent的平衡因子被改变,需要以parent为轴进行旋转
///右旋 template<class K, class V> void AVLTree<K, V>::_RotateR(AVLTreeNode<K, V>*& parent) { AVLTreeNode<K, V>* subL = parent->_left; AVLTreeNode<K, V>* subLR = subL->_right; AVLTreeNode<K, V>* ppNode = parent->_parent; //标记祖先节点 //1.构建parent子树 将parent和subLR链接起来 parent->_left = subLR; if (subLR) subLR->_parent = parent; //2.构建subL子树 将subL与parent链接起来 subL->_right = parent; parent->_parent = subL; //3.将祖先节点与sunL链接起来 if (ppNode == NULL) { //如果祖先为NULL,说明当前subL节点为根节点 subL->_parent = NULL; _root = subL; } else { subL->_parent = parent->_parent; if (ppNode->_left == parent) ppNode->_left = subL; else if (ppNode->_right == parent) ppNode->_right = subL; } //4.重置平衡因子 parent->_bf = 0; subL->_bf = 0; //5.更新subL为当前父节点 parent = subL; }
1. parent只有一个孩子:在插入节点sunLR之前,AVL树处于平衡状态,左右子树高度差的绝对值不超过1。
由于插入了节点subLR导致grandfather的平衡因子变为-2,平衡树失衡,所以需要利用旋转来降低高度!
2. parent有两个孩子:没有插入subRL或subRR之前的AVL树一定是处于平衡状态的,并且满足AVL树的性质。
正是由于插入了节点subRL或者subRR,导致其祖先节点的平衡因子被改变,grandfather的平衡因子变为-2,平衡态比打破,需要进行旋转来降低高度!
parent有两个孩子时,要看插入的节点是subR的右孩子还是左孩子,双旋后对平衡因子的修改分两种情况:
//左右双旋 template<class K, class V> void AVLTree<K, V>::_RotateLR(AVLTreeNode<K, V>*& parent) { AVLTreeNode<K, V>* pNode = parent; AVLTreeNode<K, V>* subL = parent->_left; AVLTreeNode<K, V>* subLR = subL->_right; int bf = subLR->_bf; _RotateL(parent->_left); _RotateR(parent); if (bf == 1) { pNode->_bf = 0; subL->_bf = -1; } else if (bf == -1) { pNode->_bf = 1; subL->_bf = 0; } else { pNode->_bf = 0; subL->_bf = 0; } }
1. parent只有一个孩子:由于节点subRL的插入破坏了AVL树的平衡,parent的平衡因子变为2,需要利用旋转来降低高度!
2.parent有两个孩子:没有插入subLL或者subLR之前的AVL树一定是处于平衡状态的,并且满足AVL树的性质。
正是由于插入了节点subLL或者subLR,导致其祖先节点的平衡因子被改变,grandfather的平衡因子变为2,平衡态比打破,需要进行旋转来降低高度!
parent有两个孩子时,要看插入的节点是subL的右孩子还是左孩子,双旋后对平衡因子的修改分两种情况:
//右左双旋 template<class K, class V> void AVLTree<K, V>::_RotateRL(AVLTreeNode<K, V>*& parent) { AVLTreeNode<K, V>* pNode = parent; AVLTreeNode<K, V>* subR= parent->_left; AVLTreeNode<K, V>* subRL = subR->_right; int bf = subRL->_bf; _RotateR(parent->_left); _RotateL(parent); if (bf == 1) { pNode->_bf = 0; subR->_bf = -1; } else if (bf == -1) { pNode->_bf = 1; subR->_bf = 0; } else { pNode->_bf = 0; subR->_bf = 0; } }
标签:
原文地址:http://www.cnblogs.com/Lynn-Zhang/p/5643797.html