[数据结构]AVL树


上文对map/multimap/set/multiset进行了简单的介绍,这几个容器有个共同点是:
其底层都是按照二叉搜索树来实现的,但是二叉搜索树有其自身的缺陷,假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成O(N),因此map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现


一、AVL树

1.1 AVL树的概念

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

AVL树也叫作高度平衡二叉树(红黑树是控制颜色)

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

  1. 它的左右子树都是AVL树
  2. 左右子树高度之差(简称平衡因子:右子树 - 左子树的高度)的绝对值不超过1(-1 / 0 / 1)
    在这里插入图片描述

AVL树不一定需要平衡因子,使用平衡因子是一种控制实现的方式

如果一棵二叉搜索树是高度平衡的,他就是AVL树。如果他有N个节点,其高度可以保持在O(logN),搜索时间复杂度为O(logN)

1.2 AVL树基本框架

// AVLTree节点的定义
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)
};

// AVLTree的定义
template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
private:
	AVLTree()
		:_root(nullptr)
	{}

private:
	Node* _root;
};

1.3 AVL树的插入

在这里插入图片描述

在AVL树中,我们采用平衡因子来实现,如果插入在父节点的右边,parent的平衡因子++,如果在左边则–

那么cur的增加会影响parent,那会影响祖先吗?——会,只可能影响祖先

cur新增节点只会影响cur祖先节点的平衡因子

在这里插入图片描述

1. parent -> left == cur , parent -> bf--
2. parent -> right == cur , parent -> bf++
3. 更新后,如果parent -> bf == 0 则更新结束,说明更新前parent->bf是1或是-1,现在变为了0则表明原先是不平衡的状态,现在是平衡的,parent所在的子树高度不变
4. 更新后,如果parent -> bf == 1 / -1 则继续往上更新,说明更新前parent->bf是0,现在变为了1 / -1,则说明我有一边的子树变高了,parent所在的子树高度也就变了
5. 更新后,如果parent -> bf == 2 / -2,则说明parent子树已经不平衡了,需要进行旋转处理

演示三种不同的情况:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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);
		}

		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else // 相等
			{
				return false;
			}
		}

		cur = new Node(kv);
		if (parent->_kv < kv.first)
		{
			parent->_right = cur;
			cur->_parent = parent; // 这里是三叉链
		}
		else // 这里不可能相等,因为相等上面就走false了
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
	
		// 前面是插入,下面控制平衡
		/* 一个节点的平衡因子受到子树的影响,新插入节点的平衡因子一定为0(子树为空树)
		如果新节点插入在父亲的右边,则平衡因子++
		如果新节点插入在父亲的左边,则平衡因子--
		控制平衡有两步
		1. 更新平衡因子—更新新增节点到根节点的祖先路径
		2. 如果出现异常的平衡因子,就需要旋转平衡处理
		*/
	}

private:
	Node* _root;
};

1.4 AVL树的旋转

旋转的意义:

  1. 保持搜索树规则
  2. 控制平衡

通俗来说也就是使得这棵树平衡了,这棵树的高度降了1

while (parent)
{
	if (cur == parent->_left) // 若插入在左侧,则parent的平衡因子--
	{
		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)
		{
			RotateLR(parent);
		}
		else if (parent->_bf == 2 && cur->_bf == -1)
		{
			RotateRL(parent);
		}
		else
		{
			assert(false);
		}

		break;
	}
	else
	{
		// 出现问题
		assert(false);
	}
}

1.4.1 右单旋

下图中a、b、c是高度为h的平衡搜索树
在这里插入图片描述
用抽象图可以代表各种右旋的情况,旋转的操作:

  1. b变为60的左边
  2. 60变成30的右边
    在这里插入图片描述

上图实际表示出来就是下面这样子
在这里插入图片描述

因此就可以实现右旋代码,如下图所示可以用代码实现其连接关系
在这里插入图片描述

void RotateL(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;

	parent->_left = subLR;
	if (subLR) // subLR可能会是空
		subLR->_parent = parent; // 因为是三叉链,还需要去更新parent

	Node* parentParent = parent->_parent; // 先存储祖宗

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

	// 没有祖先
	if (parent == _root)
	{
		_root = subL;
		_root->_parent = nullptr;
	}
	else // 还有祖先,将祖先的左右指针指向刚刚更新后子树的新的root
	{
		if (parentParent->_left == parent)
		{
			parentParent->_left = subL;
		}
		else
		{
			parentParent->_right = subL;
		}
		subL->_parent = parent;
	}

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

1.4.2 左单旋

右侧高了,因此我们使用左单旋
在这里插入图片描述

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

	parent->_right = subRL;
	if (subRL) // subRL可能会是空
		subRL->_parent = parent;

	Node* parentParent = parent->_parent;

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

	if (_root == parent)
	{
		_root = subR;
		subR->_parent = nullptr;
	}
	else
	{
		// 如果有祖先
		if (parentParent->_left == parent)
		{
			parentParent->_left = subR;
		}
		else
		{
			parentParent->_right = subR;
		}
		subR->_parent = parentParent;
	}

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

1.4.3 左右双旋

新节点插入较高左子树的右侧:左右 -> 先左单旋在进行右单旋
在这里插入图片描述
在这里插入图片描述

1.4.4 右左双旋

void RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf;

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

	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 if (bf == 0)
	{
		parent->_bf = 0;
		subR->_bf = 0;
		subRL->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

双旋旋转完后要记得去更新平衡因子

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
AVL树是一种自平衡二叉搜索,它的平衡因子(左子高度减去右子高度)在任何时候都是-1、0或1。当插入或删除节点后,如果AVL树失去平衡,则需要通过旋转来重新平衡它。 在Python中实现AVL树,可以使用节点类和AVL树类。节点类包括节点值、左子、右子、高度和平衡因子等属性。AVL树类包括根节点、插入节点、删除节点、旋转方法和平衡方法等方法。 下面是一个简单的Python实现AVL树的代码示例: ```python class Node: def __init__(self, val): self.val = val self.left = None self.right = None self.height = 1 self.balance = 0 class AVLTree: def __init__(self): self.root = None def insert(self, val): self.root = self._insert(self.root, val) def _insert(self, node, val): if not node: return Node(val) if val < node.val: node.left = self._insert(node.left, val) else: node.right = self._insert(node.right, val) node.height = 1 + max(self.get_height(node.left), self.get_height(node.right)) node.balance = self.get_balance(node) if node.balance > 1 and val < node.left.val: return self.right_rotate(node) if node.balance < -1 and val > node.right.val: return self.left_rotate(node) if node.balance > 1 and val > node.left.val: node.left = self.left_rotate(node.left) return self.right_rotate(node) if node.balance < -1 and val < node.right.val: node.right = self.right_rotate(node.right) return self.left_rotate(node) return node def delete(self, val): self.root = self._delete(self.root, val) def _delete(self, node, val): if not node: return node if val < node.val: node.left = self._delete(node.left, val) elif val > node.val: node.right = self._delete(node.right, val) else: if not node.left or not node.right: temp = node.left if node.left else node.right if not temp: node = None else: node = temp else: temp = self.get_min(node.right) node.val = temp.val node.right = self._delete(node.right, temp.val) if not node: return node node.height = 1 + max(self.get_height(node.left), self.get_height(node.right)) node.balance = self.get_balance(node) if node.balance > 1 and self.get_balance(node.left) >= 0: return self.right_rotate(node) if node.balance < -1 and self.get_balance(node.right) <= 0: return self.left_rotate(node) if node.balance > 1 and self.get_balance(node.left) < 0: node.left = self.left_rotate(node.left) return self.right_rotate(node) if node.balance < -1 and self.get_balance(node.right) > 0: node.right = self.right_rotate(node.right) return self.left_rotate(node) return node def right_rotate(self, node): left_child = node.left node.left = left_child.right left_child.right = node node.height = 1 + max(self.get_height(node.left), self.get_height(node.right)) left_child.height = 1 + max(self.get_height(left_child.left), self.get_height(left_child.right)) node.balance = self.get_balance(node) left_child.balance = self.get_balance(left_child) return left_child def left_rotate(self, node): right_child = node.right node.right = right_child.left right_child.left = node node.height = 1 + max(self.get_height(node.left), self.get_height(node.right)) right_child.height = 1 + max(self.get_height(right_child.left), self.get_height(right_child.right)) node.balance = self.get_balance(node) right_child.balance = self.get_balance(right_child) return right_child def get_height(self, node): if not node: return 0 return node.height def get_balance(self, node): if not node: return 0 return self.get_height(node.left) - self.get_height(node.right) def get_min(self, node): while node.left: node = node.left return node ``` 这个实现包括插入、删除、旋转和平衡等基本操作。你可以按需调用这些方法,来实现你的具体需求。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值