图解 AVL 树 -【数据结构】

文章目录:

  • AVL树的概念
  • AVL树节点类
  • AVL树的插入(insert)
  • AVL树的旋转
    • 右单旋
    • 左单旋
    • 左右双旋
    • 右左双旋
  • AVL树的删除(erase)
  • AVL树数据查找(find)
  • 析构函数 和 operator[]
  • AVL数的验证
  • AVL树的性能

AVL树的概念

二叉搜索树虽然可以提高查找数据的效率,但是若插入二叉搜索树的数据有序或者接近有序,那么二叉搜索树将会退化为单支结构,因此查找数据的效率降低。这种特殊场景下二叉搜索树不能高效率查询数据。

因此,1962年两位俄罗斯的科学家 G. M. Adelson-Velsky 和 E. M. Landis 在论文中发表了一种解决上诉问题的数据结构 - AVL树:其解决方法是当向二叉搜索树中插入新增节点后,若能够保证树中任意节点的左右子树的高度差绝对值不超过1,即可降低树的高度,减少平均搜索长度。

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

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

图解 AVL 树 -【数据结构】_第1张图片
AVL树若有N个节点,它的高度可以保持在O(logN) , 搜索的时间复杂度是O(logN) 。

AVL树节点类

这里我们实现为KV结构,为便于后序的插入和调整节点,我们将AVL树中节点定义为三叉链结构。

template<class K,class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	int _bf; // 平衡因子 = 右子树高度 - 左子树高度

	pair<K, V> _kv; // 存储KV的键值对

	AVLTreeNode(const pair<K,V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_bf(0) // 新增的节点平衡因子为0
	{}
};

AVL树的插入(insert)

AVL树在二叉搜索树的基础上引入了平衡因子,因此AVL树是在二叉搜索树的基础上调整变化而来的,因此AVL树的插入主要分为以下两个步骤:

  1. 按照二叉搜索树的规则将新节点插入到AVL树中
  2. 更新平衡因子,若出现不平衡,则需要进行旋转来调整树的结构直至树平衡

一个节点的平衡因子是否需要更新,取决于插入新节点之后它的左右子树的高度是否发生了变化,即该新插入节点的祖先节点的平衡因子可能需要更新。

  1. 子树高度变了,需要继续沿着祖先节点往上更新
  2. 子树的高度不变,则更新完成
  3. 子树违反平衡规则,则停止更新,按照对应的规则旋转子树
    图解 AVL 树 -【数据结构】_第2张图片

新增节点之后的平衡因子更新规则如下(parent 为新增节点的父节点):

  • 新增节点在 parent 的右边,parent ->_bf ++ ;
  • 新增节点在 parent 的左边,parent ->_bf – ;

此时 parent 的平衡因子有三种情况:0、+1、-1、+2、-2

每更新一次平衡因子,则进行以下判断,决定是否需要继续更新:

  1. 若 parent 的平衡因子等于 1 或者 -1 ,则需要继续往上更新(说明 parent 原本平衡因子为0,新插入节点之后左子树或者右子树高度变化,此时以 parent 为根的树的高度增加,则需要继续往上更新)。
  2. 若 parent 的平衡因子为 0 则停止更新(说明新节点的插入填补了 parent 左右子树中较矮的那一边,导致 parent 的左右子树高度相等平衡了,因此不需要继续往上更新,此时停止更新)。
  3. 若 parent 的平衡因子为 2 或者 -2 ,则已经不平衡了,需要按照相应规则进行旋转处理(以 parent 为根节点的左右子树的高度差已经违反AVL树的规则了)。

在更新过程中平衡因子违反了规则,出现了平衡因子为 -2 或 2 的节点,这时我们需要对违反规则的节点进行旋转处理,AVL树的旋转分为以下4种情况:
图解 AVL 树 -【数据结构】_第3张图片

新增节点为 cur , 其父节点为 parent , cur 节点插入后其平衡因子为0,我们首先更新其父节点的平衡因子,更新完成之后,我们执行以下代码逻辑继续向上更新:

 cur = parent;
 parent = parent->_parent;

AVL树的节点插入:

pair<Node*, bool> insert(const pair<K, V>& kv)
{
	// 空树,插入的第一个节点为根节点
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return make_pair(_root, true);
	}

	// 按照二叉搜索树的规则,找到新插入节点的对应位置
	Node* cur = _root;
	Node* parent = _root;
	while (cur)
	{
		if (cur->_kv.first > kv.first) // 需要插入的值比当前节点小,到当前节点左子树查找
		{
			parent = cur;
			cur = cur->_left; 
		}
		else if (cur->_kv.first < kv.first) // 需要插入的值比当前节点大,到当前节点右子树查找
		{
			parent = cur;
			cur = cur->_right;
		}
		else // 需要插入的值等于当前节点的值,表示树中已有相同的值,则插入失败
		{
			return make_pair(cur, false);
		}
	}

	// 走到这里,就已经找到了新节点插入的对应位置
	cur = new Node(kv);
	Node* newnode = cur;
	// 判断当前节点插入parent的左边还是右边
	if (parent->_kv.first < kv.first)
		parent->_right = cur;
	else
		parent->_left = cur;

	cur->_parent = parent;

	// 插入完成,更新平衡因子
	while (cur != _root) // 最坏情况下可能更新到根节点
	{
		if (parent->_left == cur) //插入在parent左边,平衡因子-1
			parent->_bf--;
		else                      //插入在parent右边,平衡因子+1
			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)
			{
				RotateLR(parent); //左右双旋
			}
			else if (parent->_bf == 2 && cur->_bf == 1)
			{
				RotateL(parent); //左旋
			}
			else if (parent->_bf == 2 && cur->_bf == -1)
			{
				RotateRL(parent); //右左双旋
			}
			break;
		}
		else
		{
			// 插入节点之前AVL树就已经不平衡了
			assert(false);
		}
	}
	return make_pair(newnode, true);
}

AVL树的旋转

以下的旋转情况我们使用具象图来表示,用长方形块代表各种复杂子树结构,长方形块里面的子树已经平衡,因此我们不需要在对其进行处理。

右单旋

图解 AVL 树 -【数据结构】_第4张图片

右单旋的旋转过程:

  1. subL 的右子树 subLR 链接到 parent 的左子树
  2. subL 的右指针链接到 parent
  3. 更新节点的平衡因子
    图解 AVL 树 -【数据结构】_第5张图片

旋转过程中的注意事项:

  • 40 的右子树 subLR 可能存在,也有可能不存在(h == 0 时不存在)
  • 57 可能是根节点 ,也可能是一棵子树; 若是根节点,旋转完成后需要更新根节点;若是子树,则可能是某个节点的左子树,也有可能是右子树

右单旋代码:

void RotateR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	Node* parentParent = parent->_parent;

	// 将subLR链接到parent的左边,这里注意subLR可能为空的情况,需要判断一下
	parent->_left = subLR;
	if (subLR)
	{
		subLR->_parent = parent;
	}

	// 将parent这棵子树链接到subL的右指针
	subL->_right = parent;
	parent->_parent = subL;

	// 若parent为根,则更新新的根节点
	if (parent == _root)
	{
		_root = subL;
		_root->_parent = nullptr;
	}
	else //若parent为一棵子树,则链接与parentParent的关系
	{
		if (parentParent->_right == parent)
			parentParent->_right = subL;
		else
			parentParent->_left = subL;

		subL->_parent = parentParent;
	}

	// 更新旋转完成之后的平衡因子
	subL->_bf = parent->_bf = 0;
}

左单旋

图解 AVL 树 -【数据结构】_第6张图片
左单旋的旋转过程:

  1. subRL 链接到 parent 的右指针上
  2. subR 的左指针指向 parent
  3. 更新节点平衡因子
    图解 AVL 树 -【数据结构】_第7张图片

旋转过程中的注意事项:

  • 57 的左子树 subRL 可能存在,也有可能不存在(h == 0 时不存在)
  • 40 可能是根节点 ,也可能是一棵子树; 若是根节点,旋转完成后需要更新根节点;若是子树,则可能是某个节点的左子树,也有可能是右子树

左单旋代码:

void RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	Node* parentParent = parent->_parent;

	// 让parent的右指针指向subRL,判断一下subRL是否为空
	parent->_right = subRL;
	if (subRL)
		subRL->_parent = parent;

	// subR的左指针链接parent
	subR->_left = parent;
	parent->_parent = subR;

	// parent为根的情况,更新根节点,让根节点指向空
	if (_root == parent)
	{
		_root = subR;
		_root->_parent = nullptr;
	}
	else //若parent为一棵子树,则链接与parentParent的关系
	{
		if (parentParent->_right == parent)
			parentParent->_right = subR;
		else
			parentParent->_left = subR;

		subR->_parent = parentParent;
	}

	// 更新旋转完成之后的平衡因子
	parent->_bf = subR->_bf = 0;
}

左右双旋

在 b 子树上新增节点:
图解 AVL 树 -【数据结构】_第8张图片

在 c 子树上新增节点:
图解 AVL 树 -【数据结构】_第9张图片
当 h = 0 时,57 为新插入节点:
图解 AVL 树 -【数据结构】_第10张图片

左右双旋旋转步骤:

  1. 以40为旋转点进行左单旋
  2. 以85为旋转点进行右单旋
  3. 更新旋转后节点的平衡因子

左右双旋后,平衡因子的更新分为三种情况,参考上图:

  • 当旋转前 subLR 的平衡因子为 -1 ,更新后 subLR ->_bf = 0 , subL ->_bf = 0 , parent ->_bf = 1 .
  • 当旋转前 subLR 的平衡因子为 1 ,更新后 subLR ->_bf = 0 , subL ->_bf = -1 , parent ->_bf = 0 .
  • 当旋转前 subLR 的平衡因子为 0 ,更新后 subLR ->_bf = 0 , subL ->_bf = 0 , parent ->_bf = 0 .

左右双旋代码:

void RotateLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	int bf = subLR->_bf; // 保持旋转前subLR的平衡因子

	RotateL(subL);
	RotateR(parent);

	// 更新三种情况的平衡因子
	if (bf == -1)
	{
		subLR->_bf = 0;
		subL->_bf = 0;
		parent->_bf = 1;
	}
	else if (bf == 1)
	{
		subLR->_bf = 0;
		subL->_bf = -1;
		parent->_bf = 0;
	}
	else if (bf == 0)
	{
		subLR->_bf = 0;
		subL->_bf = 0;
		parent->_bf = 0;
	}
	else
		assert(false); // 旋转前平衡因子已经有问题了
}

右左双旋

在 c 子树上新增节点:
图解 AVL 树 -【数据结构】_第11张图片

在 b 子树上新增节点:
图解 AVL 树 -【数据结构】_第12张图片
当 h = 0 时,57 为新插入节点:
图解 AVL 树 -【数据结构】_第13张图片
右左双旋旋转步骤:

  1. 以85为旋转点进行右单旋
  2. 以40为旋转点进行左单旋
  3. 更新旋转后节点的平衡因子

右左双旋后,平衡因子的更新分为三种情况,参考上图:

  • 当旋转前 subRL 的平衡因子为 -1 ,更新后 subRL ->_bf = 0 , subR ->_bf = 1 , parent ->_bf = 0 .
  • 当旋转前 subRL 的平衡因子为 1 ,更新后 subRL ->_bf = 0 , subR ->_bf = 0 , parent ->_bf = -1 .
  • 当旋转前 subRL 的平衡因子为 0 ,更新后 subRL ->_bf = 0 , subR ->_bf = 0 , parent ->_bf = 0 .

右左双旋代码:

void RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf; // 保持旋转前subRL的平衡因子

	RotateR(subR);
	RotateL(parent);

	// 更新三种情况的平衡因子
	if (bf == -1)
	{
		subRL->_bf = 0;
		subR->_bf = 1;
		parent->_bf = 0;
	}
	else if (bf == 1)
	{
		subRL->_bf =0;
		subR->_bf = 0;
		parent->_bf = -1;
	}
	else if (bf == 0)
	{
		subRL->_bf = 0;
		subR->_bf = 0;
		parent->_bf = 0;
	}
	else
		assert(false);
}

AVL树的删除(erase)

AVL树删除指定节点,我们需要在树中查找到该节点,查找节点的方法等同于二叉搜索树的查找,即删除的节点可以分为三种情况:

  1. 待删除的节点左右子树均为空
  2. 待删除的节点仅有一棵子树
  3. 待删除的节点的左子树和右子树均不为空(需要进行替换删除)

删除的具体方法看这里:二叉搜索树

当我们找到要删除的节点之后,我们需要对删除节点之后树的平衡因子进行调节,平衡因子更新的规则如下:

  • 删除的节点在 parent 的左边,parent->_bf++
  • 删除的节点在 parent 的右边,parent->_bf–

更新之后 parent 的平衡因子可以分为以下三种情况:

  1. 若 parent 的平衡因子等于 1 或者 -1,则可以停止继续往上更新了。(删除之前的AVL树是平衡的,因此删除节点之后平衡因子变为1 或者 -1,表明删除之前 parent 的平衡因子为0,即以 parent 为根的两棵子树的高度一样,删除 parent 的一个子节点之后,以 parent 为根的子树高度没有发生变化,所以不需要继续往上更新平衡因子)。
  2. 若 parent 的平衡因子等于 0 ,则需要继续沿着父节点往上更新平衡因子。(0 是由 1 或者 -1 变化而来的,表明删除前树一边高一点,删除较高的子树中的节点导致 parent 左子树和右子树高度一样了,即以 parent 为根节点的子树的高度发生变化了,所以需要继续往上更新平衡因子)。
  3. 若 parent 的平衡因子等于 2 或者 -2 ,则需要进行旋转处理。(此时以 parent 为根节点的左子树和右子树的高度差已经不符合AVL树的要求了,需要按照规则进行旋转处理)。

删除节点之后的旋转分为以下几种情况:

图解 AVL 树 -【数据结构】_第14张图片

  1. parent 的平衡因子为 -2 ,parent 的左孩子的平衡因子为 -1 时,进行右单旋。
  2. parent 的平衡因子为 -2 ,parent 的左孩子的平衡因子为 1 时 ,进行左右双旋。
  3. parent 的平衡因子为 2 ,parent 的右孩子的平衡因子为 1 时,进行左单旋。
  4. parent 的平衡因子为 2 ,parent 的左孩子的平衡因子为 -1 时 ,进行右左双旋。
  5. parent 的平衡因子为 -2 ,parent 的左孩子的平衡因子为 0 时 ,进行右单旋。
  6. parent 的平衡因子为 2 ,parent 的右孩子的平衡因子为 0 时 ,进行左单旋。

注意:5号情况和6号情况更新完成之后树的高度没有发生变化,因此旋转完成之后不需要继续向上更新平衡因子。平衡因子的更新如下图所示:
图解 AVL 树 -【数据结构】_第15张图片
注意:写代码的时候对照着图去写,这样就不太会出错。

AVL树删除代码:

bool erase(const K& key)
{
	// 空树,直接返回false
	if (_root == nullptr)
		return false;

	Node* parent = _root;
	Node* cur = _root;
	Node* deleteParent = nullptr; // 记录要删除节点的父节点
	Node* deleteCur = nullptr;    // 记录要删除的节点
	while (cur) 
	{
		if (cur->_kv.first < key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (cur->_kv.first > key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else // 找到了要删除的节点
		{
			if (cur->_right == nullptr) // 待删除节点的右子树为空
			{
				if (cur == _root) // 待删除节点为根的情况
				{
					// 让待删除节点的左子树作为新的根
					_root = cur->_left;
					if (_root)
						_root->_parent = nullptr;
					// 删除节点并返回
					delete cur;
					return true;
				}
				else
				{
					// 记录将要删除的节点和其父节点
					deleteParent = parent;
					deleteCur = cur;
				}
			}
			else if (cur->_left == nullptr) // 待删除节点的左子树为空
			{
				if (cur == _root) // 待删除节点为根的情况
				{
					// 让待删除节点的右子树作为新的根
					_root = cur->_right;
					if (_root)
						_root->_parent = nullptr;
					delete cur;
					return true;
				}
				else
				{
					// 记录将要删除的节点和其父节点
					deleteParent = parent;
					deleteCur = cur;
				}
			}
			else // 待删除节点左右子树均不为空
			{
				// 找到待删除节点右子树中的最左节点进行替换删除
				Node* minParent = cur;
				Node* minRight = cur->_right;
				while (minRight->_left)
				{
					minParent = minRight;
					minRight = minRight->_left;
				}

				// 将找到的右子树中最小值中的kv里的数据赋值给待删除节点
				cur->_kv.first = minRight->_kv.first;
				cur->_kv.second = minRight->_kv.second;

				// 记录待删除的节点及其父节点
				deleteParent = minParent;
				deleteCur = minRight;
			}
			// 更新节点祖先的平衡因子
			break;
		}
	}
	
	// 没有找到待删除节点,返回false
	if (cur == nullptr)
		return false;

	// 更新cur和parent
	cur = deleteCur;
	parent = deleteParent;

	// 最坏的情况下可能一直更新到更节点
	while (cur != _root)
	{
		// 待删除的节点为parent的左节点,parent平衡因子+1
		if (cur == parent->_left)
			parent->_bf++;
		// 待删除的节点为parent的右节点,parent平衡因子-1
		else if (cur == parent->_right)
			parent->_bf--;

		//parent平衡因子为0,继续沿着祖先节点向上更新
		if (parent->_bf == 0)
		{
			cur = parent;
			parent = parent->_parent;
		}
		// 删除后保存平衡,停止需要更新
		else if (parent->_bf == 1 || parent->_bf == -1)
		{
			break;
		}
		//以下为图中的6种情况,对照图去完成旋转和平衡因子的更新
		else if (parent->_bf == 2 || parent->_bf == -2)
		{
			// 情况3
			if (parent->_bf == 2 && parent->_right->_bf == 1) 
			{
				Node* tmp = parent->_right; //记录旋转完成之后的根节点,方便后续继续往上更新
				RotateL(parent); 
				parent = tmp; // 更新parent
			}
			// 情况4
			else if (parent->_bf == 2 && parent->_right->_bf == -1)
			{
				Node* tmp = parent->_right->_left;
				RotateRL(parent);
				parent = tmp;
			}
			// 情况6
			else if (parent->_bf == 2 && parent->_right->_bf == 0)
			{
				Node* tmp = parent->_right;
				RotateL(parent);
				parent = tmp;
				//更新旋转后树节点的平衡因子
				parent->_bf = -1;
				parent->_left->_bf = 1;
				// 旋转之后仍热保持平衡,停止更新
				break;
			}
			// 情况1
			else if (parent->_bf == -2 && parent->_left->_bf == -1)
			{
				Node* tmp = parent->_left;
				RotateR(parent);
				parent = tmp;
			}
			// 情况2
			else if (parent->_bf == -2 && parent->_left->_bf == 1)
			{
				Node* tmp = parent->_left->_right;
				RotateLR(parent);
				parent = tmp;
			}
			//情况5
			else if (parent->_bf == -2 && parent->_left->_bf == 0)
			{
				Node* tmp = parent->_left;
				RotateR(parent);
				parent = tmp;
				parent->_bf = 1;
				parent->_right->_bf = -1;
				break;
			}
			else
			{
				// 说明删除节点旋转前平衡因子已经出现错误
				assert(false);
			}
			// 继续沿着父节点往上更新
			cur = parent;
			parent = parent->_parent;
		}
	}

	// 待删除节点的左子树为空
	if (deleteCur->_left == nullptr)
	{
		if (deleteCur == deleteParent->_left)
		{
			deleteParent->_left = deleteCur->_right;
			if (deleteCur->_right)
				deleteCur->_right->_parent = deleteParent;
		}
		else
		{
			deleteParent->_right = deleteCur->_right;
			if (deleteCur->_right )
				deleteCur->_right->_parent = deleteParent;
		}
	}
	// 待删除节点的右子树为空
	else 
	{
		if (deleteCur == deleteParent->_left)
		{
			deleteParent->_left = deleteCur->_left;
			if (deleteCur->_left)
			{
				deleteCur->_left->_parent = deleteParent;
			}
		}
		else
		{
			deleteParent->_right = deleteCur->_left;
			if (deleteCur->_left )
			{
				deleteCur->_left->_parent = deleteParent;
			}
		}
	}
	// 删除指定节点
	delete deleteCur;
	return true;
}

AVL树数据查找(find)

AVL数的查找按照二叉搜索树的规则进行查找,找到了返回节点指针,找不到返回空指针。

Node* find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first < key)
			cur = cur->_right;
		else if (cur->_kv.first > key)
			cur = cur->_left;
		else
			return cur;
	}
	return nullptr;
}

析构函数 和 operator[]

析构函数中调用一个子函数递归分别删除AVL树的左子树和右子树,最后删除根节点并置为空。

void _Destroy(Node* root)
{
	if (root == nullptr)
		return;
	_Destroy(root->_left); // 递归删除左子树
	_Destroy(root->_right);// 递归删除右子树
	delete root; // 删除根节点
}

~AVLTree()
{
	_Destroy(_root);
	_root = nullptr;
}

V& operator[](const K& key)
{
	pair<Node*, bool> ret = insert(make_pair(key, V()));
	return ret.first->_kv.second;
}

AVL数的验证

AVL树是在二叉搜索树的基础上加入了平衡因子的限制,因此要验证AVL树是否实现正确,可以分两步:

验证其是否为二叉搜索树:

使用中序遍历得到的是一个有序的序列,则说明其满足二叉搜索树的性质。

void _InOrder(Node* root)
{
	if (root == nullptr)
		return;

	_InOrder(root->_left);
	cout << root->_kv.first << " -> " << root->_kv.second << endl;
	_InOrder(root->_right);
}

void InOrder()
{
	_InOrder(_root);
}

验证其为平衡树:

  • 每个节点的左子树和右子树的高度差不超过1
  • 检测节点的平衡因子是否符合规则
// 求树的高度
int _Height(Node* root)
{
	if (root == nullptr)
		return 0; // 空树,高度为0

	int leftHeight = _Height(root->_left); // 递归求左子树的高度
	int rightHeight = _Height(root->_right); // 递归求右子树的高度
    
    // 树的高度为左子树或者右子树中较高的那一棵树的高度加 1
	return rightHeight > leftHeight ? rightHeight + 1 : leftHeight + 1; 
}

// 验证平衡因子是否符合规则
bool _isBalance(Node* root)
{
    // 空树符合要求
	if (root == nullptr)
		return true;

    // 算出这棵树中左子树和右子树的高度
	int leftHeight = _Height(root->_left);
	int rightHeight = _Height(root->_right);

	// 检测一下平衡因子是否正确,判断左子树与右子树的高度差是否满足要求
	if (rightHeight - leftHeight != root->_bf)
	{
		cout << "平衡因子异常 :" << root->_kv.first << endl;
		return false;
	}
    
    // 递归检测以每一个节点为根节点的子树
	return abs(rightHeight - leftHeight) < 2 && _isBalance(root->_left) && _isBalance(root->_right);
}

bool IsAVLTree()
{
	return _IsBalance(_root);
}

AVL树的性能

AVL树是一棵绝对平衡的二叉搜索树,其中任意节点的左子树和右子树的高度差绝对值都不超过1,这样就保证了高效率的查找数据。但是若要对AVL树中做一些结构修改的操作性能比较低。例如:插入时为了保持树的平衡需要旋转很多次,删除节点时,有可能要让其一直旋转持续到根节点的位置。因此若需要一种查询高效且有序的数据结构,且数据的个数为静态的(不会改变),可以考虑使用AVL树。需要经常用到修改删除时就不太合适。

你可能感兴趣的:(数据结构,c++,数据结构,算法)