平衡二叉树:
AVL树属于二叉查找树,在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(logn),增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。
二叉查找树的查找和插入操作在最坏情况下复杂度为O(N),而AVL树最坏时仍然为O(lgN)。
平衡二叉树(Self-Balancing Binary Search Tree或 Height-Balancing Binary Search Tree),是一种二叉排序树,其中每一个结点的左子树和右子树的高度差至多等于1,
从平衡二叉树的英文名字,你也可以体会到,它是一种高度平衡的二叉排序树,那什么叫做高度平衡呢?意思是说,要么它是一棵空树,要么它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。我们将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BF(BalanceFactor),那么平衡二叉树上所有结点的平衡因子只可能是-1、0和1。只要二叉树上有一个结点的平衡因子的绝对值大于1,则该二叉树就是不平衡的。
看图8.7.2,为什么图1是平衡二叉树,而图2却不是呢?这里就是考查我们对平衡二叉树的定义的理解,它的前提首先是一棵二叉排序树,右上图的59比58大,却是58的左子树,这是不符合二叉排序树的定义的。图3不是平衡二叉树的原因就在于,结点58的左子树高度为2,而右子树为空,一者差大于了绝对值1,因此它也不是平衡的。而经过适当的调整后的图4,它就符合了定义,因此它是平衡二叉树。
距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树,我们称为最小不平衡子树。图8.7.3,当新插入结点37时,距离它最近的平衡因子绝对值超过1的结点是58(即它的左子树高度2减去右子树高度0),所以从58开始以下的子树为最小不平衡子树。
以58为结点,左子树深度与右子树相差大于1,需要旋转平衡,在以58的左子树47为结点,右边深度不大于左边深度,所以只需要左单旋就能是二叉树重新平衡,平衡步骤,将47的右子树保存在58的左子树中,47的右子树指向58,原本指向58的指针指向47。
平衡二叉树实现原理:
平衡二又树构建的基本思想就是在构建二叉排序树的过程中,每当插入一个结点时,先检查是否因插入而破坏了树的平衡性,若是,则找出最小不平衡子树。在保持二叉排序树特性的前提下,调整最小不平衡子树中各结点之间的链接关系,进行相应的旋转,使之成为新的平衡子树。
1.左单旋:(左左旋转)
左子树的左边节点插入新结点导致左子树失衡就需要左左旋转即左单旋,将2的右子树保存在3的左子树中(此处没有),将2的右子树指针指向3结点,将原本指向3结点的指针指向2。
什么时候需要左单旋?当插入的新结点或删除结点,使得左边子树失衡的情况,且以失衡点的左子树为结点,其左边高度大于或等于右边高度,就需要左单旋,否则就转化为情况4,需要右左旋(先以失衡点的左子树为结点右旋,在以失衡点为结点左旋)
C++代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
//************************************ //左单旋 // 50(K2) 40(K1) // / / \ // 40(K1) -> (N)30 50(K2) // / \ / // (N)30 44(N2) 44(N2) // 1.将K1的右子树保存在K2的左子树中 // 2.将K1的右子树置为K2 // 3.将原本指向K2的指针指向K1 //************************************ // 函数名称: SingRotateLeft // 功能: 左单旋 // FullName: CBalanceBinaryTree::SingRotateLeft // 继承关系: private // 返回值: void // 参数: PTREE_NODE & pNodeK2 //************************************ void CBalanceBinaryTree::SingRotateLeft(PTREE_NODE &pNodeK2) //左单旋 { PTREE_NODE pNodeK1 = pNodeK2->pLchild; //定义临时指针变量K1指向K2的左子树 pNodeK2->pLchild = pNodeK1->pRchild; //将指向K2左子树的指针指向K1的右子树 pNodeK1->pRchild = pNodeK2; //将指向K1右子树的指针指向K2 pNodeK2 = pNodeK1; //将原本指向K2的指针指向K1 } |
2.右单旋(右右旋)
右子树的右边结点失衡,插入新结点⑤导致③结点处失衡,左边深度为0,右边深度为2因此③结点处左右子树高度差大于1失去平衡,因此需要以③做结点旋转,此种情况需要右单旋,将④的左子树保存在③的右子树中(此处为空),将④结点的左子树指向③结点,将原本指向③结点的指针指向④结点。
什么时候需要做右单旋,当插入值大于根结点,往右子树中插入值时,新插入结点造成右子树失衡的情况,以失衡点做判断其右边高度与左边相差大于1时,就需要旋转平衡,在以失衡点的右子树为结点,如果其右边高度大于或等于左边,仅需要以失衡点为结点做有单旋;否则就转换成情况3,失衡点右子树的左边高度大于右边的情况,就需做左右旋(首先以失衡点的右子树为结点左旋,再以失衡点为结点右旋)。
C++ Code 原理
1 2 3 4 5 6 7 8 9 10 |
//************************************ //右单旋 // 40(K2) 50(K1) // \ / \ // 50(K1) -> (K2)40 70(N2) // / \ \ // (N)49 70(N2) 49(N) // 1.将K1的左子树保存在K2的右子树中 // 2.将K1的左子树置为K2 // 3.将原本指向K2的指针指向K1 //************************************ |
C++ Code 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//************************************ // 函数名称: SingRotateRight // 功能: 右单旋 // FullName: CBalanceBinaryTree::SingRotateRight // 继承关系: private // 返回值: void // 参数: PTREE_NODE & pNodeK2 //************************************ void CBalanceBinaryTree::SingRotateRight(PTREE_NODE &pNodeK2) //右单旋 { PTREE_NODE pNodeK1 = pNodeK2->pRchild; //定义零时指针变量K1指向k2的右子树 pNodeK2->pRchild = pNodeK1->pLchild; //指向K2右子树的指针指向K1的左子树 pNodeK1->pLchild = pNodeK2; //指向K1左子树的指针指向K2 pNodeK2 = pNodeK1; //指向K2的指针指向K1??? } |
3.左右旋(右子树的左边结点)
右子树的左边插入新结点造成右子树失衡,如下图,新插入结点18,造成右子树结点15的左子树高度为0右子树高度为2,高度差大于1了,故造成结点15失衡,此种情况就需要左右旋。
首先以15结点的右子树20做结点左单旋,左单旋步骤为:将18结点的右子树保存在20结点的左子树中(此处为空),再将18结点的右子树指针指向20结点,最后将原本指向20结点的指针指向18结点完成左单旋。
其次将15结点右单旋,右单旋步骤为:将18结点的左子树保存在15结点的右子树中,再将18结点的左子树指针指向15结点(此处为空),最后将原本指向15结点的指针指向18结点,完成右单旋至此整棵树重新平衡。
代码实现:
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void CBalanceBinaryTree::DoubleRotateLR(PTREE_NODE &pNodeK3) //左右旋 { //左右旋 // 90 (K3) 90 (K3) 95 // \ \ / \ // 100 左旋 95 右旋 90(K3) 100 // / ( ——> ) / \ ( ——> ) \ // 95 N 100 N // / // N SingRotateLeft(pNodeK3->pRchild);//以右孩子树(100)为结点左旋 SingRotateRight(pNodeK3);//以当前结点右旋 } |
4.右左旋(左子树的右边结点)
左子树的右边结点新插入结点导致左子树失衡,如下图新插入结点3导致结结点5失衡,所以先要将失衡点的左子树2作为结点右单旋,再将失衡结点5左单旋。
结点2右单旋步骤为:将结点3的左子树保存在结点2的右子树中(此处为空),然后将3结点的左子树指向2结点,最后将原本指向2结点的指针指向3结点完成右单旋
然后在将结点5做左单旋步骤为:将结点3的右子树保存在结点5的左子树中(此处为空),然后将结点将结点3的右子树指针指向结点5,最后将结点原本指向结点5的指针指向结点3完成左单旋,至此整棵树重新平衡。
代码实现:
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void CBalanceBinaryTree::DoubleRotateRL(PTREE_NODE &pNodeK3) //右左旋 { //右左旋 // 90(K3) 90(K3) 85 // / / / \ // 80 右旋 85 左旋 80 90(K3) // \ ( ——> ) / \ ( ——> ) / // 85 80 N N // \ // N SingRotateRight(pNodeK3->pLchild);//以左孩子树(80)为结点右单旋旋 SingRotateLeft(pNodeK3);//以当前结点左单旋 } |
理解了什么是单旋和双旋就需要判断什么时候做旋转,选择什么旋转来是二叉树重新平衡,我们由以上的理解首先可以分两种情况。第一种是左子树失衡,第二种是右子树失衡。下面分两种情况做判断选择合理的旋转方式使二叉树重新平衡。
左子树失衡(相对根结点):左子树失衡又分为两种情况(左单旋和右左旋),下面再细分两种情况,首先是以左子树为结点,其左子树的与右子树的高度差大于1,则该结点失衡需要旋转平衡,再判断,如果该左子树的左边深度大于或等于右边深度,则只需要左单旋(上述情况1);如果右边深度大于左边深度则需要右左旋(上述情况4)。
右子树失衡(相对根结点):右子树失衡也分为两种情况(右单旋和左右旋),下面分两种情况判断;
首先以右子树为结点,其右子树与左子树的高度差大于1,该结点失衡需要旋转平衡,再判断其右子树
往下的如果右边的深度大于或等于左边深度,则需要右单旋(上述情况2);如果左边深度大于或等于右边深度则需要左右旋(上述情况3)。
有以上理论就可以进行二叉树的插入数据和删除数据操作了:
数据插入:
跟二叉排序树的插入基本一样,不过再插入数据以后需要做判断,是否平衡,下面看代码。
C++ Code 代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
//************************************ // 函数名称: Insert // 功能: 插入数据(对外接口) // FullName: CBalanceBinaryTree::Insert // 继承关系: public // 返回值: bool // 参数: int nData(插入数据) //************************************ bool CBalanceBinaryTree::Insert(int nData)//插入数据(对外接口) { if (!Insert(m_TreeNode, nData)) { //插入失败 return false; } //插入成功 return true; } //************************************ // 函数名称: Insert // 功能: 插入数据 // FullName: CBalanceBinaryTree::Insert // 继承关系: private // 返回值: bool // 参数: PTREE_NODE & TreeNode(树) // 参数: int nData(数据) //************************************ bool CBalanceBinaryTree::Insert(PTREE_NODE &TreeNode, int nData)//插入数据 { if (!TreeNode)//查找到结点为空就插入结点 { //新添加结点 TreeNode = new TREE_NODE; memset(TreeNode, 0, sizeof(TREE_NODE)); TreeNode->Data = nData; m_nCount++; return true; } //判断是否小于根若小于根则从左孩子树开始查找寻找插入点 if (nData < TreeNode->Data) { Insert(TreeNode->pLchild, nData); //插入递归返回判断是否符合平衡二叉树条件(优点能找到最小的不平衡结点) if(2 == (GetDepth(TreeNode->pLchild) - GetDepth(TreeNode->pRchild))) { //左子树与右子树深度差大于1的情况需要旋转(分左单旋和右左旋)
if (GetDepth(TreeNode->pLchild->pRchild) > GetDepth(TreeNode->pLchild->pLchild)) { //如果以左孩子树为结点,其右孩子树深度大于左孩子树深度则需要右左旋(情况4) DoubleRotateRL(TreeNode); //右左旋 } else { //如果其右孩子树不大于左孩子树深度则仅需要左单旋就行了(情况1) SingRotateLeft(TreeNode); //左单旋 } } } //判断是否大于根如果成立就从右孩子树一侧开始查找插入点 else if (nData > TreeNode->Data) { Insert(TreeNode->pRchild, nData); //插入递归返回判断新插入叶子后是否符合平衡二叉树条件(优点能找到最小不平衡点) if(2 == (GetDepth(TreeNode->pRchild) - GetDepth(TreeNode->pLchild))) { //右子树与左子树的深度差大于1的情况就需要旋转(分右单旋和左右旋)
if (GetDepth(TreeNode->pRchild->pLchild) > GetDepth(TreeNode->pRchild->pRchild)) { //以右子树为结点,其左子树深度若大于右子树需要右左旋(情况3) DoubleRotateLR(TreeNode); //左右旋 } else { //以右子树为结点,其左子树深度不大于右子树仅需要右旋(情况2) SingRotateRight(TreeNode); //右单旋 } } } else //等于根(即有和插入新结点的值相等的情况就直接返回) return false; } |
数据删除:
俗话说请神容易送神难,平衡二叉树的真正难点是在数据的删除。基本原理是在数据删除,数据的删除方法跟添加方法也类似,当删除一个结点的时候,可能会引起祖先结点的失衡,所以在每次“结点”回退的时候计算结点高度,判断是否需要旋转平衡。
此处注意两种情况,如果删除的是叶子结点,返回后也要判断,是否平衡;另外一种情况是删除的是非叶子结点,就要用左子树的最大值,或者右子树的最小值替换需要删除的结点,如果用左子树的最大值替换,就要以原本删除结点的左子树为结点向下查找删除原有的最大值结点;如果用右子树最小值替换就需要以原本删除结点的右子树为结点向下查找删除原有的最小值结点。删除结点后,逐层递归返回判断二叉树的平衡性。
C++ Code代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
//************************************ // 函数名称: DeleteNode // 功能: 删除数据(对外接口) // FullName: CBalanceBinaryTree::DeleteNode // 继承关系: public // 返回值: bool // 参数: int nData(需要删除的数据) //************************************ bool CBalanceBinaryTree::DeleteNode(int nData) { if (!DeleteNode(m_TreeNode, nData)) { return false; } m_nCount--; return true; } //************************************ // 函数名称: DeleteNode // 功能: 数据删除 // FullName: CBalanceBinaryTree::DeleteNode // 继承关系: private // 返回值: bool // 参数: PTREE_NODE & TreeNode(树) // 参数: int nData(需要删除的数据) //************************************ bool CBalanceBinaryTree::DeleteNode(PTREE_NODE &TreeNode, int nData) { //用来判断是否删除成功(例如,在树中查找不到数据就返回false) bool bSuccess = true; if (!TreeNode)//先决条件 { //如果树为空就直接返回 return false; } //是否找到数据 if (nData == TreeNode->Data) { //1.删除的是叶子节点就直接删除 if (!TreeNode->pLchild && !TreeNode->pRchild)//递归出口 { delete TreeNode; TreeNode = nullptr; return true; } //2.有左子树(默认获取左子树中的最大值来替换当前结点) else if (TreeNode->pLchild) { //定义一个变量指向左子树中的最大值结点 PTREE_NODE pMaxData = GetMaxpLchild(TreeNode->pLchild); //把当前结点的值替换为最大值结点的值 TreeNode->Data = pMaxData->Data; //再以当前结点的左子树为结点,向下删除最大值结点 bSuccess = DeleteNode(TreeNode->pLchild, pMaxData->Data); //删除结点后逐层递归返回判断二叉树是否平衡 if(2 == (GetDepth(TreeNode->pLchild) - GetDepth(TreeNode->pRchild))) { //如果结点的左子树的高度比右子树的高度大于1的情况就需要旋转平衡(右左旋或左单旋) if (GetDepth(TreeNode->pLchild->pRchild) > GetDepth(TreeNode->pLchild->pLchild)) { //第一种情况,以左子树为结点,其右边高度大于左边就需要右左旋 DoubleRotateRL(TreeNode); //右左旋 } else { //第二种情况,以左子树为结点,如果右边高度不大于左边高度仅左单旋就可以了 SingRotateLeft(TreeNode); //左单旋 } } } //3.没有左子树有右子树(获取右子树中的最小值,以右子树为结点向左查找到尽头就是最小值) else if(TreeNode->pRchild) { //定义一个变量指向右子树中的最小值 PTREE_NODE pMinData = GetMinpRchild(TreeNode->pRchild); //用右子树中的最小值替换需要删除结点的值 TreeNode->Data = pMinData->Data; //再以右子树为结点删除原有最小值 bSuccess = DeleteNode(TreeNode->pRchild, pMinData->Data);
//删除结点后逐层递归返回二叉树平衡条件判断判断 if(2 == (GetDepth(TreeNode->pRchild) - GetDepth(TreeNode->pLchild))) { //如果结点的右子树与左子树的高度差大于1就需要旋转平衡(左右旋和右单旋) if (GetDepth(TreeNode->pRchild->pLchild) > GetDepth(TreeNode->pRchild->pRchild)) { //第一种情况,以右子树的结点,其左边的高度大于右边就需要左右旋 DoubleRotateLR(TreeNode); //左右旋 } else { //第二种情况,以右子树为结点,其左边高度不大于右边高度时仅右旋就可以了 SingRotateRight(TreeNode); //右单旋 } } } } else if (nData < TreeNode->Data) { //如果删除的数据小于当前结点的数据就,从当前结点的左子树向下递归查找删除 bSuccess = DeleteNode(TreeNode->pLchild, nData); //删除的是左子树的叶子结点,只能造成右子树不平衡,所以需要判断右子树失衡,左右旋和右单旋的情况 if(2 == (GetDepth(TreeNode->pRchild) - GetDepth(TreeNode->pLchild))) { //如果结点的右子树与左子树的高度差大于1就需要旋转平衡(左右旋和右单旋) if (GetDepth(TreeNode->pRchild->pLchild) > GetDepth(TreeNode->pRchild->pRchild)) { //第一种情况,以右子树的结点,其左边的高度大于右边就需要左右旋 DoubleRotateLR(TreeNode); //左右旋 } else { //第二种情况,以右子树为结点,其左边高度不大于右边高度时仅右旋就可以了 SingRotateRight(TreeNode); //右单旋 } } } else if (nData > TreeNode->Data) { //如果删除的数据大于当前结点的数据就,从当前结点的右子树向下递归查找删除 bSuccess = DeleteNode(TreeNode->pRchild, nData);
//删除的是右子树的叶子结点,只能造成左子树不平衡,所以需要判断左子树失衡,右左旋和左单旋的情况 if(2 == (GetDepth(TreeNode->pLchild) - GetDepth(TreeNode->pRchild))) { //如果结点的左子树的高度比右子树的高度大于1的情况就需要旋转平衡(右左旋或左单旋) if (GetDepth(TreeNode->pLchild->pRchild) > GetDepth(TreeNode->pLchild->pLchild)) { //第一种情况,以左子树为结点,其右边高度大于左边就需要右左旋 DoubleRotateRL(TreeNode); //右左旋 } else { //第二种情况,以左子树为结点,如果右边高度不大于左边高度仅左单旋就可以了 SingRotateLeft(TreeNode); //左单旋 } } } if (bSuccess == false)//判断是否删除成功 return bSuccess; else return true; } |
获取左子树最大值结点:
C++ Code 代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
//************************************ // 函数名称: GetMaxpLchild // 功能: 获取左子树的最大值节点 // FullName: CBalanceBinaryTree::GetMaxpLchild // 继承关系: private // 返回值: CBalanceBinaryTree::PTREE_NODE // 参数: PTREE_NODE TreeNode //************************************ CBalanceBinaryTree::PTREE_NODE CBalanceBinaryTree::GetMaxpLchild(PTREE_NODE TreeNode) { //左子树向右右到尽头就是最大值结点 ////1.(递归方法) //if (!TreeNode->pRchild)//直到右子树为空结束递归 //{ // return TreeNode; //} //GetMaxpLchild(TreeNode->pRchild); //
//2.(非递归方法) while(TreeNode->pRchild != NULL)//直到右子树为空结束 { TreeNode = TreeNode->pRchild; } return TreeNode; } |
获取右子树最大值结点:
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
//************************************ // 函数名称: GetMinpRchild // 功能: //获取右子树的最小值节点 // FullName: CTreeClass::GetMinpRchild // 继承关系: private // 返回值: CTreeClass::PTREE_NODE // 参数: PTREE_NODE TreeNode //************************************ CBalanceBinaryTree::PTREE_NODE CBalanceBinaryTree::GetMinpRchild(PTREE_NODE TreeNode) //获取右子树的最小值节点 { //右子树向左左到尽头就是最小值结点 //1.(递归方法) //if (!TreeNode->pLchild)//直到左子树为空结束递归 //{ // return TreeNode; //} //GetMinpRchild(TreeNode->pLchild); //2.(非递归方法) while (TreeNode->pLchild != NULL) { TreeNode = TreeNode->pLchild; } return TreeNode; } |
获取树的深度:
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
//************************************ // 函数名称: GetDepth // 功能: //获得树的深度 // FullName:CTreeClass::GetDepth // 继承关系: private // 返回值: int // 参数: PTREE_NODE TreeNode //************************************ int CBalanceBinaryTree::GetDepth(PTREE_NODE TreeNode) //获得树的深度 { if (!TreeNode)//空树就直接返回(做递归返回条件) { return 0; } //获取左子树深度 int LChildDepth = GetDepth(TreeNode->pLchild); //获取右子树深度 int RChildDepth = GetDepth(TreeNode->pRchild); //判断最大深度 int MaxDepth = LChildDepth > RChildDepth ? LChildDepth : RChildDepth; //如果左或右子树有元素则MaxDepth自增1 MaxDepth++; return MaxDepth; } |
其他功能基本与二叉排序树的的代码一致。下面附上完整代码:
C++ Code 平衡二叉树,增删改查
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
|
#pragma once #include"memory.h" class CBalanceBinaryTree { typedef struct _TREE_NODE { int Data; //数据 _TREE_NODE * pLchild; //左子树 _TREE_NODE * pRchild; //右子树 }TREE_NODE, *PTREE_NODE;
public: CBalanceBinaryTree() { m_TreeNode=NULL; m_nCount = 0; }; ~CBalanceBinaryTree(){};
public:
bool IsEmpty(); //判断树是否为空 bool Insert(int nData); //插入数据 bool DeleteNode(int nData); //删除数据 bool DestroyTree(); //销毁一颗树 void ClearTree(); //清空一棵树 int GetDepth(); //获得树的深度 void PreOrderTraverse(); //前序遍历整棵树 void InOrderTraverse(); //中序遍历整棵树 void PostOrderTraverse(); //后序遍历整棵树
private:
bool Insert(PTREE_NODE &pNode, int nData); //插入数据 bool AddNode(PTREE_NODE &pNode, int nData); //添加节点 bool DeleteNode(PTREE_NODE &pNode, int nData); //删除数据 bool DestroyTree(PTREE_NODE pNode); //销毁一颗树 void ClearTree(PTREE_NODE &pNode); //清空一棵树 int GetDepth(PTREE_NODE pNode); //获得树的深度 void PreOrderTraverse(PTREE_NODE pNode); //前序遍历整棵树 void InOrderTraverse(PTREE_NODE pNode); //中序遍历整棵树 void PostOrderTraverse(PTREE_NODE pNode); //后序遍历整棵树 PTREE_NODE GetMaxpLchild(PTREE_NODE pNode); //获取左子树的最大值节点 PTREE_NODE GetMinpRchild(PTREE_NODE pNode); //获取右子树的最小值节点
//////////////////////////////////////////////////////////////////////////
void LChildRotate(PTREE_NODE& pNode); //添加/删除左子树时先平衡 void RChildRotate(PTREE_NODE& pNode); //添加/删除右子树时先平衡 void SingRotateLeft(PTREE_NODE &pK2); //左单旋 void SingRotateRight(PTREE_NODE &pK2); //右单旋 void DoubleRotateLR(PTREE_NODE &pK); //左右旋 void DoubleRotateRL(PTREE_NODE &pK); //右左旋
private: PTREE_NODE m_TreeNode; //根节点 int m_nCount; //无素个数 }; //************************************ // 函数名称: SingRotateLeft // 功能: 左单旋 // FullName:CBalanceBinaryTree::SingRotateRight // 继承关系: private // 返回值: void // 参数: PTREE_NODE & pNodeK2 //************************************ //左单旋 // 50(K2) 40(K1) // / / \ // 40(K1) -> (N)30 50(K2) // / \ / // (N)30 44(N2) 44(N2) // 1.将K1的右子树保存在K2的左子树中 // 2.将K1的右子树置为K2 //************************************ void CBalanceBinaryTree::SingRotateLeft(PTREE_NODE &pNodeK2) //左单旋 { PTREE_NODE pNodeK1 = pNodeK2->pLchild;//定义临时指针变量指向K2的左子树 pNodeK2->pLchild = pNodeK1->pRchild;//将指向K2左子树的指针指向K1的右子树 pNodeK1->pRchild = pNodeK2; //将指向K1右子树的指针指向K2 pNodeK2 = pNodeK1; //将原本指向K2的指针指向K1 } //************************************ // 函数名称: SingRotateRight // 功能: 右单旋 // FullName:CBalanceBinaryTree::SingRotateLeft // 继承关系: private // 返回值: void // 参数: PTREE_NODE & pNodeK2 //************************************ //右单旋 // 40(K2) 50(K1) // \ / \ // 50(K1) -> (K2)40 70(N2) // / \ \ // (N)49 70(N2) 49(N) // 1.将K1的左子树保存在K2的右子树中 // 2.将K1的左子树置为K2 //************************************ void CBalanceBinaryTree::SingRotateRight(PTREE_NODE &pNodeK2) //右单旋 { PTREE_NODE pNodeK1 = pNodeK2->pRchild; //定义零时指针变量指向k2的右子树 pNodeK2->pRchild = pNodeK1->pLchild; //指向K2右子树的指针指向K1的左子树 pNodeK1->pLchild = pNodeK2; //指向K1左子树的指针指向K2 pNodeK2 = pNodeK1; //指向K2的指针指向K1 } void CBalanceBinaryTree::DoubleRotateRL(PTREE_NODE &pNodeK3) //右左旋 { //右左旋 // 90 90 85 // / / / \ // 80 右旋 85 左旋 80 90 // \ ( ——> ) / \ ( ——> ) / // 85 80 N N // \ // N SingRotateRight(pNodeK3->pLchild);//以左孩子树为结点右旋 SingRotateLeft(pNodeK3);//以当前结点左旋 } void CBalanceBinaryTree::DoubleRotateLR(PTREE_NODE &pNodeK3) //左右旋 { //左右旋 // 90 90 95 // \ / / \ // 100 左旋 95 右旋 90 100 // / ( ——> ) / \ ( ——> ) \ // 95 N 100 N // / // N SingRotateLeft(pNodeK3->pRchild);//以右孩子树为结点左旋 SingRotateRight(pNodeK3);//以当前结点右旋 } //************************************ // 函数名称: Insert // 功能: 插入数据(对外接口) // FullName:CBalanceBinaryTree::Insert // 继承关系: public // 返回值: bool // 参数: int nData(插入数据) //************************************ bool CBalanceBinaryTree::Insert(int nData) //插入数据(对外接口) { if (!Insert(m_TreeNode,nData)) {//插入失败 return false; } return true; } //************************************ // 函数名称: Insert // 功能: 插入数据 // FullName:CBalanceBinaryTree::Insert // 继承关系: private // 返回值: bool // 参数: PTREE_NODE & TreeNode(树) // 参数: int nData(数据) //************************************ bool CBalanceBinaryTree::Insert(PTREE_NODE &TreeNode,int nData) //插入数据 { if (!TreeNode)//查找到结点为空就插入结点 { //新添加结点 TreeNode = new TREE_NODE; memset(TreeNode,0,sizeof(TREE_NODE)); TreeNode->Data = nData; //插入成功 ,计数+1 m_nCount++; return true; } //判断是否小于根若小于根则从左孩子树开始查找寻找插入点 if (nData < TreeNode->Data) { Insert(TreeNode->pLchild,nData); //判断是否符合平衡二叉树条件 if(2 == (GetDepth(TreeNode->pLchild) - GetDepth(TreeNode->pRchild))) {//左子树与右子树深度差大于1的情况需要旋转 if (GetDepth(TreeNode->pLchild->pRchild) > GetDepth(TreeNode->pLchild->pLchild)) {//如果以左孩子树为结点,其右孩子树深度大于左孩子树深度则需要右左旋 DoubleRotateLR(TreeNode); //右左旋 } else {//如果其右孩子树不大于左孩子树深度则仅需要左单旋就行了 SingRotateLeft(TreeNode); //左单旋 } } } //判断是否大于根如果成立就从右孩子树一侧开始查找插入点 else if (nData > TreeNode->Data) { Insert(TreeNode->pRchild,nData); //判断新插入叶子后是否符合平衡二叉树条件 if(2 == (GetDepth(TreeNode->pRchild) - GetDepth(TreeNode->pLchild))) {//右子树与左子树的深度差大于1的情况就需要旋转 if (GetDepth(TreeNode->pRchild->pLchild)> GetDepth(TreeNode->pRchild->pRchild)) {//以右子树为结点,其左子树深度若大于右子树需要左右旋 DoubleRotateLR(TreeNode); //左右旋 } else {//以右子树为结点,其左子树深度小于右子树仅需要右旋 SingRotateRight(TreeNode); //右单旋 } } } else //等于根(即有和插入新结点的值相等的情况就直接返回) return false; return true; } //************************************ // 函数名称: DeleteNode // 功能: 删除数据 // FullName:CBalanceBinaryTree::DeleteNode // 继承关系: private // 返回值: bool // 参数: int nData(需要删除的数据) //************************************ bool CBalanceBinaryTree::DeleteNode(int nData) { if (!DeleteNode(m_TreeNode,nData)) { return false; } m_nCount--; return true; } //************************************ // 函数名称: DeleteNode // 功能: 数据删除 // FullName:CBalanceBinaryTree::DeleteNode // 继承关系: private // 返回值: bool // 参数: PTREE_NODE & TreeNode(树) // 参数: int nData(需要删除的数据) //************************************ bool CBalanceBinaryTree::DeleteNode(PTREE_NODE &TreeNode,int nData) { //用来判断是否删除成功(例如,在树中查找不到数据就返回false) bool bSuccess = true; if (!TreeNode)//先决条件 {//如果树为空就直接返回 return false; } //是否找到数据 if (nData == TreeNode->Data) { //1.删除的是叶子节点就直接删除 if (!TreeNode->pLchild && !TreeNode->pRchild)//递归出口 { delete TreeNode; TreeNode = nullptr; return true; } //2.有左子树(默认获取左子树中的最大值来替换当前结点) else if (TreeNode->pLchild) { //定义一个变量指向左子树中的最大值结点 PTREE_NODE pMaxData = GetMaxpLchild(TreeNode->pLchild); //把当前结点的值替换为最大值结点的值 TreeNode->Data = pMaxData->Data; //再以当前结点的左子树为结点,向下删除最大值结点 bSuccess = DeleteNode(TreeNode->pLchild,pMaxData->Data); //删除结点后逐层递归返回判断二叉树是否平衡 if(2 == (GetDepth(TreeNode->pLchild) - GetDepth(TreeNode->pRchild))) {//如果结点的左子树的高度比右子树的高度大于1的情况就需要旋转平衡(右左旋或左单旋) if (GetDepth(TreeNode->pLchild->pRchild)> GetDepth(TreeNode->pLchild->pLchild)) {//第一种情况,以左子树为结点,其右边高度大于左边就需要右左旋 DoubleRotateRL(TreeNode); //右左旋 } else {//第二种情况,以左子树为结点,如果右边高度不大于左边高度仅左单旋就可以了 SingRotateLeft(TreeNode); //左单旋 } } } //3.没有左子树有右子树(获取右子树中的最小值,以右子树为结点向左查找到尽头就是最小值) else if(TreeNode->pRchild) { //定义一个变量指向右子树中的最小值 PTREE_NODE pMinData = GetMinpRchild(TreeNode->pRchild); //用右子树中的最小值替换需要删除结点的值 TreeNode->Data = pMinData->Data; //再以右子树为结点删除原有最小值 bSuccess = DeleteNode(TreeNode->pRchild,pMinData->Data);
//删除结点后逐层递归返回二叉树平衡条件判断判断 if(2 == (GetDepth(TreeNode->pRchild) - GetDepth(TreeNode->pLchild))) {//如果结点的右子树与左子树的高度差大于1就需要旋转平衡(左右旋和右单旋) if (GetDepth(TreeNode->pRchild->pLchild) > GetDepth(TreeNode->pRchild->pRchild)) {//第一种情况,以右子树的结点,其左边的高度大于右边就需要左右旋 DoubleRotateLR(TreeNode); //左右旋 } else {//第二种情况,以右子树为结点,其左边高度不大于右边高度时仅右旋就可以了 SingRotateRight(TreeNode); //右单旋 } } } } else if (nData < TreeNode->Data) {//如果删除的数据小于当前结点的数据就,从当前结点的左子树向下递归查找删除 bSuccess = DeleteNode(TreeNode->pLchild,nData);
//删除的是左子树的叶子结点,只能造成右子树不平衡,所以需要判断右子树失衡,左右旋和右单旋的情况 if(2 == (GetDepth(TreeNode->pRchild) - GetDepth(TreeNode->pLchild))) {//如果结点的右子树与左子树的高度差大于1就需要旋转平衡(左右旋和右单旋) if (GetDepth(TreeNode->pRchild->pLchild) > GetDepth(TreeNode->pRchild->pRchild)) {//第一种情况,以右子树的结点,其左边的高度大于右边就需要左右旋 DoubleRotateLR(TreeNode); //左右旋 } else {//第二种情况,以右子树为结点,其左边高度不大于右边高度时仅右旋就可以了 SingRotateRight(TreeNode); //右单旋 } } } else if (nData > TreeNode->Data) {//如果删除的数据大于当前结点的数据就,从当前结点的右子树向下递归查找删除 bSuccess = DeleteNode(TreeNode->pRchild,nData);
//删除的是右子树的叶子结点,只能造成左子树不平衡,所以需要判断左子树失衡,右左旋和左单旋的情况 if(2 == (GetDepth(TreeNode->pLchild) - GetDepth(TreeNode->pRchild))) {//如果结点的左子树的高度比右子树的高度大于1的情况就需要旋转平衡(右左旋或左单旋) if (GetDepth(TreeNode->pLchild->pRchild)> GetDepth(TreeNode->pLchild->pLchild)) {//第一种情况,以左子树为结点,其右边高度大于左边就需要右左旋 DoubleRotateRL(TreeNode); //右左旋 } else {//第二种情况,以左子树为结点,如果右边高度不大于左边高度仅左单旋就可以了 SingRotateLeft(TreeNode); //左单旋 } } } if (bSuccess == false) return bSuccess; else return true; } //************************************ // 函数名称: GetMaxpLchild // 功能: //获取左子树的最大值节点 // FullName:CBalanceBinaryTree::GetMaxpLchild // 继承关系: private // 返回值: CBalanceBinaryTree::PTREE_NODE // 参数: PTREE_NODE TreeNode //************************************ CBalanceBinaryTree::PTREE_NODE CBalanceBinaryTree::GetMaxpLchild(PTREE_NODE TreeNode) //获取左子树的最大值节点 {//左子树向右右到尽头就是最大值结点 ////1.(递归方法) //if (!TreeNode->pRchild)//直到右子树为空结束递归 //{ // return TreeNode; //} //GetMaxpLchild(TreeNode->pRchild); // //2.(非递归方法) while(TreeNode->pRchild != NULL)//直到右子树为空结束 { TreeNode = TreeNode->pRchild; } return TreeNode; } //************************************ // 函数名称: GetMinpRchild // 功能: //获取右子树的最小值节点 // FullName:CTreeClass::GetMinpRchild // 继承关系: private // 返回值: CTreeClass::PTREE_NODE // 功能: //获取右子树的最小值节点 // 参数: PTREE_NODE TreeNode //************************************ CBalanceBinaryTree::PTREE_NODE CBalanceBinaryTree::GetMinpRchild(PTREE_NODE TreeNode) //获取右子树的最小值节点 {//右子树向左左到尽头就是最小值结点 //1.(递归方法) //if (!TreeNode->pLchild)//直到左子树为空结束递归 //{ // return TreeNode; //} //GetMinpRchild(TreeNode->pLchild);
//2.(非递归方法) while (TreeNode->pLchild != NULL) { TreeNode = TreeNode->pLchild; } return TreeNode; } //************************************ // 函数名称: GetDepth // 功能: //获得树的深度对外接口 // FullName:CTreeClass::GetDepth // 继承关系: public // 返回值: int // 功能: //获得树的深度对外接口 //************************************ int CBalanceBinaryTree::GetDepth() //获得树的深度对外接口 { return GetDepth(m_TreeNode); } //************************************ // 函数名称: GetDepth // 功能: //获得树的深度 // FullName:CTreeClass::GetDepth // 继承关系: private // 返回值: int // 参数: PTREE_NODE TreeNode //************************************ int CBalanceBinaryTree::GetDepth(PTREE_NODE TreeNode) //获得树的深度 { if (!TreeNode)//空树就直接返回(做递归终止条件) { return 0; } //获取左子树深度 int LChildDepth = GetDepth(TreeNode->pLchild); //获取右子树深度 int RChildDepth = GetDepth(TreeNode->pRchild); //判断最大深度 int MaxDepth = LChildDepth > RChildDepth ? LChildDepth : RChildDepth; //如果左或右子树有元素则MaxDepth自增1 MaxDepth++; return MaxDepth; } |
主函数:
C++ Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// 平衡二叉树.cpp : 定义控制台应用程序的入口点。 //
#include "stdafx.h" #include "BalanceBinaryTree.h"
int _tmain(int argc, _TCHAR *argv[]) { CBalanceBinaryTree obj; obj.Insert(10); obj.Insert(5); obj.Insert(15); obj.Insert(20); obj.Insert(18); obj.DeleteNode(100); obj.DeleteNode(15); return 0; } |