[C++]AVL搜索树 查找/插入/删除

AVL搜索树同时具有AVL树和搜索树的性质,相当于一种把高度控制在O(log n)的搜索树,从而保证了查找、插入、删除的时间都为O(log n)。
AVL树保证对于树中的任意节点,其两子树的高度之差不大于1,于是引入平衡因子这一概念。平衡因子表示一个节点的左子树的高度减去右子树的高度。也就是说,正常的AVL搜索树的任一节点,其平衡因子为1,0或-1,而当插入或删除了节点之后,树中出现某些节点平衡因子可能会变为2或-2,此时需要调整树的平衡。
现在假设节点nodeX是距离插入/删除位置最近、且平衡因子为2或-2的节点(之所以考察距离插入/删除位置最近的失衡节点,是因为其他失衡节点可能会在这个节点恢复平衡之后再次发生改变,这就体现出AVL搜索树调整平衡要“自下而上”)。此时共有四种可能的情况:
(1)nodeX的平衡因子为2,nodeX的左子树的平衡因子为1;
(2)nodeX的平衡因子为-2,nodeX的右子树的平衡因子为-1;
(3)nodeX的平衡因子为2,nodeX的左子树的平衡因子为-1或0;
(4)nodeX的平衡因子为-2,nodeX的右子树的平衡因子为1或0。
其中(1)(2)互相对称,(3)(4)互相对称。当且仅当删除操作的时候会出现子树平衡因子为0的情况。
情况(1),对nodeX使用一次右旋转可恢复平衡;
情况(2),对nodeX使用一次左旋转可恢复平衡;
情况(3),先对nodeX的左子树使用一次左旋转,再对nodeX使用一次右旋转可恢复平衡;
情况(4),先对nodeX的右子树使用一次右旋转,再对nodeX使用一次左旋转可恢复平衡。
(关于左旋转、右旋转,请看以下代码)

代码

本代码是在之前博文[C++]搜索树 查找/插入/删除的代码的基础上修改并且参考AVL树简介得到的。
不过关键的两个函数:插入和删除,都已经与之前搜索树的代码相去甚远,由以while循环为主体的函数转变为递归函数。虽然两者都能够查找到需要插入/删除的位置,但插入/删除完成之后需要自下而上调整树的平衡,while循环似乎很难从发生改变的位置向上回溯到整个树的根节点,而递归函数则可以在到达递归终点之后,回归到根节点,于是可以通过回归实现自下而上调整平衡。

using namespace std;
template<class T, class E>
struct bsNode//定义搜索树节点
{
	pair<T, E> element;//元素为数对,前项表示关键字,后项表示数值
	bsNode<T, E>* leftChild;//左子节点指针
	bsNode<T, E>* rightChild;//右子节点指针
	int height;//节点高度,用于计算父节点平衡因子
	bsNode(const pair<T, E>& theElement)
	{
		element = theElement;
		leftChild = NULL;
		rightChild = NULL;
		height = 0;
	}
	bsNode(const pair<T, E>& theElement, bsNode<T, E>* LEFT, bsNode<T, E>* RIGHT)
	{
		element = theElement;
		leftChild = LEFT;
		rightChild = RIGHT;
		height = max(getHeight(LEFT),getHeight(RIGHT)) + 1;
	}
};

template<class T, class E>
int getHeight(bsNode<T, E>* node)//获得某一节点下二叉树的高度
{
	if (node == nullptr)
		return 0;
	int leftHeight = getHeight(node->leftChild);
	int rightHeight = getHeight(node->rightChild);
	return (max(leftHeight, rightHeight) + 1);
}

template<class T, class E>
class bsTree//AVL搜索树
{
public:
	bsTree() { root = NULL; }
	void ascend()//按关键字顺序输出元素数值
	{ 
		inOrder(root); 
		cout << endl;
	}
	void find(const T& theKey)//按关键字查找对应元素的数值
	{
		bsNode<T, E>* p = root;
		while (p != NULL)
		{
			if (theKey < p->element.first)
				p = p->leftChild;
			else if (theKey > p->element.first)
				p = p->rightChild;
			else
			{
				cout << p->element.second << endl;
				return;
			}

		}
		cout << "Not find" << endl;
	}
	void insert(const pair<T, E>& theElement) { root = insert(root, theElement); }//插入节点,具体方法已被封装
	void erase(const pair<T, E>& theElement) { root = erase(root, theElement); }//删除节点,具体方法已被封装
private:
	bsNode<T, E>* root;
	void inOrder(bsNode<T, E>* node)//中序遍历
	{
		if (node != nullptr)
		{
			inOrder(node->leftChild);
			cout << node->element.second << " ";
			inOrder(node->rightChild);
		}
	}
	//以下为恢复AVL树平衡所需要的旋转函数
	bsNode<T, E>* leftRotation(bsNode<T, E>* nodeX)//单左旋
	{
		bsNode<T, E>* X_right = nodeX->rightChild;
		nodeX->rightChild = X_right->leftChild;
		X_right->leftChild = nodeX;

		nodeX->height = getHeight(nodeX);
		X_right->height = getHeight(X_right);

		return X_right;
	}
	bsNode<T, E>* rightRotation(bsNode<T, E>* nodeX)//单右旋
	{
		bsNode<T, E>* X_left = nodeX->leftChild;
		nodeX->leftChild = X_left->rightChild;
		X_left->rightChild = nodeX;

		nodeX->height = getHeight(nodeX);
		X_left->height = getHeight(X_left);

		return X_left;
	}
	bsNode<T, E>* rightLeftRotation(bsNode<T, E>* nodeX)//先右旋,后左旋
	{
		nodeX->rightChild = rightRotation(nodeX->rightChild);
		return leftRotation(nodeX);
	}
	bsNode<T, E>* leftRightRotation(bsNode<T, E>* nodeX)//先左旋,后右旋
	{
		nodeX->leftChild = leftRotation(nodeX->leftChild);
		return rightRotation(nodeX);
	}

    //插入
	bsNode<T, E>* insert(bsNode<T, E>*& node, const pair<T, E>& theElement)//递归找到插入位置,插入节点,然后自下而上调整平衡
	{
		if (node == NULL)//到达插入位置
			node = new bsNode<T, E>(theElement);
		else if (theElement.first > node->element.first)//向右走
		{
			node->rightChild = insert(node->rightChild, theElement);
			if (getHeight(node->leftChild)-getHeight(node->rightChild) <- 1)//此时node平衡因子为-2
			{
				if (theElement.first > node->rightChild->element.first)//RR,单左旋
					node = leftRotation(node);
				else if (theElement.first < node->rightChild->element.first)//RL,先右旋后左旋
					node = rightLeftRotation(node);
			}
		}
		else if (theElement.first < node->element.first)//向左走
		{
			node->leftChild = insert(node->leftChild, theElement);
			if (getHeight(node->leftChild) - getHeight(node->rightChild) > 1)//此时node平衡因子为2
			{
				if (theElement.first < node->leftChild->element.first)//LL,单右旋
					node = rightRotation(node);
				else if (theElement.first > node->leftChild->element.first)//LR,先左旋后右旋
					node = leftRightRotation(node);
			}
		}
		return node;
	}

    //删除
	bsNode<T, E>* erase(bsNode<T, E>*& node, const pair<T, E>& theElement)//递归查找删除位置,删除节点,然后自下而上调整平衡
	{
		if (node != NULL)
		{
			if (theElement.first == node->element.first)//找到了需要删除的节点
			{
				if (node->leftChild != NULL && node->rightChild != NULL)//如果这个节点有两个子树,则需要化为删除其左子树最大值或右子树最小值
				{
					if (node->leftChild->height > node->rightChild->height)//当删除节点的左子树高于右子树时,删除左子树最大值
					{
						//找到左子树最大值,把它复制到删除节点上
						bsNode<T, E>* leftMax = node->leftChild;
						while (leftMax->rightChild != NULL)
							leftMax = leftMax->rightChild;
						node->element = leftMax->element;

						node->leftChild = erase(node->leftChild, leftMax->element);
					}
					else//否则删除右子树最小值
					{
						//找到右子树最小值,把它复制到删除节点上
						bsNode<T, E>* rightMin = node->rightChild;
						while (rightMin->leftChild != NULL)
							rightMin = rightMin->leftChild;
						node->element = rightMin->element;

						node->rightChild = erase(node->rightChild, rightMin->element);
					}
				}
				else//如果删除节点只有至多一个子树,则将子树移动到删除节点位置上即可
				{
					bsNode<T, E>* deleteNode = node;
					if (node->leftChild != NULL)
						node = node->leftChild;
					else
						node = node->rightChild;
					delete deleteNode;
				}
			}
			else if (theElement.first > node->element.first)//向右走
			{
				node->rightChild = erase(node->rightChild, theElement);
				if (getHeight(node->leftChild) - getHeight(node->rightChild) > 1)//此时node的平衡因子为2
				{
					if (getHeight(node->leftChild->leftChild) > getHeight(node->leftChild->rightChild))//node的左子树平衡因子为1,node单右旋
						node = rightRotation(node);
					else//node的左子树平衡因子为-1,node先左旋后右旋
						node = leftRightRotation(node);
				}
			}
			else if (theElement.first < node->element.first)//向左走
			{
				node->leftChild = erase(node->leftChild, theElement);
				if (getHeight(node->leftChild) - getHeight(node->rightChild) < -1)//此时node的平衡因子为-2
				{
					if (getHeight(node->rightChild->rightChild) > getHeight(node->rightChild->leftChild))//node的右子树平衡因子为-1,node单左旋
						node = leftRotation(node);
					else//node的右子树平衡因子为-1,node先右旋后左旋
						node = rightLeftRotation(node);
				}
			}
			return node;
		}
		return NULL;
	}
};
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值