AVL树的实现和插入

二叉搜索树虽然可以提高查找效率,但是在数据有序或近似有序的情况下,二叉搜索树会退化为单枝树,查找效率大大降低,1962年两位俄罗斯数学家联合发明了以一种解决问题的办法,在对二叉搜索树进行插入时,保证每个节点左右子树高度之差的绝对值不超过1,那么就可以降低树的高度,进而减少平均搜索的深度。这种结构的二叉搜索树就是AVL树。

在这里插入图片描述

AVL树实现

节点的创建:

节点中需要的成员有,左右孩子指针,增加了一个双亲的指针,表示平衡因子的bf,以及值域data。

template<class T>

struct AVLTreeNode {
	AVLTreeNode* left;
	AVLTreeNode* right;
	AVLTreeNode* parent;
	int bf; 
	T data; 

	AVLTreeNode(const T& x) 
		:left(nullptr)
		,right(nullptr)
		,parent(nullptr)
		,bf(0)
		,data(x)
	{}
};
树的构造:
template<class T>

class AVLTree {
	typedef AVLTreeNode<T> Node;

public:
	AVLTree()
		:root(nullptr)
	{}
	~AVLTree() {
		Destory(root);
	}
插入方法:

AVL树的插入与二叉搜索树插入过程类似,首先需要找待插入元素的位置,然后插入元素,到这里是相同的步骤,接下来要满足AVL树的特性,插入新节点cur之后需要更新节点的parent的平衡因子,平衡因子更新有这些情况,
1.新插入的节点在parent的左子树中,这种情况下以parent为根的二叉树,左子树的高度增加了1,所以parent的平衡因子要减去1,
在这里插入图片描述

2.新插入的节点在parent的右子树中,那么以parent为根的二叉树右子树的高度增加了1,所以parent的平衡因子要加上1
在这里插入图片描述

在parent的平衡因子更新后,要看parent平衡因子的值,判断是否更新完成
1.更新后parent的平衡因子是0,说明以parent为根的这颗二叉树在插入前后整体高度没有改变,所以对上层的平衡不会产生影响

在这里插入图片描述
2.更新后parent的平衡因子是-1或1,说明以parent为根的树高度发生变化,对上层的平衡有影响,需要继续向上更新
在这里插入图片描述
3.当parent更新到一个节点,平衡因子为2或者-2的时候,说明AVL树的结构已经被破坏,此时需要通过名为旋转的方法来重新调整AVL树的结构,根据新插入节点位置的不同,旋转需要分为4种情况

①右单旋
在新节点插入后,cur和parent更新到图中位置时,parent的平衡因子更新后为-2,违反了AVL树的性质,而以parent为根的二叉树左子树高度增加了,因此右单旋的实质就是让左子树高度减少1,让右子树高度增加1.
我们给在旋转过程中连接方式发生变化的几个节点加上标识
在这里插入图片描述

在这里插入图片描述
结果:
在这里插入图片描述
②左单旋:
以parent为根的二叉树右子树高度增加了,通过旋转把右子树高度降低1,让左子树高度增加1.
在这里插入图片描述
结果
在这里插入图片描述
左右双旋转,先左后右:

在这里插入图片描述
先对虚线框部分左单旋,
在这里插入图片描述
然后整体进行右单旋:
在这里插入图片描述
双旋,先右后左
在这里插入图片描述
先右:
在这里插入图片描述
整体左单旋
在这里插入图片描述
总结一下:需要直接进行右单旋的时候,新插入节点插入到了较高左子树的外侧,
此时parent的bf为-2,说明是左子树高度增加了1,cur,即parent的左孩子,bf为-1,
在这里插入图片描述
需要进行左单旋的时候,新插入节点在较高右子树的外侧,parent的bf为2,cur的bf为1
在这里插入图片描述
需要进行双选且是先左单旋后右单旋的时候,
新插入的节点位于较高左子树的内侧,
在这里插入图片描述

需要进行双旋且先右单旋再左单旋的时候,
在这里插入图片描述

AVL树及插入

#pragma once

#include <iostream>
using namespace std;

template<class T>
struct AVLTreeNode{
	AVLTreeNode<T>* left;
	AVLTreeNode<T>* right;
	AVLTreeNode<T>* parent;
	T data;
	int bf;   // 表示节点的平衡因子

	AVLTreeNode(const T& x)
		: left(nullptr)
		, right(nullptr)
		, parent(nullptr)
		, data(x)
		, bf(0)
	{}
};


template<class T>
class AVLTree{
	typedef AVLTreeNode<T> Node;

public:
	AVLTree()
		: root(nullptr)
	{}

	~AVLTree(){
		Destroy(root);
	}

	bool Insert(const T& val){
		if (nullptr == root){
			root = new Node(val);
			return true;
		}
		Node* cur = root;
		Node* parent = nullptr;
		while (cur) {
			parent = cur;
			if (val < cur->data) {
				cur = cur->left;
			}
			else if (val > cur->data) {
				cur = cur->right;
			}
			else {
				return false;
			}
		}

		cur = new Node(val);
		if (val < parent->data) {
			parent->left = cur;
		}
		else {
			parent->right = cur;
		}
		cur->parent = parent;
		//更新parent的平衡因子
		while (parent){
			if (cur == parent->left) {
				parent->bf--;
			}
			else {
				parent->bf++;
			}
			if (0 == parent->bf) {   // 以parent为根的二叉树的高度没有改变,对上层不会有任何影响
				break;
			}
			else if (1 == parent->bf || -1 == parent->bf){
				// 以parent为根的二叉树的高度增加了,需要继续往上更新
				cur = parent;
				parent = cur->parent;
			}
			else{
				// parent节点已经违反AVL树的特性
				// 需要对以parent为根的二叉树进行旋转处理
				if (-2 == parent->bf) {
					// parent的左子树高---最终需要右单旋
					if (-1 == cur->bf) {
						RotateRight(parent);
					}
					else {
						RotateLR(parent);
					}
				}
				else {
					// parent的右子树高---最终需要左单旋
					if (1 == cur->bf) {
						RotateLeft(parent);
					}
					else {
						RotateRL(parent);
					}
				}
				break;
			}
		}
		return true;
	}

	void InOrder(){
		_InOrder(root);
		cout << endl;
	}

	bool IsAVLTree(){
		return _IsAVLTree(root);
	}

private:
	bool _IsAVLTree(Node* proot){
		if (nullptr == proot){
			return true;
		}

		int leftHeight = _Height(proot->left);
		int rightHeight = _Height(proot->right);
		int bf = rightHeight - leftHeight;
		if (abs(proot->bf) > 1 || bf != proot->bf){
			cout << proot->data << ":" << bf << "--" << proot->bf << endl;
			return false;
		}
		return _IsAVLTree(proot->left) && _IsAVLTree(proot->right);
	}

	int _Height(Node* proot){
		if (nullptr == proot) {
			return 0;
		}
		int leftHeight = _Height(proot->left);
		int rightHeight = _Height(proot->right);
		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

	void _InOrder(Node* proot){
		if (proot){
			_InOrder(proot->left);
			cout << proot->data << " ";
			_InOrder(proot->right);
		}
	}

	void RotateRight(Node* parent){
		Node* subL = parent->left;
		Node* subLR = subL->right;

		parent->left = subLR;
		if (subLR) {
			subLR->parent = parent;
		}
		subL->right = parent;
		Node* pparent = parent->parent;
		parent->parent = subL;
		subL->parent = pparent;
		if (nullptr == pparent){
			root = subL;
		}
		else {
			if (parent == pparent->left) {
				pparent->left = subL;
			}
			else {
				pparent->right = subL;
			}
		}
		subL->bf = parent->bf = 0;
	}

	void RotateLeft(Node* parent){
		Node* subR = parent->right;
		Node* subRL = subR->left;
		parent->right = subRL;
		if (subRL) {
			subRL->parent = parent;
		}
		subR->left = parent;
		Node* pparent = parent->parent;
		parent->parent = subR;
		subR->parent = pparent;
		if (nullptr == pparent){
			root = subR;
		}
		else {
			if (parent == pparent->left) {
				pparent->left = subR;
			}
			else {
				pparent->right = subR;
			}
		}
		parent->bf = subR->bf = 0;
	}

	void RotateLR(Node* parent){
		Node* subL = parent->left;
		Node* subLR = subL->right;
		int bf = subLR->bf;

		RotateLeft(parent->left);
		RotateRight(parent);

		// 重新更新部分节点的平衡因子
		if (-1 == bf) {
			parent->bf = 1;
		}
		else if (1 == bf) {
			subL->bf = -1;
		}
	}

	void RotateRL(Node* parent) {
		Node* subR = parent->right;
		Node* subRL = subR->left;
		int bf = subRL->bf;

		RotateRight(parent->right);
		RotateLeft(parent);

		// 重新更新部分节点的平衡因子
		if (-1 == bf) {
			subR->bf = 1;
		}
		else if (1 == bf) {
			parent->bf = -1;
		}
	}

	void Destroy(Node*& proot){
		if (proot){
			Destroy(proot->left);
			Destroy(proot->right);
			delete proot;
			proot = nullptr;
		}
	}
private:
	Node* root;
};

应用

AVL树的优点在于高度平衡,因此查找效率很高,但是其在插入和删除的过程中有时候需要大量的进行旋转,尤其在删除时,最差的情况下要一直旋转直到更新到根的位置。一般需要用到平衡树的场景中很少会考虑到AVL树,更多的会使用红黑树。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值