【C++】AVL树

目录

[AVL 树](#AVL 树)

[a. AVL树的实现](#a. AVL树的实现)

(一)树的构建

(二)树的插入


AVL 树

AVL 树又叫平衡二叉树,它也有搜索二叉树的性质,但是:当向二叉搜索树中插入新结点后,能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度

a. AVL树的实现

(一)树的构建

代码

复制代码
	template<class K>
	struct node
	{
		node(const K& t)
		{
			s = t;
		}
		node* left = nullptr;
		node* right = nullptr;
		node* parent = nullptr;
		K s;
		int _bf = 0;
	};

// _bf 是平衡因子,记录左右子树之差,这里我们用 右子树高度 - 左子树高度

// parent 记录它们的父节点

(二)树的插入

实现原理和思路

插入前半部分和搜索二叉树一样,但是在插入时,时刻更新每个节点的平衡因子,和每个节点的父节点,如果向某个父节点插入右子树,则平衡因子++,插入左子树,平衡因子--(这里实现的平衡因子是 右子树高度 - 左子树高度)

插入一个节点后,影响的不只是父节点的平衡因子,还有可能是父节点的父节点的平衡因子

因此,平衡因子有三种可能:

  1. 如果父节点的平衡因子是 -1 或者 1

则父节点的原来的平衡因子是 0 (左右子树平衡),而现在平衡被破坏,则它的父节点的平衡因子也要更改**(所以我们需要向上更改父节点的平衡因子,直到父节点为空,或者平衡因子不属于这一种情况)**

  1. 如果父节点的平衡因子是 0

则现在的树已经平衡了,也不会影响它的父节点的平衡因子,不做任何处理,直接 break

  1. 如果父节点的平衡因子是 2 或者 - 2

则此时AVL树要保持左右子树一定高度(发生旋转)

代码大框架(里层实现没有完成)

复制代码
template<class K>
class bin_node
{
public:
	typedef node<K>  Node;
	bool insert(K & t)
	{
		if (_root == nullptr)
		{
			_root = new Node(t);
			return true;
		}
		Node* _parent = _root;
		Node* cur = _root;
		while (cur)
		{
			if (cur->s > t)
			{
				_parent = cur;
				cur = cur->left;
			}
			else if (cur->s < t)
			{
				_parent = cur;
				cur = cur->right;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(t);
		if (cur->s > _parent->s)
		{
			cur->parent = _parent;
			_parent->right = cur;
			_parent->_bf++;
		}
		else
		{
			cur->parent = _parent;
			_parent->left = cur;
			_parent->_bf--;
		}
		while (_parent)
		{
			if (_parent->_bf == 1 || _parent->_bf == -1)
			{
				cur = _parent;
				_parent = _parent->parent;
				if (_parent)
				{
					if (_parent->s > cur->s)
					{
						_parent->_bf--;
					}
					else
					{
						_parent->_bf++;
					}
				}
			}
			else if (_parent->_bf == 2 || _parent->_bf == -2)
			{
				 //旋转(代码实现在后面)
			}
			else if(_parent->_bf == 0)
			{
				break;
			}
			else
			{
				assert(false);
			}
		}
		
		return true;
	}

private:
	Node* _root = nullptr;
};

旋转原理

探讨什么时候发生旋转

  1. 第一种情况
  1. 第二种情况

针对第一种情况,举几个示例再细分一下:(向 c 插入节点)

综上:

左单旋:SubR的左子树链接root,root的右子树链接SubRL

我们容易发现,对于发生左单旋的树,root , SubR ,SubRL 的平衡因子都是 0

发生这个场景, root 的平衡因子是 2 ,SubR 的平衡因子是 1

针对第二种情况:(向 a 插入节点)

综上:

SubL的右子树链接root , root的左子树链接SubLR

我们容易发现,对于发生左单旋的树,root , SubL ,SubLR 的平衡因子都是 0

发生这个场景, root 的平衡因子是 -2 ,SubL 的平衡因子是 -1

回到第一种情况:(向 b 插入节点)

综上:

先右单旋,再左单旋

此情况发生在 root 的平衡因子为 2 ,SubR的平衡因子为 - 1

其它的情况照例分析即可(如 h = 1 是 ,插入是右子树)

回到第二种情况(向 b 插入节点)

同理可得: 先左单旋,再右单旋

此情况发生在 root 的平衡因子为 -2 ,SubR的平衡因子为 1

注意:

  1. 注意 根节点特殊情况 和 节点之间的相连(尤其是父节点的链接)
  2. 平衡因子的修改最好举例画图

完整代码

复制代码
template<class K>
struct node
{
	node(const K& t)
	{
		s = t;
	}
	node* left = nullptr;
	node* right = nullptr;
	node* parent = nullptr;
	K s;
	int _bf = 0;
};
template<class K>
class bin_node
{
public:
	typedef node<K>  Node;
	bool insert(K & t)
	{
		if (_root == nullptr)
		{
			_root = new Node(t);
			return true;
		}
		Node* _parent = _root;
		Node* cur = _root;
		while (cur)
		{
			if (cur->s > t)
			{
				_parent = cur;
				cur = cur->left;
			}
			else if (cur->s < t)
			{
				_parent = cur;
				cur = cur->right;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(t);
		if (cur->s > _parent->s)
		{
			cur->parent = _parent;
			_parent->right = cur;
			_parent->_bf++;
		}
		else
		{
			cur->parent = _parent;
			_parent->left = cur;
			_parent->_bf--;
		}
		while (_parent)
		{
			if (_parent->_bf == 1 || _parent->_bf == -1)
			{
				cur = _parent;
				_parent = _parent->parent;
				if (_parent)
				{
					if (_parent->s > cur->s)
					{
						_parent->_bf--;
					}
					else
					{
						_parent->_bf++;
					}
				}
			}
			else if (_parent->_bf == 2 || _parent->_bf == -2)//旋转
			{
				if (_parent->_bf == 2)
				{

					if (cur->_bf == 1)
					{
						RotateR(_parent);
						
						break;
					}
					else if (cur->_bf == -1)
					{
						RotateRL(_parent);
					}
					else
					{
						assert(false);
					}
				}
				else
				{
					if (cur->_bf == -1)
					{
						RotateR(_parent);
					}
					else if (cur->_bf == 1)
					{
						RotateLR(_parent);
					}
					else
					{
						assert(false);
					}
				}
				break;
			}
			else if(_parent->_bf == 0)
			{
				break;
			}
			else
			{
				assert(false);
			}
		}
		
		return true;
	}
	void InOder()
	{
		_InOder(_root);
	}
private:
	void _InOder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOder(root->left);
		cout << root->s << " ";
		_InOder(root->right);
	}
	void RotateLR(Node* root)  //左右旋转
	{
		Node* SubL = root->left;
		Node* SubLR = SubL->right;
		int n = SubLR->_bf;
		RotateL(SubL);
		RotateR(root);
		if (n == -1)
		{
			SubL->_bf = 0;
			SubLR->_bf = 0;
			root->_bf = 1;
		}
		else if (n == 1)
		{
			SubLR->_bf = 0;
			SubL->_bf = -1;
			root->_bf = 0;
		}
		else if (n == 0)
		{
			SubLR->_bf = 0;
			SubL->_bf = 0;
			root->_bf = 0;
		}
		else
		{
			assert(false);
		}

	}
	void RotateRL(Node* root) //右左旋转
	{
		Node* SubR = root->right;
		Node* SubRL = SubR->left;
		int n = SubRL->_bf;
		RotateR(SubR);
		RotateL(root);
		if (n == -1)
		{
			SubR->_bf = 1;
			SubRL->_bf = 0;
			root->_bf = 0;
		}
		else if (n == 1)
		{
			SubRL->_bf = 0;
			SubR->_bf = 0;
			root->_bf = -1;
		}
		else if (n == 0)
		{
			SubRL->_bf = 0;
			SubR->_bf = 0;
			root->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}
	void RotateL(Node* root)  //左单旋
	{
		Node* SubR = root->right;
		Node* SunRL = SubR->left;
		if (root == _root)
		{
			_root = SubR;
		}
		else
		{
			Node* parent = root->parent;
			if (parent->left == root)
			{
				parent->left = SubR;
			}
			else
			{
				parent->right = SubR;
			}
		}
		root->right = SunRL;
		SubR->left = root;
		SubR->parent = root->parent;
		root->parent = SubR;
		root->_bf = SubR->_bf = 0;
	}
	void RotateR(Node* root)  //右单旋
	{
		Node* SubL = root->left;
		Node* SubLR = SubL->right;
		if (root == _root)
		{
			_root = SubL;
		}
		else
		{
			Node* parent = root->parent;
			if (parent->left == root)
			{
				parent->left = SubL;
			}
			else
			{
				parent->right = SubL;
			}
		}
		root->left = SubLR;
		SubL->right = root;
		SubL->parent = root->parent;
		root->parent = SubL;
		root->_bf = SubL->_bf = 0;
	}
	Node* _root = nullptr;
};
相关推荐
小七在进步7 小时前
string的使用(1)
c++·visual studio
白杨尚青8 小时前
C++进阶篇(六):map 和 set 的模拟实现——用红黑树封装 mymap/myset(迭代器 + operator[])
开发语言·c++·迭代器·stl·set·map·红黑树
kloc11 小时前
聊天室项目入口网关子服务的实现
linux·c++
csdn_aspnet14 小时前
C++ 3个有序点的方向(Orientation of 3 ordered points)
c++·算法
小七在进步15 小时前
string的使用(2)
c++
青少儿编程课堂15 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级:折半搜索优化子集枚举(Meet in the Middle)解析
c++·python·算法·bfs·信息学竞赛
wo_jack17 小时前
C++ 入门笔记(十七):list 入门 —— 为什么 list 不能 sort?
数据结构·c++·面试·stl·list
青少儿编程课堂18 小时前
蓝桥杯青少组图形化 + 全国青少年信息素养大赛 + 电子学会Scratch考级:校园连连看解析
c++·python·算法·bfs·信息学竞赛
草莓熊Lotso19 小时前
【C++ 基于微服务的即时通讯系统】文件存储子服务实战:UUID生成、文件IO封装与全接口单元测试
开发语言·网络·数据库·c++·微服务·架构·单元测试
David猪大卫1 天前
【C++修炼】map 和 set 的使用
c语言·开发语言·数据结构·c++·经验分享·笔记·学习