C++实现平衡二叉树

本文详细介绍了如何在AVL树中插入节点并自动保持平衡,包括节点的插入、高度计算、失衡检测、四种调整操作(LL、RR、LR、RL)以及搜索功能。通过一系列示例展示了整个过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大一学生,写的肯定一般般,作为参考看一下吧,欢迎指出代码问题。只写了插入,删除还没有写。

#include<iostream>
using namespace std;
typedef int KeyType;
typedef struct VALNode
{
	KeyType key;
	VALNode* lchild, * rchild, * parent;
	int deep;
}VALNode;
//声明函数:
VALNode* insertVAL(VALNode*& tree, KeyType k);
VALNode* FindUnbalance(VALNode* n);
int Judge(VALNode* p, VALNode* s);
void LL_turn(VALNode*& nodeA);
void RR_turn(VALNode*& nodeA);
void LR_turn(VALNode*& nodeA);
void RL_turn(VALNode*& nodeA);
bool SearchVAL(VALNode* bt, VALNode* n);
void ChangeHeight(VALNode*& n);
VALNode* GetRoot(VALNode* n);
void GetHeight(VALNode*& n);

//insert a node into the tree, return the root of the tree after inserting
VALNode* insertVAL(VALNode*& tree, KeyType k)
{
	//tree是空树,建一颗树返回
	if (tree == NULL)
	{
		tree = (VALNode*)malloc(sizeof(VALNode));
		tree->key = k;
		tree->deep = 0;
		tree->lchild = tree->rchild = tree->parent = NULL;
		return tree;
	}
	//移动到要插入的位置
	VALNode* p = tree;
	VALNode* pre = tree;
	while (p != NULL)
	{
		pre = p;
		if (p->key < k)
		{
			p = p->rchild;
		}
		else
		{
			p = p->lchild;
		}
	}
	//创建一个节点
	VALNode* node = (VALNode*)malloc(sizeof(VALNode));
	node->key = k;
	node->deep = 0;
	node->lchild = node->rchild = node->parent = NULL;
	//插入
	if (node->key < pre->key)
	{
		pre->lchild = node;
	}
	else
	{
		pre->rchild = node;
	}
	node->parent = pre;
	//改变平衡因子(高度)
	ChangeHeight(node);
	//看看有没有失衡
	VALNode* unba = FindUnbalance(node);
	if (unba != NULL)
	{
		//怎么调整
		int how = Judge(unba, node);
		switch (how)
		{
		case 0:
			LL_turn(unba);break;
		case 1:
			RR_turn(unba);break;
		case 2:
			LR_turn(unba);break;
		case 3:
			RL_turn(unba);break;
		}
		//再次计算高度,以新的根节点为起点往下调整
		tree = GetRoot(unba);
		GetHeight(tree);
	}
	return tree;
}
//计算树n的所有节点高度
void GetHeight(VALNode*& n)
{
	if (n->lchild == NULL && n->rchild == NULL)
	{
		ChangeHeight(n);
		return;
	}
	if (n->lchild != NULL)
	{
		GetHeight(n->lchild);
	}
	if (n->rchild != NULL)
	{
		GetHeight(n->rchild);
	}
}
//插入节点后改变相关节点高度,传入插入的节点的指针
void ChangeHeight(VALNode*& n)
{
	n->deep = 0;
	VALNode* p = n->parent;
	while (p != NULL)
	{
		int ldeep = p->lchild == NULL ? -1 : p->lchild->deep;//左子树高度
		int rdeep = p->rchild == NULL ? -1 : p->rchild->deep;//左子树高度
		p->deep = (ldeep > rdeep ? ldeep : rdeep) + 1;//p.deep=左子树和右子树较大者+1
		p = p->parent;
	}
}
//找失衡节点,没有就返回null
VALNode* FindUnbalance(VALNode* n)//n是插入节点的位置,从n往根节点找
{
	int ldeep = n->lchild == NULL ? -1 : n->lchild->deep;//左子树高度
	int rdeep = n->rchild == NULL ? -1 : n->rchild->deep;//左子树高度
	int df = ldeep - rdeep;//平衡因子df=左子树高度-右子树高度
	if (df < -1 || df>1)
	{
		//该节点失衡
		return n;
	}
	//向根节点方向找
	if (n->parent != NULL)
	{
		return FindUnbalance(n->parent);
	}
	else
	{
		return NULL;
	}
}
//判断要进行哪种调整
int Judge(VALNode* p, VALNode* s)//传入失衡节点和插入节点
{
	if (SearchVAL(p->lchild, s))
	{
		if (SearchVAL(p->lchild->lchild, s))
		{
			//ll
			return 0;
		}
		else
		{
			//lr
			return 2;
		}
	}
	else
	{
		if (SearchVAL(p->rchild->rchild, s))
		{
			//rr
			return 1;
		}
		else
		{
			//rl
			return 3;
		}
	}
}
//LL型调整0
void LL_turn(VALNode*& nodeA)//nodeA是第一个失衡节点,即调整子树的根节点
{
	VALNode* nodeB = nodeA->lchild;
	nodeA->lchild = nodeB->rchild;//b的右节点改成a的左节点
	if (nodeB->rchild != NULL)
	{
		nodeB->rchild->parent = nodeA;
	}
	nodeB->rchild = nodeA;//a作为b的右节点
	VALNode* Aparent = nodeA->parent;
	nodeB->parent = Aparent;
	if (Aparent != NULL)
	{
		if (Aparent->lchild == nodeA)
			Aparent->lchild = nodeB;
		else
			Aparent->rchild = nodeB;
	}
	nodeA->parent = nodeB;
}
//RR型调整1
void RR_turn(VALNode*& nodeA)//nodeA是第一个失衡节点,即调整子树的根节点
{
	VALNode* nodeB = nodeA->rchild;
	nodeA->rchild = nodeB->lchild;//b的左节点改成a的右节点
	if (nodeB->lchild != NULL)
	{
		nodeB->lchild->parent = nodeA;
	}
	nodeB->lchild = nodeA;//a作为b的左节点
	VALNode* Aparent = nodeA->parent;
	nodeB->parent = Aparent;
	if (Aparent != NULL)
	{
		if (Aparent->lchild == nodeA)
			Aparent->lchild = nodeB;
		else
			Aparent->rchild = nodeB;
	}
	nodeA->parent = nodeB;
}
//LR型调整2
void LR_turn(VALNode*& nodeA)//nodeA是第一个失衡节点,即调整子树的根节点
{
	VALNode* nodeB = nodeA->lchild;
	VALNode* nodeC = nodeB->rchild;
	nodeB->rchild = nodeC->lchild;//c的lchild改成b的rchild
	if (nodeC->lchild != NULL)
	{
		nodeC->lchild->parent = nodeB;
	}
	nodeA->lchild = nodeC->rchild;//c的rchild改成a的lchild
	if (nodeC->rchild != NULL)
	{
		nodeC->rchild->parent = nodeA;
	}
	nodeC->lchild = nodeB;//c改成根节点
	nodeC->rchild = nodeA;
	VALNode* Aparent = nodeA->parent;
	nodeC->parent = Aparent;
	if (Aparent != NULL)
	{
		if (Aparent->lchild == nodeA)
			Aparent->lchild = nodeC;
		else
			Aparent->rchild = nodeC;
	}
	nodeA->parent = nodeC;
	nodeB->parent = nodeC;
}
//RL型调整3
void RL_turn(VALNode*& nodeA)//nodeA是第一个失衡节点,即调整子树的根节点
{
	VALNode* nodeB = nodeA->rchild;
	VALNode* nodeC = nodeB->lchild;
	nodeA->rchild = nodeC->lchild;//c的lchild改成a的rchild
	if (nodeC->lchild != NULL)
	{
		nodeC->lchild->parent = nodeA;
	}
	nodeB->lchild = nodeC->rchild;//c的rchild改成b的lchild
	if (nodeC->rchild != NULL)
	{
		nodeC->rchild->parent = nodeB;
	}
	nodeC->lchild = nodeA;//c改成根节点
	nodeC->rchild = nodeB;
	VALNode* Aparent = nodeA->parent;
	nodeC->parent = Aparent;
	if (Aparent != NULL)
	{
		if (Aparent->lchild == nodeA)
			Aparent->lchild = nodeC;
		else
			Aparent->rchild = nodeC;
	}
	nodeA->parent = nodeC;
	nodeB->parent = nodeC;
}
//在树上查找节点n。找到返回true,否则返回false
bool SearchVAL(VALNode* bt, VALNode* n)
{
	if (bt == NULL)
	{
		return false;
	}
	if (bt == n)
	{
		return true;
	}
	if (n->key < bt->key)
	{
		return SearchVAL(bt->lchild, n);
	}
	else
	{
		return SearchVAL(bt->rchild, n);
	}
}
//得到根节点,调整完树后根节点可能会乱,用这个函数找到根节点
VALNode* GetRoot(VALNode* n)
{
	if (n->parent == NULL)
		return n;
	return GetRoot(n->parent);
}

int main()
{
	VALNode* tree = NULL;
	insertVAL(tree, -1);
	insertVAL(tree, 2);
	insertVAL(tree, 5);//RR
	insertVAL(tree, -2);
	insertVAL(tree, -3);//LL
	insertVAL(tree, 4);
	insertVAL(tree, 3);//RL
	insertVAL(tree, 0);
	insertVAL(tree, 1);//LR

	return 0;
}

附上主函数测试数据最后形成的树:

<think>好的,我需要回答用户关于如何用C++实现AVL树或其他平衡二叉树的问题。首先,我得回顾一下AVL树的基本概念和结构。AVL树是一种自平衡的二叉搜索树,每个节点的左右子树高度差不超过1。实现它需要处理节点插入、删除以及平衡因子的调整,可能还需要旋转操作来维持平衡。 用户提供的引用中有关于AVL树节点结构的描述,包括左、右、父指针,键值对和平衡因子。我需要把这些信息整合起来,给出一个C++的节点定义。然后,插入操作部分,按照二叉搜索树的方式插入节点后,需要更新平衡因子,并根据不同的情况进行旋转调整,比如左旋、右旋、左右旋、右左旋。 接下来,我需要考虑如何将这些步骤转化为代码。比如,插入时递归或迭代找到合适的位置,插入新节点后,沿着路径更新父节点的平衡因子,一旦发现某个节点的平衡因子绝对值超过1,就进行相应的旋转。旋转函数的实现是关键,需要正确处理子树的指针和父指针的更新,同时调整平衡因子。 用户还提到了查找操作,这和普通二叉搜索树类似,可以用递归实现。删除操作可能更复杂,因为删除节点后也需要调整平衡因子并进行旋转,但考虑到复杂度,可能需要简要说明或参考引用中的方法。 在代码示例部分,需要展示节点结构、插入和旋转的具体实现。确保代码符合C++语法,比如使用模板类、指针操作等。同时,注意平衡因子的更新逻辑,比如插入左子树时平衡因子减1,右子树加1,然后检查是否需要旋转。 可能遇到的问题包括正确维护父指针,旋转后各个节点的平衡因子更新是否正确,以及处理各种插入情况。需要确保代码示例能够覆盖这些情况,并注释关键步骤。 最后,根据用户要求生成相关问题,比如性能分析、应用场景、与红黑树的比较等。这些问题可以帮助用户进一步理解AVL树的特性和使用场景。 总结步骤: 1. 定义AVL树节点结构,包含左右子节点、父节点、键值对和平衡因子。 2. 实现插入方法,包括二叉搜索树的插入和平衡因子的更新。 3. 实现四种旋转操作来处理不平衡情况。 4. 提供查找方法的代码示例。 5. 简要说明删除操作的复杂性。 6. 生成相关问题,引导深入学习。 需要确保代码正确,特别是旋转和平衡因子调整的部分,避免常见的错误,比如指针未正确更新或平衡因子计算错误。同时,引用用户提供的参考资料来支持回答,比如引用中的节点定义和插入步骤。</think>### 如何用C++实现AVL树 #### 一、AVL树节点定义 根据引用[2][3],AVL树节点需要包含**三叉链结构**(左右子节点和父节点指针)、键值对和平衡因子: ```cpp template<class K, class V> struct AVLTreeNode { AVLTreeNode(const std::pair<K, V>& kv) : _left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _bf(0) {} AVLTreeNode<K, V>* _left; AVLTreeNode<K, V>* _right; AVLTreeNode<K, V>* _parent; // 三叉链便于回溯更新平衡因子 std::pair<K, V> _kv; // 键值对 int _bf; // 平衡因子(右子树高度 - 左子树高度) }; ``` --- #### 二、插入操作与平衡调节 ##### 1. 二叉搜索树插入 按照引用[2][3]的步骤,首先完成二叉搜索树的插入: ```cpp template<class K, class V> class AVLTree { using Node = AVLTreeNode<K, V>; public: bool Insert(const std::pair<K, V>& kv) { if (_root == nullptr) { _root = new Node(kv); return true; } // 查找插入位置 Node* parent = nullptr; Node* cur = _root; while (cur) { if (cur->_kv.first < kv.first) { parent = cur; cur = cur->_right; } else if (cur->_kv.first > kv.first) { parent = cur; cur = cur->_left; } else { return false; // 键已存在 } } // 插入新节点 cur = new Node(kv); if (parent->_kv.first < kv.first) { parent->_right = cur; } else { parent->_left = cur; } cur->_parent = parent; // 更新平衡因子并旋转调节 UpdateBalanceFactor(parent); return true; } private: Node* _root = nullptr; }; ``` ##### 2. 平衡因子更新与旋转 根据引用[2][3],插入后需**沿父节点回溯更新平衡因子**: ```cpp void UpdateBalanceFactor(Node* parent) { while (parent) { // 计算左右子树高度差 int leftHeight = GetHeight(parent->_left); int rightHeight = GetHeight(parent->_right); parent->_bf = rightHeight - leftHeight; // 检查是否需要旋转 if (parent->_bf == 2 || parent->_bf == -2) { Rotate(parent); // 触发旋转操作 break; } parent = parent->_parent; } } ``` --- #### 三、旋转操作实现 ##### 1. 右单旋(LL型不平衡) 当左子树过高时触发: ```cpp void RotateRight(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; // 调整子树关系 parent->_left = subLR; if (subLR) subLR->_parent = parent; subL->_right = parent; Node* ppNode = parent->_parent; // 保存原父节点的父节点 parent->_parent = subL; // 更新根或父节点链接 if (ppNode == nullptr) { _root = subL; subL->_parent = nullptr; } else { if (ppNode->_left == parent) { ppNode->_left = subL; } else { ppNode->_right = subL; } subL->_parent = ppNode; } // 更新平衡因子 parent->_bf = subL->_bf = 0; } ``` ##### 2. 左单旋(RR型不平衡) 当右子树过高时触发,代码结构与右旋对称。 ##### 3. 左右双旋(LR型) 先对左孩子左旋,再对父节点右旋: ```cpp void RotateLeftRight(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; int bf = subLR->_bf; // 保存旋转前的平衡因子 RotateLeft(subL); // 左旋左子树 RotateRight(parent); // 右旋父节点 // 更新平衡因子 if (bf == 1) { parent->_bf = 0; subL->_bf = -1; } else if (bf == -1) { parent->_bf = 1; subL->_bf = 0; } subLR->_bf = 0; } ``` --- #### 四、查找操作 与普通二叉搜索树一致(引用[4]): ```cpp bool Find(const K& key) { Node* cur = _root; while (cur) { if (cur->_kv.first < key) { cur = cur->_right; } else if (cur->_kv.first > key) { cur = cur->_left; } else { return true; } } return false; } ``` --- #### 五、删除操作 删除操作需要先执行标准BST删除,然后类似插入操作回溯更新平衡因子并进行旋转调节(引用[4])。具体实现较为复杂,需考虑多种情况。 --- §§ 1. AVL树的旋转操作有哪些类型?分别在什么场景下触发? 2. 如何验证实现的AVL树是否满足平衡条件?[^1] 3. AVL树与红黑树在性能和应用场景上有何差异? 4. 如何处理AVL树删除结点后的平衡调节? 5. 平衡因子更新时如何避免重复计算子树高度?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值