锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

数据结构进阶—AVL树(高度平衡二叉搜索树)

时间:2023-04-09 23:37:00 lqm18dh片式电感器

文章目录

  • 1、AVL树的基本概念
    • 1.1 性质
    • 1.2 适用场景
  • 2、AVL实现树的插入
    • 2.1 调整平衡因子
    • 2.2 四种旋转情况
      • 2.2.1 右单旋(RR型)
      • 2.2.2 左单旋(LL型)
      • 2.2.3 左右单旋(LR型)
      • 2.2.4 右左单旋(RL型)
  • 3.整体代码及验证
    • 3.1 代码
    • 3.2 验证


1、AVL树的基本概念

虽然二叉搜索树可以缩短搜索效率,但是,如果数据有序或接近有序,二叉搜索树将退化为单树,搜索元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis1962年发明了解决上述问题的方法:将新的结点插入到二叉搜索树中,如果每个结点左右子树的绝对值不超过1(需要调整树中的结点),可以降低树的高度,从而降低平均搜索长度

1.1 性质

一棵AVL或空树,或具有以下性质的二叉搜索树:

  • 它的左右子树都是AVL树
  • 左右子树的绝对值不得超过1(-1/0/1)
  • 如果一棵二叉搜索树高度平衡,那就是AVL树。若有n个结点,其高度可保持在 ,搜索时间复杂度O( l o g 2 N log_2 N log2N)

1.2 适用场景

AVL树是一棵绝对平衡的二叉搜索树,要求每个节点左右子树高度差的绝对值不超过1,保证查询时高效的时间复杂性,即O( l o g 2 N log_2 N log2N) 。但如果要对的话AVL树木做一些结构修改操作,性能很低,如:
插入时,应保持绝对平衡,旋转次数较多。更糟糕的是,在删除时,旋转可能会持续到根部。
因此:如果需要一个高效有序的查询数据结构,数据的数量是静态的(即不会改变),可以考虑AVL但是经常修改一个结构是不合适的

2、AVL实现树的插入

2.1 调整平衡因子

在这里插入图片描述

2.2 四种旋转情况

2.2.1 右单旋(RR型)

代码实现:

void RotateR(Node* parent) { 
          Node* subL = parent->_left;//parent左边一定不能nullptr,因为平衡因素是-2  Node* subLR = subL->_right;//subL的右边可能nullptr
	Node* pparent = parent->_parent;//保存parent的根(父节点)

	//将subLR链接到parent的左侧
	parent->_left = subLR;
	if (subLR != nullptr)
		subLR->_parent = parent;

	//将parent链接到subL右侧
	subL->_right = parent;
	parent->_parent = subL;

	//将subL和pparent链接起来
	//pparent为nullptr,subL要成为新的根
	if (pparent == nullptr)
	{ 
        
		subL->_parent = pparent;
		_root = subL;
	}
	//pparent不为nullptr,就链接pparent和subL
	else
	{ 
        
		if (pparent->_left == parent)
			pparent->_left = subL;
		else
			pparent->_right = subL;
		subL->_parent = pparent;			
	}

	//更新平衡因子
	subL->_bf = parent->_bf = 0;
}

2.2.2 左单旋(LL型)


代码实现:

void RotateL(Node* parent)
{ 
        
	Node* subR = parent->_right;//parent的的右边一定不为nullptr,因为平衡因子为2
	Node* subRL = subR->_left;//subR的左边可能为nullptr
	Node* pparent = parent->_parent;//保存parent的根(父节点)

	//将subRL链接到parent的右侧
	parent->_right = subRL;
	if (subRL != nullptr)
		subRL->_parent = parent;

	//将parent链接到subR左侧
	subR->_left = parent;
	parent->_parent = subR;

	//将subR和pparent链接起来
	//pparent为nullptr,subR要成为新的根
	if (pparent == nullptr)
	{ 
        
		subR->_parent = pparent;
		_root = subR;
	}
	//pparent不为nullptr,就链接pparent和subR
	else
	{ 
        
		if (pparent->_right == parent)
			pparent->_right = subR;
		else
			pparent->_left = subR;
		subR->_parent = pparent;
	}

	//更新平衡因子
	subR->_bf = parent->_bf = 0;
}

2.2.3 左右单旋(LR型)


代码实现:

void RotateLR(Node* parent)
{ 
        
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	int bf = subLR->_bf;//保存平衡因子,后面会根据bf更新其他结点的平衡因子

	RotateL(parent->_left);
	RotateR(parent);

	//更新平衡因子
	if (bf == -1)//在左边插入
	{ 
        
		parent->_bf = 1;
		subL->_bf = subLR->_bf = 0;
	}
	else if (bf == 1)//在右边插入
	{ 
        
		subL->_bf = -1;
		parent->_bf = subLR->_bf = 0;
	}
	else if (bf == 0)
		parent->_bf = subL->_bf = subLR->_bf = 0;
	else
		assert(false);
}

2.2.4 右左单旋(RL型)


代码实现:

void RotateRL(Node* parent)
{ 
        
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf;//保存平衡因子,后面会根据bf更新其他结点的平衡因子


	RotateR(parent->_right);
	RotateL(parent);

	//更新平衡因子
	if (bf == -1)//在subRL左边插入
	{ 
        
		subR->_bf = 1;
		parent->_bf = subRL->_bf = 0;
	}
	else if (bf == 1)//在subRL右边插入
	{ 
        
		parent->_bf = -1;
		subR->_bf = subRL->_bf = 0;
	}
	else if (bf == 0)
		parent->_bf = subR->_bf = subRL->_bf = 0;
	else
		assert(false);
}

3、整体代码及验证

3.1 代码

#pragma once

#include
#include
#include
#include
using namespace std;
template<class K, class V>
struct AVLTreeNode
{ 
        
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	pair<K, V> _kv;
	int _bf;//balance factor

	AVLTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _bf(0)
		, _kv(kv)
	{ 
        }
};

template<class K, class V>
class AVLTree
{ 
        
	typedef AVLTreeNode<K, V> Node;

public:
	AVLTree()
		:_root(nullptr)
	{ 
        }
	
	bool Insert(const pair<K, V>& kv)
	{ 
        
		//_root为nullptr,说明是插入的第一个值
		if (_root == nullptr)
		{ 
        
			_root = new Node(kv);
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{ 
        
			//插入的值比cur大,说明要在右边插入
			if (kv.first > cur->_kv.first)
			{ 
        
				parent = cur;
				cur = cur->_right;
			}
			//插入的值比cur小,说明要在左边插入
			else if (kv.first < cur->_kv.first)
			{ 
        
				parent = cur;
				cur = cur->_left;
			}
			else
				return false;
		}

		//链接父子结点
		cur = new Node(kv);
		if (kv.first > parent->_kv.first)
		{ 
        
			parent->_right = cur;
			cur->_parent = parent;
		}
		else
		{ 
        
			parent->_left = cur;
			cur->_parent = parent;
		}

		//控制平衡
		//1、更新平衡因子
		//2、出现异常平衡因子,则需要进行旋转调整
		while (parent)
		{ 
        
			if (cur == parent->_left)
				parent->_bf--;
			else
				parent->_bf++;
			
			//判断是需要继续向上调整

			//平衡因子为0,则不需要向上调整
			if (parent->_bf == 0)
				break;

			//平衡因子为-1/1,继续向上调整
			else if (abs(parent->_bf) == 1)
			{ 
        
				cur = parent;
				parent = parent->_parent;
			}
			
			//平衡因子为-2/2,需要进行旋转处理,然后重新调节平衡因子
			else if (abs(parent->_bf) == 2)
			{ 
        
				//右单旋
				if (parent->_bf == -2 && parent->_left->_bf == -1)
					RotateR(parent);

				//左单旋
				else if (parent->_bf == 2 && parent->_right->_bf == 1)
					RotateL(parent);

				//左右单旋
				else if (parent->_bf == 2 && parent->_left->_bf == 1)
				{ 
        
					RotateLR(parent);
				}

				//右左单旋
				else if (parent->_bf == 2 && parent->_right->_bf == -1)
				{ 
        
					RotateRL(parent);
				}

				else
					assert(false);

				break;
			}

			//平衡因子如果不为以上情况,则说明在插入之前,树的平衡因子已经存在问题
			else
				assert(false);

		}
		
		return true;
	}

	void InOrder()
	{ 
        
		_InOrder(_root);
	}

	bool Isbalance()
	{ 
        
		if (_Isbalance(_root) == -1)
			return false;
		return true;
	}
private:
	Node* _root;

	//后续遍历判断该树是否平衡
	int _Isbalance(Node* root)
	{ 
        
		if (root == nullptr)
			return 0;

		int left = _Isbalance(root->_left);
		int right = _Isbalance(root->_right);

		if (abs(root->_bf) > 1 || abs(right - left) > 1)//不满足条件,返回-1
			return -1;

		return (int)fmax(left, right) + 1;//返回当前树的高度
	}
	
	void _InOrder(Node* root)
	{ 
        
		if (root == nullptr)
			return;

		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl; 
		//cout << root->_kv.first << ":" << root->_kv.second;
		//cout << "平衡因子为:" << root->_bf << endl;
		_InOrder(root->_right);
	}

	//右单旋
	void RotateR(Node* parent)
	{ 
        
		Node* subL = parent->_left;//parent的的左边一定不为nullptr,因为平衡因子为-2
		Node* subLR = subL->_right;//subL的右边可能为nullptr
		Node* pparent = parent->_parent;//保存parent的根(父节点)

		//将subLR链接到parent的左侧
		parent->_left = subLR;
		if (subLR != nullptr)
			subLR->_parent = parent;

		//将parent链接到subL右侧
		subL->_right = parent;
		parent->_parent = subL;

		//将subL和pparent链接起来
		//pparent为nullptr,subL要成为新的根
		if (pparent == nullptr)
		{ 
        
			subL->_parent = pparent;
			_root = subL;
		}
		//pparent不为nullptr,就链接pparent和subL
		else
		{ 
        
			if (pparent->_left == parent)
				pparent->_left = subL;
			else
				pparent->_right = subL;
			subL->_parent = pparent;
		}

		//更新平衡因子
		subL->_bf = parent->_bf = 0;
	}

	//左单旋
	void RotateL(Node* parent)
	{ 
        
		Node* subR = parent->_right;//parent的的右边一定不为nullptr,因为平衡因子为2
		Node* subRL = subR->_left;//subR的左边可能为nullptr
		Node* pparent = parent->_parent;//保存parent的根(父节点)

		//将subRL链接到parent的右侧
		parent->_right = subRL;
		if (subRL != nullptr)
			subRL->_parent = parent;

		//将parent链接到subR左侧
		subR->_left = parent;
		parent->_parent = subR;

		//将subR和pparent链接起来
		//pparent为nullptr,subR要成为新的根
		if (pparent == nullptr)
		{ 
        
			subR->_parent = pparent;
			_root = subR;
		}
		//pparent不为nullptr,就链接pparent和subR
		else
		{ 
        
			if (pparent->_right == parent)
				pparent->_right = subR;
			else
				pparent->_left = subR;
			subR->_parent = pparent;
		}

		//更新平衡因子
		subR->_bf = parent->_bf = 0;
	}

	//右旋左旋
	void RotateRL(Node* parent)
	{ 
        
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;//保存平衡因子,后面会根据bf更新其他结点的平衡因子


		RotateR(parent->_right);
		RotateL(parent);

		//更新平衡因子
		if (bf == -1)//在subRL左边插入
		{ 
        
			subR->_bf = 1;
			parent->_bf = subRL->_bf = 0;
		}
		else if (bf == 1)//在subRL右边插入
		{ 
        
			parent->_bf = -1;
			subR->_bf = subRL->_bf = 0;
		}
		else if (bf == 0)
			parent->_bf = subR->_bf = subRL->_bf = 0;
		else
			assert(false);
	}

	//左旋右旋
	void RotateLR(Node* parent)
	{ 
        
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;//保存平衡因子,后面会根据bf更新其他结点的平衡因子

		RotateL(parent->_left);
		RotateR(parent);

		//更新平衡因子
		if (bf == -1)//在左边插入
		{ 
        
			parent->_bf = 1;
			subL->_bf = subLR->_bf = 0;
		}
		else if (bf == 1)//在右边插入
		{ 
        
			subL->_bf = -1;
			parent->_bf = subLR->_bf = 0;
		}
		else if (bf == 0)
			parent->_bf = subL->_bf = subLR->_bf = 0;
		else
			assert(false);
	}
};

3.2 验证

void test()
{ 
        
	AVLTree<int, int> avl;
	int a[] = { 
         4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	for (auto e : a)
	{ 
        
		avl.Insert(make_pair(e, e));
	}
	avl.InOrder();
}

int main()
{ 
        
	test();
	return 0;
}

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章