高阶数据结构与算法 | AVL树(高度平衡树)的实现

二叉平衡树(AVL树)

AVL树其实就是在二叉搜索树的基础上,引入平衡因子的概念,通过旋转来调整平衡因子,使得二叉树始终平衡,效率更高.

特点:

  1. 本身首先是一棵二叉搜索树
  2. 带有平衡条件:每个节点的左右子树的高度之差的绝对值(平衡因子)最多为1

AVL树的性能:

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树的高度差的绝对值不超过1,这样就可以保证.

查询时高效的时间复杂度:log2(N),但是如果AVL树做一些结构修改的操作,性能就非常低下:

插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转到根的位置

因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),则可以考虑AVL树.

实现思路:

数据结构:这里不仅需要左右子树,因为涉及到大量的平衡因子调节,所有还需要保存父节点的指针,要用到三叉链的结构.

template
struct AVLNode {
	int _val;
	int _bf; //平衡因子
	AVLNode* _left;
	AVLNode* _right;
	AVLNode* _parent;
	AVLNode(const T& val = T())
		:_val(val)
		,_bf(0)
		,_left(nullptr)
		,_right(nullptr)
		_parent(nullptr)
	{}
};

查找:

AVL树的本质其实还是二叉搜索树,所有查找的部分与二叉搜索树的相同,直接复用,不用修改.

直接从根节点出发,,比根节点小则查找左子树,比根节点大则查找右子树.相同则直接返回.如果遍历完还没有找到,则说明想要查找的节点不在此树中,返回nullptr.

Node* Find(const K& key)
{
	//根据二叉搜索树的性质,从根节点出发,比根节点大则查找右子树,比根节点小则查找左子树
	Node* cur = _root;

	while (cur)
	{
		//比根节点大则查找右子树
		if (key > cur->_kv.first)
		{
			cur = cur->_right;
		}
		//比根节点小则查找左子树
		else if (key < cur->_kv.first)
		{
			cur = cur->_left;
		}
		//相同则返回
		else
		{
			return cur;
		}
	}

	//遍历完则说明查找不到,返回false
	return nullptr;
}

插入(查找+插入+旋转):

插入分三个步骤:

  1. 按照二叉搜索树的规则找到合适的位置插入
  2. 更新插入的平衡因子
  3. 根据平衡因子来选择是否进行旋转调节

平衡因子

平衡因子,其实就是左右子树的高度差.AVL树通过控制高度差不超过2,来实现平衡.

当某节点平衡因子为0时,说明它的左右子树平衡

当平衡因子为1或者-1时,说明左右子树存在高度差,其父节点可能存在不平衡,需要向上继续判断.

当平衡因子为2或者-2时,说明此事不平衡,需要旋转处理.

AVL树的插入调整:

假如以parent为根的子树不平衡,即parent的平衡因子为2或者-2,分一下情况考虑:

parent的平衡因子为2,说明parent的右子树高,设parent的右子树的根为subR

(1).当subR的平衡因子为1时,即右边的右边高,执行左单旋;

高阶数据结构与算法 | AVL树(高度平衡树)的实现_第1张图片

左旋的步骤:

  1. parent变为subR的左子树
  2. subRL变为parent的右子树
  3. 如果subRL存在,则将subRL的父节点连接到parent
  4. 连接subR祖父节点
  5. 更新parent父节点,连接到subR
  6. 更新 parentsubR 的平衡因子为0
     void RotateL(){
         Node* subR = parent->_right;
         Node* subRL = subR->_left;
         
         subR->_left = parent;
         parent->_right = subRL;
         if(subRL){
             subRL->_parent = parent;
         }
         //连接subR与祖父节点
         if(parent == _root){
             _root = subR;
             subR->_parent = nullptr;
         }else{
             Node* g = parent->_parent;
             subR->_parent = g;
             if(g->_left == parent){
                 g->_left = subR;
             }else{
                 g->_right = subR;
             }
         }
         parent->_parent = subR;
         parent->_bf = subR->_bf = 0;
     }

(2).当subR的平衡因子为-1时,即右边的左边高,执行右左双旋;

高阶数据结构与算法 | AVL树(高度平衡树)的实现_第2张图片

注意:红蓝节点分别为两种插入情况

右左双旋的步骤: 

  1. 保存subRL的平衡因子(用来判断更新parent和subR的平衡因子)
  2. cur节点为父节点,先进行右旋
  3. 再以parent为父节点进行左旋
  4. 调节平衡因子,根据subRL一开始的平衡因子进行判断调节 有两种情况:①.subRL->_bf==1,则parent->_bf = -1,subR->_bf = 0; ②.subRL->_bf == -1,则 parent->_bf = 0,subR->bf = 1;
 if(parent->_bf == 2 && cur->_bf == -1){
     //右边的左边高:右旋+左旋
     Node* subR = parent->_right;
     Node* subRL = subR->_left;
     int bf = subRL->_bf;
     //右旋+左旋
     RotateR(cur);
     RotateL(parent);
     //更新平衡因子
     if(bf == 1){
         parent->_bf = -1;
         subR->_bf = 0;
     }else{
         parent->_bf = 0;
         subR->_bf = 1;
     }
 }

2.parent的平衡因子为-2,说明parent的左子树高,设parent的左子树的根为subL

(1).当subL的平衡因子为-1时,即左边的左边高,执行右单旋;
高阶数据结构与算法 | AVL树(高度平衡树)的实现_第3张图片

右旋的步骤:

  1. parent变为subL的右子树
  2. subLR变为parent的左子树
  3. 如果subLR存在,则将subRL的父节点连接到parent
  4. 连接subL祖父节点
  5. 更新parent父节点,连接到subL
  6. 更新 parentsubL 的平衡因子为0
//右旋:
     void RotateR(Node* parent) {
         Node* subL = parent->_left;
         Node* subLR = subL->_right;
 ​
         subL->_right = parent;
         parent->_left = subLR;
         if (sunLR) {
             subLR->_parent = parent;
         }
         if (parent == root) {
             _root = subL;
             subL->_parent = nullptr;
         }
         else {
             Node* g = parent->_parent;
             subL->_parent = g;
             if (g->_left == parent) {
                 g->_left = subL;
             }
             else {
                 g->_right = subL;
             }
         }
         //再连接parent的_parent
         parent->_parent = subL;
         parent->_bf = subL->_bf = 0;
     }

(2).当subL的平衡因子为1时,即左边的右边高,执行左右双旋;

高阶数据结构与算法 | AVL树(高度平衡树)的实现_第4张图片

注意:红蓝节点分别为两种插入情况

左右双旋的步骤: 

  1. 保存subLR的平衡因子(用来判断更新parent和subL的平衡因子)
  2. cur节点为父节点,先进行左旋
  3. 再以parent为父节点进行右旋
  4. 调节平衡因子,根据subRL一开始的平衡因子进行判断调节 有两种情况:①.subLR->_bf==1,则parent->_bf = 0,subL->_bf = -1; ②.subLR->_bf == -1,则 parent->_bf = 1,subL->bf = 0;
 if(parent->_bf == -2 && cur->_bf == 1){
     //左边的右边高:左旋+右旋
     Node* subL = parent->_left;
     Node* subLR = subL->_right;
     int bf = subLR->_bf; //保存subLR的平衡因子,用来判断更新subL和parent的平衡因子
     //左旋+右旋:
     RotateL(cur);
     RotateR(parent);
     //更新平衡因子:
     if(bf == 1){
         parent->_bf = 0;
         subL->_bf = -1;
     }else{
         parent->_bf = 1;
         subL->_bf = 0;
     }
 }

旋转完成后,原parent为根的子树高度降低,已经平衡,不需要再向上更新.

删除:

AVL树的删除极为复杂,但是思路还是很简单.

1.按照二叉搜索树的规则进行删除

2.更新平衡因子,并且进行旋转来调整(最坏的情况下可能会一直调整到根节点)

完整代码实现:


#include 
using namespace std;
 
template
struct AVLNode {
	T _value;
	AVLNode* _left;
	AVLNode* _right;
	AVLNode* _parent;
	int _bf; //平衡因子
 
	AVLNode(const T& val = T())
		:_value(val)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _bf(0)
	{}
};
 
template
class AVLTree {
public:
	typedef AVLNode Node;
	AVLTree()
		:_root(nullptr)
	{}
	//搜索树的插入
	bool insert(const T& val) {
		//如果根为空
		if (_root == nullptr) {
			_root = new Node(val);
			return true;
		}
		//查找插入的位置
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur) {
			parent = cur;
			if (cur->_value == val)
				return false;
			else if (cur->_value < val)
				cur = cur->_left;
			else 
				cur = cur->_right;
		}
		//插入
		cur = new Node(val);
		if (parent->_value < val)
			parent->_right = cur;
		else
			parent->_left = cur;
 
		cur->_parent = parent;
 
		//更新+调整
		while (parent) {
			//更新parent平衡因子
			if (parent->_left == cur)
				--parent->_bf;
			else
				++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->_bf == 2) {
				//调整
				if (parent->_bf == -2 && cur->_bf == -1) {
					//左边的左边高,需要右旋
					RotateR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == 1) {
					//右边的右边高,需要左旋
					RotateL(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1) {
					//右边的左边高,需要右旋+左旋
					Node* subR = parent->_right;
					Node* subRL = subR->_left;
					int bf = subRL->_bf;
					//以cur为轴左旋
					RotateR(cur);
					//以parent为轴右旋
					RotateL(parent);
 
					//调整平衡因子
					if (bf == 1) {
						subR->_bf = 0;
						parent->_bf = -1;
					}
					else if (bf == -1) {
						subR->_bf = 1;
						parent->_bf = 0;
					}
				}
				else if (parent->_bf == -2 && cur->_bf == 1) {
					//左边的右边高
					Node* subL = parent->_left;
					Node* subLR = subL->_right;
					int bf = subLR->_bf;
 
					RotateL(cur);
					RotateR(parent);
 
					if (bf == 1) {
						parent->_bf = 0;
						subL->_bf = -1;
					}
					else if (bf == -1) {
						parent->_bf = 1;
						subL->_bf = 0;
					}
				}
				break;
			}
		}
		return true;
	}
	//右旋:
	void RotateR(Node* parent) {
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
 
		subL->_right = parent;
		parent->_left = subLR;
 
		if (subLR) {
			subLR->_parent = parent;
		}
		//先连接subL的_parent
		if (parent == _root) {
			_root = subL;
			subL->_parent = nullptr;
		}
		else {
			Node* g = parent->_parent;
			subL->_parent = g;
			if (g->_left == parent)
				g->_left = subL;
			else
				g->_right = subL;
		}
		//再连接parent的_parent
		parent->_parent = subL;
		parent->_bf = subL->_bf = 0;
	}
	//左旋:
	void RotateL(Node* parent) {
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
 
		subR->_left = parent;
		parent->_right = subRL;
 
		if (subRL)
			subRL->_parent = parent;
 
		if (parent == _root) {
			_root = subR;
			subR->_parent = nullptr;
		}
		else {
			Node* g = parent->_parent;
			subR->_parent = g;
			if (g->_left==parent)
				g->_left = subR;
			else
				g->_right = subR;
		}
		parent->_parent = subR;
		parent->_bf = subR->_bf = 0;
	}
 
 
	void inorder() {
		_inorder(_root);
		cout << endl;
	}
	void _inorder(Node* root){
		if (root) {
			_inorder(root->_left);
			cout << root->_value << " ";
			_inorder(root->_right);
		}		
	}
 
	bool isBalance()
	{
		return _isBalance(_root);
	}
 
	bool _isBalance(Node* root)
	{
		if (root == nullptr)
			return true;
 
		//左右子树高度差是否和平衡因子相等
		int subL = Height(root->_left);
		int subR = Height(root->_right);
		if (root->_bf != subR - subL)
		{
			cout << "节点:" << root->_value << "异常: bf: " << root->_bf << " 高度差:" << subR - subL << endl;
			return false;
		}
		//平衡因子的绝对值知否小于2
		return abs(root->_bf) < 2
			&& _isBalance(root->_left)
			&& _isBalance(root->_right);
	}
 
	int Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int left = Height(root->_left);
		int right = Height(root->_right);
		return left > right ? left + 1 : right + 1;
	}
private:
	Node* _root;
};

你可能感兴趣的:(二叉树,数据结构,算法,AVL树)