没事儿就学习(2):AVL树删除

       在上一篇文章里莫名其妙的说了AVL树的添加节点,那么这一次我们就可以简单的说一说AVL树的删除节点问题了。学会了添加,删除还会很难吗?当然很简单的,只需要先用递归找到那个需要删除的节点之后,如果其有左孩子,把自己删了然后拿左孩子顶替它,然后把左孩子的右孩子连向它的右孩子(我晕),如果其没有左孩子但是有右孩子,那就拿右孩子顶替他,如果它是个孤寡老人,那就不费事了,直接删了完事儿。

      但是,真的有那么简单吗?很明显在删除完毕某些节点之后,需要判断树的深度是不是降低了,如果树的深度减少,那么整颗二叉树很可能再次出现不平衡的状态,需要进行调整。比如处于LH状态的一个节点的右子树的深度降低一层,其会处于LHLH的状态,则需要进行右旋以保证其平衡。所以我们把上一篇文章中的左旋和右旋操作的代码复制过来先(厚颜无耻脸)。

bool TurnLeft(TreeNode* &node) {
	TreeNode* tempNode = node->rightChild;
	node->rightChild = tempNode->leftChild;
	tempNode->leftChild = node;
	node = tempNode;
	return true;
}

bool TurnRight(TreeNode* &node) {
	TreeNode* tempNode = node->leftChild;
	node->leftChild = tempNode->rightChild;
	tempNode->rightChild = node;
	node = tempNode;
	return true;
}

bool BigTurnLeft(TreeNode* &node) {
	TreeNode *nextNode = node->rightChild;
	if (node->heightType != nextNode->heightType) {
		node->rightChild = nextNode->leftChild;
		node->rightChild->rightChild = nextNode;
		nextNode->leftChild = nullptr;
	}

	TurnLeft(node);
	return true;
}

bool BigTurnRight(TreeNode* &node) {
	TreeNode *nextNode = node->rightChild;
	if (node->heightType != nextNode->heightType) {
		node->leftChild = nextNode->rightChild;
		node->leftChild->leftChild = nextNode;
		nextNode->rightChild = nullptr;
	}

	TurnRight(node);
	return true;
}

        在有了基础的旋转操作函数之后,我们就可以进行正式的删除操作了,我们还是从一个小例子入手看看我们要怎么进行删除操作。序列{33,17,42,8,3,19,42,39,60}已经构建出一棵AVL树,此时需要删除42这个节点,由于其同时具有左孩子和右孩子,所以用它的左孩子代替它,此时重新判断树的平衡性,发现替换的39处于RH的状态,但是仍保持平衡。

《没事儿就学习(2):AVL树删除》

      那么比如再删除一个19试一试,19是一个叶子节点,因此可以直接删除,但是在删除完毕后17的平衡性发生了变化,17原先处于LH的状态,在失去了其右孩子后,右子树的深度减小,其处于LHLH的状态,需要进行右旋操作以保证其平衡性。

《没事儿就学习(2):AVL树删除》

     在了解到基本的规则之后,那么我们就可以实现AVL树删除的代码了。

 

bool RemoveAVLTreeNode(TreeNode* &node, int value, bool &isShort) {
	if (value == node->value) {
		//若节点无左右孩子,则直接删除,深度-1
		if (node->leftChild == nullptr && node->rightChild == nullptr) {
			delete node;
			node = nullptr;
			isShort = true;
			return true;
		}
		
		//若节点无左孩子但是有右孩子,用右孩子顶替该节点
		if (node->leftChild == nullptr) {
			TreeNode *tempNode = node;
			node = node->rightChild;
			delete tempNode;
			isShort = true;
			return true;
		}


		//若节点拥有左右孩子,则将左孩子的右孩子连向右孩子
		TreeNode *tempNode = node;
		node->leftChild->rightChild = node->rightChild;
		node->leftChild->heightType = node->heightType;
		node = node->leftChild;
		switch (node->heightType)
		{
		case LEFTHEIGHT:
			node->heightType = EQUALHEIGHT;
			break;
		case EQUALHEIGHT:
			node->heightType = RIGHTHEIGHT;
			break;
		case RIGHTHEIGHT:
			BigTurnLeft(node);
			break;
		default:
			break;
		}
		delete tempNode;
		isShort = true;
	}
	else if(value < node->value) {
		//当数值小于该节点且该节点无左孩子时,表示无该数值,删除失败
		if (node->leftChild == nullptr) {
			return false;
		}

		if (RemoveAVLTreeNode(node->leftChild, value, isShort)) {
			if (isShort) {
				switch (node->heightType)
				{
				case LEFTHEIGHT:
					node->heightType = EQUALHEIGHT;
					break;
				case RIGHTHEIGHT:
					BigTurnLeft(node);
					isShort = false;
					break;
				case EQUALHEIGHT:
					node->heightType = RIGHTHEIGHT;
				default:
					break;
				}
			}
		}
		else {
			return false;
		}
	}
	else {
		//当数值大于该节点且该节点无右孩子时,表示无该数值,删除失败
		if (node->rightChild == nullptr) {
			return false;
		}

		if (RemoveAVLTreeNode(node->rightChild, value, isShort)) {
			if (isShort) {
				switch (node->heightType)
				{
				case LEFTHEIGHT:
					BigTurnLeft(node);
					isShort = false;
					break;
				case RIGHTHEIGHT:
					node->heightType = EQUALHEIGHT;
					break;
				case EQUALHEIGHT:
					node->heightType = LEFTHEIGHT;
					break;
				default:
					break;
				}
			}
		}
		else {
			return false;
		}
	}
	
	return true;
}

    原文作者:AVL树
    原文地址: https://blog.csdn.net/NNU_Giser/article/details/80530515
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞