AVL二叉树

一. AVL树的概念

二叉树搜索的数据有序或接近有序时,搜索效率就会接近于n,效率低下。当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

  1. 它的左右子树都是AVL树
  2. 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
    在这里插入图片描述
    平衡因子规定左子树高度减右子树高度。

二. AVL树模拟实现

1. AVL树节点

template <class T ,class V>
struct AvlNode
{
	AvlNode<T,V>* _right;
	AvlNode<T,V>* _left;
	AvlNode<T,V>* _parent;//该节点的父节点
	std::pair<T, V> _kv;//kv形式存储数据
	int _bf;//平衡因子

	AvlNode(const std::pair<T,V>& kv)
		:_right(nullptr)
		,_left(nullptr)
		,_parent(nullptr)
		,_kv(kv)//自定义参数拷贝构造,调用它自己的拷贝函数。
		,_bf(0)
		{
		}
};

2. AVL树的整体

template <class T, class V>
class AVLStree
{
	typedef AvlNode<T, V> node;

public:


private:

	node* root = nullptr;
};

3. AVL树的插入

  1. 首先AVL树插入满足二叉搜索树:
    从根开始,根为空,直接在根上插入;根不为空,小于根节点的k值,往右寻找,大于往左找,按照该规则,直到找到一个为空的节点,在该位置插入。

在这里插入图片描述

bool insert(const pair<T, V>& kv)
{
	if (_root == nullptr)//根为空,在根上插入
	{
		_root = new node(kv);
		return true;
	}
	
	node* cur = _root;
	node* parent = nullptr;
	while (cur)
	{
		if (cur->_kv.first == kv.first)
		{
			return false;
		}
		else if (kv.first < cur->_kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (kv.first > cur->_kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
	}//寻找插入位置
	if (kv.first<parent->_kv.first)
	{
		parent->_left = new node(kv);
	}
	else if (kv.first > parent->_kv.first)
	{
		parent->_right = new node(kv);
	}

}
  1. 插入后调整每个节点的平衡因子
while (parent)
{
	if (cur==parent->_right)//当该节点右子树高度增加了,平衡因子加一。
	{
		parent->_bf++;
	}
	else if (cur == parent->_left)//当该节点左子树高度增加了,平衡因子减一。
	{
		parent->_bf--;
	}

	if (parent->_bf == 0)//平衡因子为0,不用继续向上调节了。
		break;
	else if (parent->_bf == 1 || parent->_bf == -1)//平衡因子为1或-1,继续向上调整。
	{
		cur = parent;
		parent = parent->_parent;
	}
	else//平衡因子为2或-2,需要调节二叉树。
	{
		
	}

};

在这里插入图片描述
当该节点平衡因子为0,就不需要往上修改了,说明以该节点为根的子树,高度没有改变,为1或-1时,就继续向上修改。

  1. 旋转AVL树
    当二叉树的某节点平衡因子为2或-2时,需要旋转二叉树。
    在这里插入图片描述
    如图上这种情况,5节点的左孩子的左子树上插入1层,导致平衡因子变成了-2,可以采取右旋转。
    在这里插入图片描述
    右旋就是将平衡因子为-2节点的左子树的右子树,成为-2节点的左子树,然后使-2节点,成为它原理左子节点的,右子树。(该旋转符合搜索二叉树排序规则)
    我们发现旋转后平衡因子改变的只有根和左子节点的,且都变成了0,其他子树的形状保持不变因此平衡因子未改变,且树的高度也未改变,都是h+2,因此该旋转数上面的节点的平衡因子不受影响。
void RotateR(node* root)
{
	node* cur = root->_left;//先拿到-2节点的左子节点
	root->_left = cur->_right;//根的左子树成为左子节点的右子树
	if(cur->_right)
	cur->_right->_parent = root;//左子节点的右子树的父亲改为根
	cur->_right = root;//让根成为左子树的右节点
	cur->_parent = root->_parent;//让左子树成为新的根节点
	if (root->_parent == nullptr)//判断该节点是否为_root
	{
		_root = cur;			//_root为原来的左子节点
		root->_parent = cur;
		root->_bf = 0;
		cur->_bf = 0;
		return;
	}
	else if (root->_parent->_left == root)
	{
		root->_parent->_left = cur;
	}
	else
	{
		root->_parent->_right = cur;
	}
	root->_parent = cur;//原来的根的父亲改为原来的左子节点
	root->_bf = 0;//将两个改变的平衡因子修改
	cur->_bf = 0;

}
  1. AVL树左旋
    上面讲了右旋,再来看左旋,左旋顾名思义,就是当树的右边插入数据导致平衡因子,超过了1或-1,从而向左边旋转的操作。
    左旋满足根平衡因子为2,右子节点平衡因子为1。
    在这里插入图片描述
void RotateL(node* root)
{
	node* cur = root->_right;//先拿到-2节点的右子节点
	root->_right = cur->_left;//
	if (cur->_left)
		cur->_left->_parent = root;//右子节点的右子树的父亲改为根
	cur->_left = root;//让根成为右子树的左节点
	cur->_parent = root->_parent;//让右子树成为新的根节点
	if (root->_parent == nullptr)//判断该节点是否为_root
	{
		_root = cur;			//_root改为原来的右子节点
		root->_parent = cur;
		root->_bf = 0;
		cur->_bf = 0;
		return;
	}
	else if (root->_parent->_left == root)
	{
		root->_parent->_left = cur;
	}
	else
	{
		root->_parent->_right = cur;
	}
	root->_parent = cur;//原来的根的父亲改为原来的右子节点
	root->_bf = 0;//将两个改变的平衡因子修改
	cur->_bf = 0;

}
  1. AVL树的左右旋转
    在这里插入图片描述
    如图某节点平衡因子为-2时,它的左子树平衡因子为1,这说明左子树的右子树高度是高于左子树的,这时需要以图中3节点,先进行左旋,最后在一5节点进行右旋。
    在这里插入图片描述
    在这里插入图片描述

左右旋分为三种情况,1是给-2节点左子树的右子树的左子树高度加一。如第一个图上,导致4节点平衡因子为-1,这种,经过旋转后新的树的左子树平衡因子为0,右子树平衡因子为1,根的平衡因子为0.
2是给-2节点左子树的右子树的有子树高度加一。如第二个图上,导致4节点平衡因子为1,这种,经过旋转后新的树的左子树平衡因子为-1,右子树平衡因子为0,根的平衡因子为0.
3是图3这种情况,根的左子树的右子节点就是新插入的,此时4节点平衡因子为0。这种经过旋转后,根和左子树,右子树平衡因子都为0.

void RotateLR(node* root)
{
		int bf = root->_left->_right->_bf;//将-2节点左子树的右子树节点记下来
	node* SubL = root->_left;
	node* SubLR = root->_left->_right;//将平衡因子会改变的节点记下来。
	RotateL(SubL);
	RotateR(root);
	if (bf == 1)//
	{
		SubL->_bf = -1;
		SubLR->_bf = 0;
		root->_bf = 0;
	}
	else if (bf == -1)
	{
		SubL->_bf = 0;
		SubLR->_bf = 0;
		root->_bf = 1;
	}
	else if (bf == 0)
	{
		SubL->_bf = 0;
		SubLR->_bf = 0;
		root->_bf = 0;
	}
	else
	{
		assert("bf erron");
	}

}
  1. AVL树的右左旋转

右左旋转是,根节点为2时,它的右子树平衡因子为-1时,进行的旋转。先对右子树进行右旋转,再对根进行左旋转。

右左旋转同样分为3种情况:

  1. 右子树的左子树平衡因子为1,旋转后右子树平衡因子为0,左子树平衡因子为-1,根平衡因子为0.
  2. 右子树的左子树平衡因子为-1,旋转后右子树平衡因子为1,左子树平衡因子为0,根平衡因子为0.
  3. 右子树的左子树平衡因子为0,旋转后右子树平衡因子为0,左子树平衡因子为0,根平衡因子为0.

在这里插入图片描述
在这里插入图片描述

void RotateRL(node* root)
{
	int bf = root->_right->_left->_bf;//将-2节点左子树的右子树节点记下来
	node* SubR = root->_right;
	node* SubRL = root->_right->_left;//将平衡因子会改变的节点记下来。
	RotateR(SubR);
	RotateL(root);
	if (bf == 1)//
	{
		SubR->_bf = 0;//旋转后原来的根右子节点成为了新树根的右子节点。
		SubRL->_bf = 0;//旋转后原来的根右子节点的左子节点成为了新的根。
		root->_bf = -1;//旋转后原来的根成为了新树根的左子节点。
	}
	else if (bf == -1)
	{
		SubR->_bf = 1;
		SubRL->_bf = 0;
		root->_bf = 0;
	}
	else if (bf == 0)
	{
		SubR->_bf = 0;
		SubRL->_bf = 0;
		root->_bf = 0;
	}
	else
	{
		assert("bf erron");
	}

}

总结:无论是左右双旋还是右左双旋,影响最后根,左子节点和右子节点平衡因子的因素是,高度改变的子树的平衡因子。若为-1,则旋转后的新树,左子节点平衡因子为0,右为1.若为1,则旋转后的新树,左子节点平衡因子为-1,右为0。若为0,则旋转后的新树,左右子节点平衡因子都为0.

  1. 二叉树高度和节点数量查询
{
	publi: 
	void Inoder()
	{
		_Inoder(_root);
	}
	int hight()
	{
		return _hight(_root);
	}
	int size()
	{

		return _size(_root);
	}
private:
	void _Inoder(node* root)
	{
		if (root == nullptr)
			return;
		_Inoder(root->_left);
		cout << "K: " << root->_kv.first << ";" << "V: " << root->_kv.second << ";" << "bf: " << root->_bf << endl;
		_Inoder(root->_right);
	}

	int _hight(node* root)
	{
		if (root == nullptr)
			return 0;


		return max(_hight(root->_left), _hight(root->_right)) + 1;
	}

	int _size(node* root)
	{
		if (root == nullptr)
			return 0;


		return _size(root->_left) + _size(root->_right) + 1;
	}

  1. 判断二叉树是否为AVL平衡
bool _isblance(node* root)
{
	if (root == nullptr)
		return true;

	int lefthight = _hight(root->_left);
	int righthight = _hight(root->_right);
	if (lefthight - righthight >= 2 || lefthight - righthight <= -2)
	{
		return false;
	}


	return _isblance(root->_left)&&_isblance(root->_right);
}

三. 整体代码

#pragma once
#include<iostream>
#include<vector>
#include<assert.h>
using namespace std;

template <class T ,class V>
struct AvlNode
{
	AvlNode<T,V>* _right;
	AvlNode<T,V>* _left;
	AvlNode<T,V>* _parent;
	std::pair<T, V> _kv;
	int _bf;

	AvlNode(const std::pair<T,V>& kv)
		:_right(nullptr)
		,_left(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_bf(0)
		{

		}
	
};
template <class T, class V>
class AVLStree
{
	typedef AvlNode<T, V> node;

public:
	bool insert(const pair<T, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new node(kv);
			return true;
		}
		
		node* cur = _root;
		node* parent = nullptr;
		while (cur)
		{
			if (cur->_kv.first == kv.first)
			{
				return false;
			}
			else if (kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
		}
		if (kv.first<parent->_kv.first)
		{
			parent->_left = new node(kv);
			cur = parent->_left;
			cur->_parent = parent;
			
		}
		else if (kv.first > parent->_kv.first)
		{	
			parent->_right = new node(kv);
			cur = parent->_right;
			cur->_parent = parent;
			
		}
		while (parent)
		{
			if (cur==parent->_right)
			{
				parent->_bf++;
			}
			else if (cur == parent->_left)
			{
				parent->_bf--;
			}

			if (parent->_bf == 0)
				break;
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = parent;
				parent = parent->_parent;
			}
			else
			{
				if (parent->_bf == -2 && parent->_left->_bf==-1)//-2的左子节点,的左子树高度加1,右旋
				{
					RotateR(parent);
					return true;
				}
				else if (parent->_bf == 2 && parent->_right->_bf == 1)//-2的左子节点,的左子树高度加1,右旋
				{
					RotateL(parent);
					return true;
				}
				else if (parent->_bf == -2 && parent->_left->_bf == 1)
				{
					RotateLR(parent);
					return true;
				}
				else if (parent->_bf == 2 && parent->_right->_bf == -1)
				{
					RotateRL(parent);
					return true;
				}
			}

     		};



	}
	void RotateR(node* root)
	{
		node* cur = root->_left;//先拿到-2节点的左子节点
		root->_left = cur->_right;//根的左子树成为左子节点的右子树
		if(cur->_right)
		cur->_right->_parent = root;//左子节点的右子树的父亲改为根
		cur->_right = root;//让根成为左子树的右节点
		cur->_parent = root->_parent;//让左子树成为新的根节点
		if (root->_parent == nullptr)//判断该节点是否为_root
		{
			_root = cur;			//_root为原来的左子节点
			root->_parent = cur;
			root->_bf = 0;
			cur->_bf = 0;
			return;
			

		}
		else if (root->_parent->_left == root)
		{
			root->_parent->_left = cur;
		}
		else
		{
			root->_parent->_right = cur;
		}
		root->_parent = cur;//原来的根的父亲改为原来的左子节点
		root->_bf = 0;//将两个改变的平衡因子修改
		cur->_bf = 0;

	}

	void RotateL(node* root)
	{
		node* cur = root->_right;//先拿到-2节点的右子节点
		root->_right = cur->_left;//
		if (cur->_left)
			cur->_left->_parent = root;//右子节点的右子树的父亲改为根
		cur->_left = root;//让根成为右子树的左节点
		cur->_parent = root->_parent;//让右子树成为新的根节点
		if (root->_parent == nullptr)//判断该节点是否为_root
		{
			_root = cur;			//_root为原来的左子节点
			root->_parent = cur;
			root->_bf = 0;
			cur->_bf = 0;
			return;
		}
		else if (root->_parent->_left == root)
		{
			root->_parent->_left = cur;
		}
		else
		{
			root->_parent->_right = cur;
		}
		root->_parent = cur;//原来的根的父亲改为原来的右子节点
		root->_bf = 0;//将两个改变的平衡因子修改
		cur->_bf = 0;

	}
	void RotateLR(node* root)
	{
 		int bf = root->_left->_right->_bf;//将-2节点左子树的右子树节点记下来
		node* SubL = root->_left;
		node* SubLR = root->_left->_right;//将平衡因子会改变的节点记下来。
		RotateL(SubL);
		RotateR(root);
		if (bf == 1)//
		{
			SubL->_bf = -1;
			SubLR->_bf = 0;
			root->_bf = 0;
		}
		else if (bf == -1)
		{
			SubL->_bf = 0;
			SubLR->_bf = 0;
			root->_bf = 1;
		}
		else if (bf == 0)
		{
			SubL->_bf = 0;
			SubLR->_bf = 0;
			root->_bf = 0;
		}
		else
		{
			assert("bf erron");
		}

	}

	void RotateRL(node* root)
	{
		int bf = root->_right->_left->_bf;//将-2节点左子树的右子树节点记下来
		node* SubR = root->_right;
		node* SubRL = root->_right->_left;//将平衡因子会改变的节点记下来。
		RotateR(SubR);
		RotateL(root);
		if (bf == 1)//
		{
			SubR->_bf = 0;//旋转后原来的根右子节点成为了新树根的右子节点。
			SubRL->_bf = 0;//旋转后原来的根右子节点的左子节点成为了新的根。
			root->_bf = -1;//旋转后原来的根成为了新树根的左子节点。
		}
		else if (bf == -1)
		{
			SubR->_bf = 1;
			SubRL->_bf = 0;
			root->_bf = 0;
		}
		else if (bf == 0)
		{
			SubR->_bf = 0;
			SubRL->_bf = 0;
			root->_bf = 0;
		}
		else
		{
			assert("bf erron");
		}

	}
	void Inoder()
	{
		_Inoder(_root);
	}
	int hight()
	{
		return _hight(_root);
	}
	int size()
	{

		return _size(_root);
	}
	bool isblance()
	{
		
		return _isblance(_root);
	}
private:
	void _Inoder(node* root)
	{
		if (root == nullptr)
			return;
		_Inoder(root->_left);
		cout << "K: " << root->_kv.first << ";" << "V: " << root->_kv.second << ";" << "bf: " << root->_bf << endl;
		_Inoder(root->_right);
	}

	int _hight(node* root)
	{
		if (root == nullptr)
			return 0;


		return max(_hight(root->_left), _hight(root->_right)) + 1;
	}

	int _size(node* root)
	{
		if (root == nullptr)
			return 0;


		return _size(root->_left) + _size(root->_right) + 1;
	}

	bool _isblance(node* root)
	{
		if (root == nullptr)
			return true;

		int lefthight = _hight(root->_left);
		int righthight = _hight(root->_right);
		if (lefthight - righthight >= 2 || lefthight - righthight <= -2)
		{
			return false;
		}


		return _isblance(root->_left)&&_isblance(root->_right);
	}
private:

	node* _root = nullptr;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aaa最北边

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值