AVL树
二叉查找树在极端情况下,会退化为链表,比如一个排好序的数组,构建成二叉树后,就是一颗全部左倾或右倾的树,这时候查找的时间为O(N),AVL树是带有平衡条件的二叉查找树,它的每个节点左子树和右子树的高度最多相差1,它会保证树的高度为O(logN),所以在查找时,能保证最坏情况下时间为O(logN),AVL树节点中需要一个成员存储高度属性。
AVL树节点可以定义如下:
1 struct AVLTreeNode { 2 AVLTreeNode* pLeft; 3 AVLTreeNode* pRight; 4 int nData; 5 int height; 6 };
计算节点高度代码如下:
1 int AVLTreeHeight(AVLTreeNode* pNode) { 2 if (pNode == nullptr) 3 return 0; 4 else { 5 return Max(AVLTreeHeight(pNode->pLeft), AVLTreeHeight(pNode->pRight)) + 1; 6 } 7 }
要维护树的平衡,每个节点左右节点的高度差不能超过1,计算一个节点的高度差代码如下:
1 int AVLTreeNodeFactor(AVLTreeNode* pNode) { 2 //left sub node's height - right sub node's height 3 if (pNode == nullptr) 4 return 0; 5 6 int leftHeight = AVLTreeHeight(pNode->pLeft); 7 int rightHeight = AVLTreeHeight(pNode->pRight); 8 9 return leftHeight - rightHeight; 10 }
在讨论如何修复树的平衡之前,先来讨论下旋转,旋转是对树的修正,是AVL树,红黑树维持平衡的基础操作。
旋转后依然维持二叉树性质
左旋转代码如下:
1 AVLTreeNode* AVLTreeNodeLeftRotate(AVLTreeNode* pNode) { 2 if (pNode == nullptr) 3 return nullptr; 4 5 AVLTreeNode* pTemp = pNode->pRight; 6 pNode->pRight = pTemp->pLeft; 7 pTemp->pLeft = pNode; 8 9 //recalc pTemp and pNode 's height 10 pTemp->height = AVLTreeHeight(pTemp); 11 pNode->height = AVLTreeHeight(pNode); 12 13 return pTemp; 14 }
右旋转代码如下:
1 AVLTreeNode* AVLTreeNodeRightRotate(AVLTreeNode* pNode) { 2 if (pNode == nullptr) 3 return nullptr; 4 5 AVLTreeNode* pTemp = pNode->pLeft; 6 pNode->pLeft = pTemp->pRight; 7 pTemp->pRight = pNode; 8 9 //recalc pTemp and pNode 's height 10 pTemp->height = AVLTreeHeight(pTemp); 11 pNode->height = AVLTreeHeight(pNode); 12 return pTemp; 13 }
当插入或删除节点时,有四种树不平衡的情况,下面逐一讨论。
一、LL和LR
某节点在插入或删除后,可能会影响到父节点或更高层的节点的高度差 ,如果高度差大于1,则继续判断高度差大于1的节点的左子节点高度差,如果左子节点高度差大于0,我们称这种情况为LL,如果左子节点高度差小于0,则称之为LR。
以上图所示,在插入节点D后,导致A节点左边高度为2,右边高度为0,导致失去平衡,对A右旋转即可恢复平衡
而对于LR的情形,在插入E节点后,A失去平衡,这时候,先对A的左子节点,也就是B进行左旋转,转化为LL的情形,再对A进行右旋转。
二、RR和RL
这两种情形只是LL和LR的镜像问题,取对称操作 就可以了。LL和RR这两种情况是不平衡发生在外侧,而LR和RL这两种情况是不平衡发生在树内侧
拿代码来表示这四种情形如下:AVLTreeReblance的参数pNode为失去平衡的节点,返回的节点是在旋转前以pNode为根节点的子树在旋转后新的子树根节点。就拿LR为例,传入的参数是A节点,返回的参数是B节点。
1 AVLTreeNode* AVLTreeReblance(AVLTreeNode* pNode) { 2 if (pNode == nullptr) 3 return nullptr; 4 5 int nFactor = AVLTreeNodeFactor(pNode); 6 if (nFactor > 1) { 7 //LL or LR 8 if (AVLTreeNodeFactor(pNode->pLeft) >= 0)//LL 9 return AVLTreeNodeRightRotate(pNode); 10 else {//LR 11 //first:left rotate on sub left 12 pNode->pLeft = AVLTreeNodeLeftRotate(pNode->pLeft); 13 //second: right rotate on self 14 return AVLTreeNodeRightRotate(pNode); 15 } 16 } 17 else if (nFactor < -1) { 18 //RR or RL 19 if (AVLTreeNodeFactor(pNode->pRight) <= 0)//RR 20 return AVLTreeNodeLeftRotate(pNode); 21 else { 22 //RL 23 //first:right rotate on sub left 24 pNode->pRight = AVLTreeNodeRightRotate(pNode->pRight); 25 //second: right rotate on self 26 return AVLTreeNodeLeftRotate(pNode); 27 } 28 } 29 else 30 return pNode; 31 }
AVL树的插入
AVL树在插入后,就需要更新从新插入节点到根节点路径上那些节点的高度信息,并且对这条路径上的所有节点进行重新平衡。采用递归插入,可以通过递归的回溯来完成。
1 AVLTreeNode* AVLTreeInsert(AVLTreeNode* pRoot, int nData) { 2 if (pRoot == nullptr) { 3 pRoot = new AVLTreeNode; 4 pRoot->pLeft = pRoot->pRight = nullptr; 5 pRoot->nData = nData; 6 pRoot->height = 0; 7 } 8 else { 9 if (nData > pRoot->nData) { 10 pRoot->pRight = AVLTreeInsert(pRoot->pRight, nData); 11 } 12 else if (nData < pRoot->nData) { 13 pRoot->pLeft = AVLTreeInsert(pRoot->pLeft, nData); 14 } 15 } 16 pRoot->height = AVLTreeHeight(pRoot);//更新节点高度 17 pRoot = AVLTreeReblance(pRoot);//对节点重新平衡 18 return pRoot; 19 }
AVL树的删除
AVL树的删除跟普通二叉查找树删除流程一样,只是在删除完后,要对新子树根节点进行平衡操作。同样的,路径上的节点高度也要更新,并对被删除节点到根节点做修复
1 AVLTreeNode* AVLTreeDelete(AVLTreeNode* pRoot, int nData) { 2 if (pRoot == nullptr) 3 return nullptr; 4 5 AVLTreeNode* pResult = nullptr; 6 if (nData > pRoot->nData) { 7 pRoot->pRight = AVLTreeDelete(pRoot->pRight, nData); 8 pResult = pRoot; 9 } 10 else if (nData < pRoot->nData) { 11 pRoot->pLeft = AVLTreeDelete(pRoot->pLeft, nData); 12 pResult = pRoot; 13 } 14 else { 15 if (pRoot->pRight && pRoot->pLeft) { 16 AVLTreeNode* pTemp = AVLTreeMinimumNode(pRoot->pRight); 17 pRoot->nData = pTemp->nData; 18 pRoot->pRight = AVLTreeDelete(pRoot->pRight, pTemp->nData); 19 pResult = pRoot; 20 } 21 else { 22 AVLTreeNode* pTemp = (pRoot->pRight == nullptr)?pRoot->pLeft:pRoot->pRight; 23 delete pRoot; 24 pResult = pTemp; 25 } 26 } 27 if (pResult) 28 pResult->height = AVLTreeHeight(pResult); 29 return AVLTreeReblance(pResult); 30 }