BS+AVL——二刷错误总结

本文详细介绍了BST树的插入策略,特别是处理头节点的方法,以及AVL树的概念、插入操作中的左右旋调整和平衡因子计算。同时,讨论了如何判断BST和AVL树的正确性,包括isBST和isAVL函数的实现。

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

🥇我的学习记录🥇
🚀 和我一起学习吧🚀

🎭BSTree
 🎭insert中对头节点的处理
 ✨pop需要记住规律
 ✨判断BS是否符合测试代码
🎭AVL
 🎭概念
 ✨insert
 ✨左右双旋
 ✨判断AVL是否符合测试代码

在这里插入图片描述

🎭BSTree

🎭insert中对头节点的处理

注意

  1. 当插入的位置是根节点的时候
	void Insert(int val)
	{
		Node* node = new Node(val);
		// 找插入位置
		if (_root == nullptr)
		{
			_root = node;
			return;
		}
		else
		{
			Node* pre = _root, *head = _root;
			while (head)
			{
				pre = head;
				if (head->_val < val) head = head->_right;
				else if (head->_val > val) head = head->_left;
				else
				{
					assert(false);
				}
			}
			if (val < pre->_val) pre->_left = node;
			else pre->_right = node;
		}
	}

pop需要记住规律

注意

  1. 右边为nullptr
  2. 左边为nullptr
  3. 两边都为nullptr
  4. 两边都不为nullptr
    情况3合并情况1,2一起分析
    其中需要考虑每种情况中当更改的节点是根节点的情况
	void pop(int val)
	{
		// 需要在一开始找cur的时候就要找到pre节点
		// 因为需要改变pre后的节点
		// 找cur阶段不需要找pre节点,pre节点指的是左树最右边节点的父节点
		// 在左右有nullptr的时候是需要直接进行调整的——需要知道pre节点
		// 所以必须在找des的时候找到pre节点
		Node* des = _root, * pre = _root;
		while (des)
		{
			// 只有在向下变换的时候才会变
			// 如果在一开始就变,当他在找到des的时候,也会变换一次,这个时候就坏了
			//pre = des;
			if (des->_val < val) pre=des,des = des->_right;
			else if (des->_val > val) pre=des,des = des->_left;
			else
			{
				if (des->_left == nullptr)
				{
					if (pre == _root)
					{
						_root = pre->_right;
					}
					// 左为空
					else if (pre->_left == des)
					{
						pre->_left = des->_right;
					}
					else
					{
						pre->_right = des->_right;
					}
					delete des;
					des = nullptr;
					
				}
				else if (des->_right == nullptr)
				{
					if (pre == _root) _root = _root->_left;
					else if (pre->_left == des)
						pre->_left = des->_left;
					else pre->_right = des->_left;
					delete des;
					des = nullptr;
				}
				else
				{
					// 两边都不是空节点
					pre = des;// 必须要将pre节点进行转移
					Node* cur = des->_left;
					// 进行找节点的时候必须保证这个子树不是空树
					// 找左树的最右节点
					while (cur->_right)
					{
						pre = cur;
						cur = cur->_right;
					}
					swap(cur->_val, des->_val);
					//pre->_right = nullptr;
					// 这里细节没有注意到
					if (cur == pre->_right) pre->_right = nullptr;
					else pre->_left = nullptr;
					delete cur;
					cur = nullptr;
				}
				break;
			}
		}
	}
  1. 必须在查找目标删除节点的时候就要提供pre节点的位置——在目标节点的左右有节点为空的时候需要立即知道pre节点并直接进行链接
  2. 删除的点是否为根节点
    <1> 目标节点的左右有空节点的情况
    需要考虑删除的点是根节点(需要特殊判断)
    <2> 在目标节点的左右不为空的情况
    不需要考虑这个点是否为根节点,他的变换规则是找到左边最大的或者右边最小的交换,所以不需要考虑是否为根节点
  3. 循环终止条件是找到了nullptr依旧没有找到目标节点
  4. insert中,pre需要一起更新;在pop中,在查找过程中更新,不能一起更新,如果des已经找到了目标节点,但是你在已进入循环就更新,那直接就将pre移动到了需要删除的点的位置,当左右节点有空的时候就找不到pre节点
  5. 一定要注意在左右都不为空的情况
    请添加图片描述

✨判断BS是否符合测试代码

这种判断方法是一个算法题——验证平衡二叉搜索树

class BST
{
	bool isBST()
	{
		_mem = INT_MIN; // 这个每次都要更新
		return _isBST(_root);
	}
	int _mem = INT_MIN;

private:
	bool _isBST(BSTNode* root)
	{
		//cout << _mem << endl;
		if (root == nullptr) return true;
		bool l = _isBST(root->_left);
		if (l == false) return false;
		if (_mem >= root->_data) return false;
		_mem = root->_data;//更新_mem
		bool r = _isBST(root->_right);
		if (r == false) return false;
		return true;
	}
};


🎭AVL

🎭概念

任意节点的左右高度 < 2 ,并且使用平衡因子——右树高度-左树高度

insert

pop不需要知道,如果想知道,可以去网上找找

		void insert(const T& val)
		{
			Node* node = new Node(val);
			if (_root == nullptr)
			{
				_root = node;
				return;
			}
			Node* des = _root, * pre = _root;
			while (des)
			{
				pre = des;
				if (des->_val < val) des = des->_right;
				else if (des->_val) des = des->_left;
				else assert(false);
			}
			if (pre->_val < val) pre->_right = node, pre->_bf++;
			else pre->_left = node, pre->_bf--;
			// 因为是三叉链,一定要将父节点进行更新
			node->_parent = pre;
			// 进行调整
			Node* cur = pre;
			pre = cur->_parent;
			while (pre && cur->_bf)
			{
				if (pre->_left == cur) pre->_bf--;
				else pre->_bf++;
				if (pre->_bf == -2 && cur->_bf == -1)
				{
					rotateR(pre);
					break;
				}
				else if (pre->_bf == 2 && cur->_bf == 1)
				{
					rotateL(pre);
					break;
				}
				else if (pre->_bf == -2 && cur->_bf == 1)
				{
					rotateLR(pre);
					break;
				}
				else if (pre->_bf == 2 && cur->_bf == -1)
				{
					rotateRL(pre);
					break;
				}
				else
				{
					cur = pre;
					pre = cur->_parent;
				}
			}
		}
		void rotateL(Node* pre)
		{
			Node* subR = pre->_right, *subRL = subR->_left;
			Node* ppre = pre->_parent;
			subR->_parent = ppre;
			if (pre == _root)
			{
				_root = subR;
			}
			else
			{
				if (ppre->_right == pre) ppre->_right = subR;
				else ppre->_left = subR;
			}
			pre->_parent = subR;
			subR->_left = pre;
			pre->_right = subRL;
			if (subRL) subRL->_parent = pre;
			pre->_bf = subR->_bf = 0;
		}
		void rotateR(Node* pre)
		{
			Node* subL = pre->_left, *subLR = subL->_right;
			Node* ppre = pre->_parent;
			subL->_parent = ppre;
			if (pre == _root)
			{
				_root = subL;
			}
			else
			{
				if (ppre->_right == pre) ppre->_right = subL;
				else ppre->_left = subL;
			}
			pre->_parent = subL;
			subL->_right = pre;
			pre->_left = subLR;
			if (subLR) subLR->_parent = pre;
			pre->_bf = subL->_bf = 0;
		}
	void rotateRL(Node* head)
	{
		Node* subR = head->_right, * subRL = subR->_left;
		int bf = subRL->_bf;
		rotateR(subR);
		rotateL(head);
		if (bf == -1)
		{
			subR->_bf = 1;
			head->_bf = 0;
			subRL->_bf = 0;
		}
		else if(bf==1)
		{
			subR->_bf = 0;
			head->_bf = -1;
			subRL->_bf = 0;
		}
		else
		{
			subR->_bf = head->_bf = subRL->_bf = 0;
		}
	}
	void rotateLR(Node* head)
	{
		Node* subL = head->_left, * subLR = subL->_right;
		int bf = subLR->_bf;
		rotateL(subL);
		rotateR(head);
		// 最终变化的只可能是下面的两个点,sbuLR/subRL是不可能不平衡的
		if (bf == -1)
		{
			head->_bf = 1;
			subL->_bf = 0;
			subLR->_bf = 0;
		}
		else if(bf==1)
		{
			head->_bf = 0;
			subL->_bf = -1;
			subLR->_bf = 0;
		}
		else
		{
			subL->_bf = head->_bf = subLR->_bf = 0;
		}
	}
  1. 插入第一个节点对根节点进行特判
  2. 插入的时候需要知道pre节点,插入的同时要调整平衡因子
  3. 注意循环更新条件while (pre && cur->_bf) ,我们要更新的是pre节点,所以pre必须要有;cur->bf 如果为0的话,证明子树已经平衡,不会影响上面;如果bf是1,可能还有向上影响的可能需要继续向上
  4. 在代码复用上,一定要小心,需要在复用的时候,将需要变化的位置进行改变才行
  5. 单旋的时候需要注意pre节点是根节点以及subLR节点是空节点的情况
  6. 双旋平衡因子更新是不同的,需要特别分析

左右双旋

双旋有3种情况,每种都要分析出来
请添加图片描述

在这里插入图片描述

✨判断AVL是否符合测试代码

class AVL
{
	bool isAVL()
	{
		return _isAVL(_root);
	}
private:
	bool _isAVL(AVLNode* head)
	{
		if (head == nullptr) return true;
		if (abs(head->_bf) >= 2) return false;
		AVLNode* pphead = head->_parent;
		if (pphead)
		{
			if (pphead->_left == head)
			{
				if (head->_data >= pphead->_data) return false;
			}
			else
			{
				if (head->_data <= pphead->_data) return false;
			}
		}
		return _isAVL(head->_left) & _isAVL(head->_right);
	}
};

以上就是这次分享,希望对大家有帮助。
如果有什么不对的地方,还望指正,我一定会回来改正的!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值