AVL树详解 原理+实现 C++

AVL Tree

AVL树是由G. M. Adelson-Velsky和E. M. Landis 在1962年发明, 是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为1,也被称为高度平衡树。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。



Tips

二叉搜索树(BST)

对于普通的二叉搜索树(BST),增加或删除节点都可能使其退化成链表,时间复杂度就是O(N),AVL在BST的基础上维护了树的平衡,通过树的旋转保持整棵树趋向于一颗满二叉树,使其高度保持在 O(logN)

在这里插入图片描述


BST改进

添加节点12,此时这颗树并不平衡。

在这里插入图片描述
中序遍历依旧是有序的,但保持了树的平衡,本质上其实是为了减少树的高度
在这里插入图片描述



添加导致的失衡

AVL添加节点前都是平衡的,添加节点最坏情况可能会导致其祖先节点都失衡(父节点不会失衡)。

在这里插入图片描述



右旋 LL

红色节点为新增节点,添加后此时子树是失衡的,通过以下步骤恢复平衡。

  1. g.left = p.right
  2. p.right = g
  3. 使p成为新的根

注意旋转后需要维护高度和parent的指向。
在这里插入图片描述
旋转后子树依旧是有序的
在这里插入图片描述

旋转后根节点往上的其他节点并不会失衡,因为添加新节点前AVL永远是平衡的,旋转后子树的高度并不会发生改变,所以添加节点只需要一次的旋转,而删除可能需要 logN 次旋转。



左旋 RR

和LL情况相反

  1. g.right = p.left
  2. p.left = g
  3. 使p成为新的根

注意旋转后需要维护高度和parent的指向。
在这里插入图片描述

也依旧有序。



左旋+右旋 LR


需要进行两次旋转,向对p进行右旋,将n旋转上来,再对g进行左旋,此时n成为新的根节点

注意维护高度和parent指向
在这里插入图片描述

子树恢复平衡,也依旧有序。



右旋+左旋 RL

和LR差不多,也是通过旋转想办法将n成为根节点。

注意维护高度和parent指向
在这里插入图片描述
子树恢复平衡,也依旧有序。



添加后的操作 C++

void afterAdd(Node* cur)
	{
		//从新增节点的父节点往上
		while ((cur = cur->_parent) != nullptr)
		{
			if (cur->isBalance())	//平衡情况下更新高度信息。
				cur->reHight();
			else {					//只需要一次旋转就可以恢复平衡。
				maintain(cur);		//调整子树恢复平衡
				break;
			}

		}
	}


添加代码 C++

bool put(const K& key, const V& value)
	{
		if (!_root) {
			_root = new Node(key, value);
			_size++;
			return true;
		}

		Node* cur = _root;
		while (cur)	
		{
			if (cur->_key < key)	//大往右
			{
				if (cur->_right)	//新节点都为叶子节点
					cur = cur->_right;
				else {
					Node* newNode = new Node(key, value);
					cur->_right = newNode;
					newNode->_parent = cur;

					afterAdd(newNode); //添加后一路往上更新高度和维持平衡
					_size++;
					return true;
				}
			}
			else if (cur->_key > key)	//小往右
			{
				if (cur->_left)
					cur = cur->_left;
				else {
					Node* newNode = new Node(key, value);
					cur->_left = newNode;
					newNode->_parent = cur;

					afterAdd(newNode); //添加后一路往上更新高度和维持平衡
					_size++;
					return true;
				}
			}
			else {		//存在直接更新value
				cur->_value = value;
				return true;
			}

		}//end of while

		return true;
	}


删除导致的失衡


删除节点可能导致其父节点,或往上的祖先节点失衡(只会存在一个失衡)。

我们通过旋转可以让失衡的子树恢复了平衡,但可能导致更高层次的节点发生失衡,最坏情况下,每次旋转可能导致所有祖先节点都失衡,最坏logN次的旋转调整。

在这里插入图片描述



删除代码 C++

	bool remove(const K& key)
	{
		if (empty())	return false;
		//只有根节点
		if (_root->_key == key && size() == 1) {
			_size = 0;
			delete _root, _root = nullptr;
			return true;
		}

		Node* cur = _root;
		bool find = false;
		while (cur)
		{
			if (cur->_key < key)
				cur = cur->_right;
			else if (cur->_key > key)
				cur = cur->_left;
			else {
				find = true;
				break;
			}

		}//end of while

		//没找到直接返回
		if (!find) {
			return false;
		}

		if (cur->_left && cur->_right)	//度为2
		{
			Node* prev = cur->_left;	//找到前驱节点
			while (prev && prev->_right)
				prev = prev->_right;

			cur->_key = prev->_key, cur->_value = prev->_value;
			cur = prev;
		}

		if (cur->_left || cur->_right)	//度为1情况
		{
			Node* parent = cur->_parent;
			Node* child = cur->_left ? cur->_left : cur->_right;

			parent->_left == cur ? parent->_left = child : parent->_right = child;
			child->_parent = parent;

			afterRemove(cur);
		}
		else {		//度为0的情况

			Node* parent = cur->_parent;
			parent->_left == cur ? parent->_left = nullptr : parent->_right = nullptr;

			afterRemove(cur);
		}

		_size--;
		delete cur;
		return true;
	}



完整代码 C++

#include <iostream>
#include <cmath>
#include <queue>
#include <cassert>
using namespace std;

template<class K, class V>
class AVL {

public:
	struct Node {
		Node(const K& key, const V& value) :
			_key(key), _value(value), _parent(nullptr), _left(nullptr), _right(nullptr) {}

		Node* tallerChild()
		{
			size_t left = this->_left->hight();
			size_t right = this->_right->hight();

			return left < right
				? this->_right
				: this->_left;
		}

		size_t hight()
		{
			return !this ? 0 : this->_hight;
		}

		bool isBalance()
		{
			int left = this->_left->hight();
			int right = this->_right->hight();

			return fabs(left - right) < 2 ? true : false;
		}

		void reHight()
		{
			int left = this->_left->hight();
			int right = this->_right->hight();
			this->_hight = left > right ? left + 1 : right + 1;
		}

		K _key;
		V _value;
		Node* _parent;
		Node* _left;
		Node* _right;
		size_t _hight = 1;
	};

public:

	bool put(const K& key, const V& value)
	{
		if (!_root) {
			_root = new Node(key, value);
			_size++;
			return true;
		}

		Node* cur = _root;
		while (cur)	
		{
			if (cur->_key < key)	//大往右
			{
				if (cur->_right)	//新节点都为叶子节点
					cur = cur->_right;
				else {
					Node* newNode = new Node(key, value);
					cur->_right = newNode;
					newNode->_parent = cur;

					afterAdd(newNode); //添加后一路往上更新高度和维持平衡
					_size++;
					return true;
				}
			}
			else if (cur->_key > key)	//小往右
			{
				if (cur->_left)
					cur = cur->_left;
				else {
					Node* newNode = new Node(key, value);
					cur->_left = newNode;
					newNode->_parent = cur;

					afterAdd(newNode); //添加后一路往上更新高度和维持平衡
					_size++;
					return true;
				}
			}
			else {		//存在直接更新value
				cur->_value = value;
				return true;
			}

		}//end of while

		return true;
	}


	bool remove(const K& key)
	{
		if (empty())	return false;
		//只有根节点
		if (_root->_key == key && size() == 1) {
			_size = 0;
			delete _root, _root = nullptr;
			return true;
		}

		Node* cur = _root;
		bool find = false;
		while (cur)
		{
			if (cur->_key < key)
				cur = cur->_right;
			else if (cur->_key > key)
				cur = cur->_left;
			else {
				find = true;
				break;
			}

		}//end of while

		//没找到直接返回
		if (!find) {
			return false;
		}

		if (cur->_left && cur->_right)	//度为2
		{
			Node* prev = cur->_left;	//找到前驱节点
			while (prev && prev->_right)
				prev = prev->_right;

			cur->_key = prev->_key, cur->_value = prev->_value;
			cur = prev;
		}

		if (cur->_left || cur->_right)	//度为1情况
		{
			Node* parent = cur->_parent;
			Node* child = cur->_left ? cur->_left : cur->_right;

			parent->_left == cur ? parent->_left = child : parent->_right = child;
			child->_parent = parent;

			afterRemove(cur);
		}
		else {		//度为0的情况

			Node* parent = cur->_parent;
			parent->_left == cur ? parent->_left = nullptr : parent->_right = nullptr;

			afterRemove(cur);
		}

		_size--;
		delete cur;
		return true;
	}

	void afterRemove(Node* cur) {

		while ((cur = cur->_parent) != nullptr)
		{
			if (cur->isBalance())
				cur->reHight();
			else {
				maintain(cur);
			}

		}
	}
	void afterAdd(Node* cur)
	{
		while ((cur = cur->_parent) != nullptr)
		{
			if (cur->isBalance())
				cur->reHight();
			else {
				maintain(cur);
				break;
			}

		}
	}
	bool contains(const K& key)
	{
		if (empty())	return false;

		Node* cur = _root;
		while (cur)
		{
			if (cur->_key > key)
				cur = cur->_left;
			else if (cur->_key < key)
				cur = cur->_right;
			else
				return true;
		}

		return false;
	}
	void maintain(Node* grand)
	{
		if (!grand)	return;

		Node* parent = grand->tallerChild();
		Node* node = parent ? parent->tallerChild() : nullptr;

		if (parent && node && parent == grand->_left)
		{
			if (node == parent->_left)	//LL
				right_rotate(grand);
			else {						//LR
				left_rotate(parent);
				right_rotate(grand);
			}
		}
		else if (parent && node && parent == grand->_right)
		{
			if (node == parent->_right)	//RR
				left_rotate(grand);
			else {						//RL
				right_rotate(parent);
				left_rotate(grand);
			}
		}

	}
	void left_rotate(Node* cur)
	{
		Node* parent = cur->_parent;
		bool isRoot = (cur == _root) ? true : false;

		Node* R = cur->_right;
		cur->_parent = R;
		cur->_right = R->_left;

		if (cur->_right)
			cur->_right->_parent = cur;

		R->_left = cur;
		cur->reHight();

		R->reHight();

		if (isRoot) {
			_root = R;
			_root->_parent = nullptr;
		}
		else {
			parent->_left == cur ? parent->_left = R : parent->_right = R;
			R->_parent = parent;
		}

	}
	void right_rotate(Node* cur)
	{
		Node* parent = cur->_parent;
		bool isRoot = (cur == _root) ? true : false;

		Node* L = cur->_left;
		cur->_parent = L;
		cur->_left = L->_right;

		if (cur->_left)
			cur->_left->_parent = cur;


		L->_right = cur;
		cur->reHight();

		L->reHight();

		if (isRoot) {
			_root = L;
			_root->_parent = nullptr;
		}
		else {
			parent->_left == cur ? parent->_left = L : parent->_right = L;
			L->_parent = parent;
		}
	}



	size_t size()
	{
		return _size;
	}

	bool empty()
	{
		return _root == nullptr;
	}

	//private:
	Node* _root = nullptr;
	size_t _size = 0;
};
  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

necesse

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值