【数据结构】—— 浅析AVL树及模拟实现

本文深入探讨了AVL树的概念及其重要性,特别是在解决二叉搜索树在数据有序时退化为单支树的问题。文章详细介绍了AVL树的性质、平衡因子的维护以及在插入节点后的旋转操作,包括左单旋、右单旋、左右双旋和右左双旋,以保持树的平衡状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AVL树

AVL树的概念

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

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

  • 它的左右子树都是AVL树
  • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

AVL树

模拟实现一个AVL树(主要实现插入节点功能)

插入一个节点

  • 和二叉搜索树插入一个节点一样
  • 若是树为空直接插入
  • 若不为空,先找到插入位置,再插入节点
  • 可参见博客: 模拟实现一个二叉搜索树

调剂平衡因子(灰常重要)

  • 如果节点插入到父节点的左边,父节点的平衡因子bf -1
  • 如果节点插入到父节点的右边,父节点的平衡因子bf +1
  • 1、插入节点后父节点的平衡因子|bf| == 0;

AVLTree

  • 2、插入节点后父节点的平衡因子|bf| == 1;

AVLTree

  • 3、插入节点后父节点的平衡因子|bf| == 2;
    AVLTree

AVL树的旋转

  • 1、新节点插入较高右子树的右侧—右右:左单旋

左单旋

  • 2、 新节点插入较高左子树的左侧—左左:右单旋
    右单旋
  • 3、新节点插入较高左子树的右侧—左右:先左单旋再右单旋

左右单旋

  • 4、新节点插入较高右子树的左侧—右左:先右单旋再左单旋
    右左单旋

完整代码实现

#pragma once

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

template<class K,class V>
struct AVLTreeNode
{
	//AVLTreeNode(const pair<K, V>& kv)
	//:_left(nullptr)
	//, _right(nullptr)
	//, _kv(kv)
	//, _bf(0)
	//{}

	AVLTreeNode* _left;
	AVLTreeNode* _right;
	AVLTreeNode* _parent;
	pair<K, V> _kv;

	int _bf;//平衡因子
};

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	AVLTree()
		:_root(nullptr)
	{}

	bool Insert(const pair<K, V>& kv)
	{
		//二叉树搜索树一样插入,插入之后更新平衡因子
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}

		Node* cur = _root;
		Node* parent = nullptr;

		while (cur)
		{
			if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
				
			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->_left = cur;
			cur->_parent = parent;
		}
			
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}

		//插入节点成功,更新平衡因子
		while (parent)
		{
			//cur插入parent的左边,parent的bf -1
			//cur插入parent的右边,parent的bf +1
			if (parent->_left == cur)
				parent->_bf--;
			else
				parent->_bf++;
			//1.parent的bf == 0,说明插入之前parent的bf为正负1,AVL树的高度不变,不会影响上层节点的bf,不用调整
			if (parent->_bf == 0)
				break;
			//2.parent的|bf| == 1,说明插入之前parent的bf为0,AVL树的高度改变,更新parent到root节点的bf
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = parent;
				parent = parent->_parent;
			}
			//3.parent的|bf| == 2,已经破坏该树的平衡,违反AVL树的性质,需要对树进行旋转
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				//旋转
			}
			else
			{
				assert(false);
			}
		}	
		return true;
	}

	//左单旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		Node* pparent = parent->_parent;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		subR->_left = parent;
		parent->_parent = subR;

		if (parent == _root)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subR;
				subR->_parent = pparent;
			}
			else
			{
				pparent->_right = subR;
				subR->_parent = pparent;
			}
		}
		parent->_bf = subR->_bf = 0;
	}
	
	//右单旋
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		Node* pparent = parent->_parent;

		parent->_left = subLR;
		if (subLR)//置parent->_left的时候可以不管subLR是否为空,但是若是subLR为空取其parent就会出错
		{
			subLR->_parent = parent;
		}

		subL->_right = parent;
		parent->_parent = subL;

		if (pparent == _root)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subL;
				subL->_parent = pparent;
			}
			else
			{
				pparent->_right = subL;
				subL->_parent = pparent;
			}

		}
		subL->_bf = parent->_bf = 0;
	}

	//左右双旋
	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		int bf = subLR->_bf;
		RotateL(subL);//先左单旋
		RotateR(parent);//再右单旋

		if (bf == 0)
		{
			parent->_bf = subL->_bf = subLR->_bf = 0;
		}
		else if (bf == 1)
		{
			parent->_bf = 0;
			subL->_bf = -1;
			subLR->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 1;
			subL->_bf = 0;
			subLR->_bf = 0;
		}
		else
			assert(false);
	}

	//右左双旋
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		int bf = subRL->_bf;
		RotateR(subR);
		RotateL(parent);

		if (bf == 0)
			parent->_bf = subR->_bf =subRL->_bf = 0;
		else if (bf == 1)
		{
			parent->_bf = -1;
			subR->_bf = 0;
			subRL->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 0;
			subR->_bf = 1;
			subRL->_bf = 0;
		}
		else
			assert(false);
	}
private:
	Node* _root;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值