AVL树
- 引言
- AVL树的模拟实现
- AVL树的底层结构
- insert的实现
- 实现思路
- 旋转逻辑
- insert的完整代码
 
- insert的验证
 
- 源码
引言
前面 二叉搜索树的时间复杂度那里提过一嘴 AVL树 和 红黑树. 因为二叉搜索树的时间复杂度是 O(高度次), 最坏情况下 -- 退化到单支链, 是 O(N); AVL 和 红黑树 可以避免这种 极端情况, 时间复杂度是 O(log N)
🗨️AVL树是如何做到避免 二叉搜索树的极端情况的呢?
-  利用了 三叉链 && 平衡因子
 三叉链 和 平衡因子是互相成就的,.
至于它们到底是什么, 有什么妙用, 下面会有详细的解释
AVL树的模拟实现
AVL树的底层结构
AVL树具有以下的特点:
- 左右子树都是AVL树
- 每棵子树的 高度差(平衡因子)的绝对值不超过 1(-1, 0 1)
- 一般的平衡因子是 : 右子树的高度 - 左子树的高度
  
🗨️为啥不让每棵子树的 平衡因子的绝对值为 0 ?
AVL树的底层结构:
 
- AVLTreeNode类
template<class K, class V>
struct AVLTreeNode
{
public:
	AVLTreeNode(const pair<K,V>& kv)
		:_kv(kv)
	{}
public:
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left = nullptr;
	AVLTreeNode<K, V>* _right = nullptr;
	AVLTreeNode<K, V>* _parent = nullptr;
	int _bf = 0;
};
- AVLTree类
template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	AVLTree()
		:_root(nullptr)
	{}
private:
	// 根节点
	Node* _root = nullptr;
	// 记录旋转次数
	int RotateCount = 0;
};
insert的实现
实现思路
二叉树的插入逻辑 + 更新平衡因子
bool Insert(const pair<K, V>& kv)
{
	//二叉搜索树的插入逻辑
	
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return true;
	}
	Node* parent = _root;
	Node* cur = _root;
	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 (cur->_kv.first > parent->_kv.first)
	{
		parent->_right = cur;
		cur->_parent = parent;
	}
	else
	{
		parent->_left = cur;
		cur->_parent = parent;
	}
	parent = cur->_parent;
	// 更新平衡因子
	// ... ...
}
那重点就是 如何更新平衡因子 :
首先, 先明确; 新插入的节点不影响自己的平衡因子, 只会影响父亲节点到 root这一段的节点的平衡因子
 
其次, 要讨论插入节点的位置
- 新插在左 — — parent的平衡因子减减
- 新插在右 — — parent的平衡因子加加
最后, 也要讨论插入后的parent的平衡因子
- 更新后的parent的平衡因子为 1 或 -1 — — 继续往上更新
- 更新后的parent的平衡因子为 0 — — 停止更新
- 更新后的parent的平衡因子为 2 或 -2 — — 需要旋转 — — 旋转后停止更新

这里有几个问题:
 🗨️为什么更新后的parent的平衡因子等于 1 或 -1, 要继续往上更新?
-  更新后的parent等于 0, 说明新插入的节点并不会影响parent的高度差
 如果更新后的parent等于 1 或 -1, 说明新插入的节点影响到了parent的高度差, 有可能也会影响到parent上面的高度差 ⇒ 所以, 我们要继续向上更新, 直至parent的平衡因子为 0 或 更新到了root
🗨️ 为什么更新后的parent等于空, 也要停止更新呢?
-  首先, 只有root 的父亲节点才是 空
 其次,我们最差的更新情况是更新到root节点
🗨️ 为什么旋转后就停止更新了呢?
-  结合后面的 旋转逻辑来进行讲解
insert的主体结构👇👇👇
bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return true;
	}
	Node* parent = _root;
	Node* cur = _root;
	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 (cur->_kv.first > parent->_kv.first)
	{
		parent->_right = cur;
		cur->_parent = parent;
	}
	else
	{
		parent->_left = cur;
		cur->_parent = parent;
	}
	parent = cur->_parent;
	// 更新平衡因子
	while (parent) // 最差更新到root节点
	{
		// 1. 先更新一下parent
		// 新插在右
		if (parent->_right == cur)
		{
			parent->_bf++;
		}
		else // 新插在左
		{
			parent->_bf--;
		}
		// 2. 检查更新后的parent是否影响平衡
		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)
		{
			// 旋转的逻辑
			// ...
			// ...
			
			// 旋转后停止更新平衡因子
			break;
		}
	}
	return true;
}
旋转逻辑
原本的树形结构符合AVL树的特点, 如果插入一个新节点 造成不平衡了, 即parent的平衡因子 等于 2 或 -2了, 这时候就要进行旋转. 根据 插入节点的位置, 一共有四种情况:
- 新节点插入到较高左子树的左侧 — — 左左— —对parent进行右旋
  
核心操作: 让cur的右节点充当parent的左节点, 然后让parent整体充当cur的右节点
- 原理 :
 左边偏高 — —想办法让左边的高度降下来— —把cur的左右高度差降低
 每次旋转, 也要维持搜索树的特性— —中序遍历是有序的— — cur的右节点 (b) 充当parent的左节点是合理的, parent充当 cur的右也是合理的
 ⇒ 这样以来:cur的左右子树的高度是相等的, 都为 h+1;
void RotateL(Node* parent)
{
	// 每次旋转都++
	++RotateCount;
	// 提前保存grandfather节点, 保证后面的链接是正确的
	Node* cur = parent->_right;
	Node* grandfather = parent->_parent;
	Node* curleft = cur->_left;
	// 旋转核心
	parent->_right = curleft;
	cur->_left = parent;
	// 更新父亲
	// 1. parent && curleft
	if (curleft)
	{
		curleft->_parent = parent;
	}
	parent->_parent = cur;
	// 2.更新cur
	// cur要充当起parent的责任, 向上进行连接
	if (grandfather == nullptr)
	{
		cur->_parent = nullptr;
		_root = cur;
	}
	else
	{
		// 判读cur应该位于grandfather节点的哪一侧
		// 1. 向下进行链接
		if (grandfather->_left == parent)
		{
			grandfather->_left = cur;
		}
		else
		{
			grandfather->_right = cur;
		}
		
		// 2. 向上进行链接
		cur->_parent = grandfather;
	}
	// 更新平衡因子
	cur->_bf = parent->_bf = 0;
}
- 新节点插入到较高右子树的右侧 — — 右右
  
void RotateR(Node* parent)
{
	++RotateCount;
	Node* cur = parent->_left;
	Node* grandfather = parent->_parent;
	Node* curright = cur->_right;
	// 旋转核心
	parent->_left = curright;
	cur->_right = parent;
	// 更新链接关系
	// 1. parent && curright
	if (curright)
	{
		curright->_parent = parent;
	}
	parent->_parent = cur;
	// 2.更新cur
	if (grandfather == nullptr)
	{
		cur->_parent = nullptr;
		_root = cur;
	}
	else
	{
		if (grandfather->_left == parent)
		{
			grandfather->_left = cur;
		}
		else
		{
			grandfather->_right = cur;
		}
		cur->_parent = grandfather;
	}
	cur->_bf = parent->_bf = 0;
}
- 新节点插入到较高左子树的右侧 — — 左右— —先对cur左旋, 再对parent右旋
  
- 其实 左右双旋的本质是:
 把curright的左子树(b)充当cur的右子树,
 把curright的右子树(c)充当parent的左子树,
 然后curright充当根节点, cur 和 parent分别充当左右子树
⇒ 那么 更新平衡因子, 也是要看curright的左右子树(b 和 c的高度情况) :
- h = 0 ⇒ curright 为新增
  
cur->bf = 0;
curright->_bf = 0;
parent->_bf = 0;
- h > 0 ⇒ 新增在 b 或 c
 (1)新增在 b
  
cur->bf = 0;
curright->_bf = 0;
parent->_bf = 1;
(2)新增在 c
 
cur->bf = -1;
curright->_bf = 0;
parent->_bf = 0;
左右双旋的完整代码
void RotateLR(Node* parent)
{
	// 提前保存一份, 后面的左右旋转中会发生变化的
	Node* cur = parent->_left;
	Node* curright = cur->_right;
	RotateL(parent->_left);
	RotateR(parent);
	// 更新平衡因子
	if (curright->_bf == 0)
	{
		cur->_bf = 0;
		parent->_bf = 0;
		curright->_bf = 0;
	}
	else if (curright->_bf == 1)
	{
		cur->_bf = -1;
		parent->_bf = 0;
		curright->_bf = 0;
	}
	else if (curright->_bf == -1)
	{
		cur->_bf = 0;
		parent->_bf = 1;
		curright->_bf = 0;
	}
}
- 新节点插入到较高右子树的左侧 — — 右左— —先对cur右旋, 再对parent左旋
  
- 其实, 右左双旋的本质是:
 把curleft的左子树(b) 充当 parent的右子树
 把curleft的右子树(c) 充当cur的左子树
 让curleft来根节点, 让parent 和 cur分别充当curleft的左右子树
⇒ 那么 更新平衡因子, 也是要看curleft的左右子树(b 和 c的高度情况) :
- h = 0 ⇒ curleft为新增
  
cur->bf = 0;
curleft->_bf = 0;
parent->_bf = 1;
- h > 0 ⇒ 新增在b / c
 (1) 新增在 b
  
cur->bf = 1;
curleft->_bf = 0;
parent->_bf = 0;
(2) 新增在 c
 
cur->bf = 0;
curleft->_bf = 0;
parent->_bf = -1;
右左双旋的完整代码
void RotateRL(Node* parent)
{
	Node* cur = parent->_right;
	Node* curleft = cur->_left;
	RotateR(parent->_right);
	RotateL(parent);
	// 更新平衡因子
	if (curleft->_bf == 0)
	{
		cur->_bf = 0;
		parent->_bf = 0;
		curleft->_bf = 0;
	}
	else if (curleft->_bf == 1)
	{
		cur->_bf = 0;
		parent->_bf = -1;
		curleft->_bf = 0;
	}
	else if (curleft->_bf == -1)
	{
		cur->_bf = 1;
		parent->_bf = 0;
		curleft->_bf = 0;
	}
}
insert的完整代码
bool Insert(const pair<K, V>& kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return true;
	}
	Node* parent = _root;
	Node* cur = _root;
	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 (cur->_kv.first > parent->_kv.first)
	{
		parent->_right = cur;
		cur->_parent = parent;
	}
	else
	{
		parent->_left = cur;
		cur->_parent = parent;
	}
	parent = cur->_parent;
	// 更新平衡因子
	while (parent)
	{
		// 1. 先更新一下parent
		// 新插在右
		if (parent->_right == cur)
		{
			parent->_bf++;
		}
		else // 新插在左
		{
			parent->_bf--;
		}
		// 2. 检查更新后的parent是否影响平衡
		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)
			{
				RotateL(parent);
			}
			else if (parent->_bf == 2 && cur->_bf == -1)
			{
				RotateRL(parent);
			}
			else if (parent->_bf == -2 && cur->_bf == -1)
			{
				RotateR(parent);
			}
			else if (parent->_bf == -2 && cur->_bf == 1)
			{
				RotateLR(parent);
			}
			else
			{
				assert("平衡因子更新错误!");
			}
			
			// 旋转结束, 就停止更新平衡因子
			break;
		}
	}
	return true;
}
insert的验证
- 检查每棵子树的高度差的绝对值小于 1
- 检查平衡因子是否等于左右子树的高度差 ( 我们算的平衡因子有可能不对)
检查程序
int Height()
{
	return Height(_root);
}
int Height(Node* root)
{
	if (root == nullptr)
		return 0;
	int left = Height(root->_left);
	int right = Height(root->_right);
	return left > right ? left + 1 : right + 1;
}
bool Isbalance()
{
	return Isbalance(_root);
}
bool Isbalance(Node* root)
{
	if (root == nullptr)
		return true;
	int lheight = Height(root->_left);
	int rheight = Height(root->_right);
	
	if (root->_bf != rheight - lheight || abs(rheight - lheight) > 1)
	{
		cout << "平衡因子异常:" << root->_kv.first << "->" << root->_bf << endl;
		return false;
	}
	
	// 继续检查下一个支树
	return Isbalance(root->_left) && Isbalance(root->_right);
}
GetRotateCount
int GetRoateCount()
{
	return RotateCount;
}
验证代码:
void avl_test()
{
	const int N = 100000;
	vector<int> v;
	v.reserve(N);
	// srand((unsigned int)time(nullptr));
	for (size_t i = 0; i < N; i++)
	{
		// int ret = rand();
		// v.push_back(ret);
		v.push_back(i);
	}
	muyu::AVLTree<int, int> avl;
	for (auto e : v)
	{
		avl.Insert(make_pair(e, e));
	}
	
	cout << "AVL树是否达标-> " << avl.Isbalance() << endl;
	cout << "AVL树的高度-> " << avl.Height() << endl;
	cout << "AVL树旋转的次数-> " << avl.GetRoateCount() << endl;
}
int main()
{
	avl_test();
	
	return 0;
}
运行结果:
AVL树是否达标-> 1
AVL树的高度-> 17
AVL树旋转的次数-> 99983
源码
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace muyu
{
	template<class K, class V>
	struct AVLTreeNode
	{
	public:
		AVLTreeNode(const pair<K,V>& kv)
			:_kv(kv)
		{}
	public:
		pair<K, V> _kv;
		AVLTreeNode<K, V>* _left = nullptr;
		AVLTreeNode<K, V>* _right = nullptr;
		AVLTreeNode<K, V>* _parent = nullptr;
		int _bf = 0;
	};
	template<class K, class V>
	class AVLTree
	{
		typedef AVLTreeNode<K, V> Node;
	public:
		AVLTree()
			:_root(nullptr)
		{}
		void RotateL(Node* parent)
		{
			// 每次旋转都++
			++RotateCount;
			// 提前保存grandfather节点, 保证后面的链接是正确的
			Node* cur = parent->_right;
			Node* grandfather = parent->_parent;
			Node* curleft = cur->_left;
			// 旋转核心
			parent->_right = curleft;
			cur->_left = parent;
			// 更新父亲
			// 1. parent && curleft
			if (curleft)
			{
				curleft->_parent = parent;
			}
			parent->_parent = cur;
			// 2.更新cur
			// cur要充当起parent的责任, 向上进行连接
			if (grandfather == nullptr)
			{
				cur->_parent = nullptr;
				_root = cur;
			}
			else
			{
				// 判读cur应该位于grandfather节点的哪一侧
				// 1. 向下进行链接
				if (grandfather->_left == parent)
				{
					grandfather->_left = cur;
				}
				else
				{
					grandfather->_right = cur;
				}
				
				// 2. 向上进行链接
				cur->_parent = grandfather;
			}
			// 更新平衡因子
			cur->_bf = parent->_bf = 0;
		}
		void RotateR(Node* parent)
		{
			++RotateCount;
			Node* cur = parent->_left;
			Node* grandfather = parent->_parent;
			Node* curright = cur->_right;
			// 旋转核心
			parent->_left = curright;
			cur->_right = parent;
			// 更新链接关系
			// 1. parent && curright
			if (curright)
			{
				curright->_parent = parent;
			}
			parent->_parent = cur;
			// 2.更新cur
			if (grandfather == nullptr)
			{
				cur->_parent = nullptr;
				_root = cur;
			}
			else
			{
				if (grandfather->_left == parent)
				{
					grandfather->_left = cur;
				}
				else
				{
					grandfather->_right = cur;
				}
				cur->_parent = grandfather;
			}
			cur->_bf = parent->_bf = 0;
		}
		void RotateRL(Node* parent)
		{
			Node* cur = parent->_right;
			Node* curleft = cur->_left;
			RotateR(parent->_right);
			RotateL(parent);
			// 更新平衡因子
			if (curleft->_bf == 0)
			{
				cur->_bf = 0;
				parent->_bf = 0;
				curleft->_bf = 0;
			}
			else if (curleft->_bf == 1)
			{
				cur->_bf = 0;
				parent->_bf = -1;
				curleft->_bf = 0;
			}
			else if (curleft->_bf == -1)
			{
				cur->_bf = 1;
				parent->_bf = 0;
				curleft->_bf = 0;
			}
		}
		void RotateLR(Node* parent)
		{
			Node* cur = parent->_left;
			Node* curright = cur->_right;
			RotateL(parent->_left);
			RotateR(parent);
			// 更新平衡因子
			if (curright->_bf == 0)
			{
				cur->_bf = 0;
				parent->_bf = 0;
				curright->_bf = 0;
			}
			else if (curright->_bf == 1)
			{
				cur->_bf = -1;
				parent->_bf = 0;
				curright->_bf = 0;
			}
			else if (curright->_bf == -1)
			{
				cur->_bf = 0;
				parent->_bf = 1;
				curright->_bf = 0;
			}
		}
		bool Insert(const pair<K, V>& kv)
		{
			if (_root == nullptr)
			{
				_root = new Node(kv);
				return true;
			}
			Node* parent = _root;
			Node* cur = _root;
			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 (cur->_kv.first > parent->_kv.first)
			{
				parent->_right = cur;
				cur->_parent = parent;
			}
			else
			{
				parent->_left = cur;
				cur->_parent = parent;
			}
			parent = cur->_parent;
			// 更新平衡因子
			while (parent)
			{
				// 1. 先更新一下parent
				// 新插在右
				if (parent->_right == cur)
				{
					parent->_bf++;
				}
				else // 新插在左
				{
					parent->_bf--;
				}
				// 2. 检查更新后的parent是否影响平衡
				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)
					{
						RotateL(parent);
					}
					else if (parent->_bf == 2 && cur->_bf == -1)
					{
						RotateRL(parent);
					}
					else if (parent->_bf == -2 && cur->_bf == -1)
					{
						RotateR(parent);
					}
					else if (parent->_bf == -2 && cur->_bf == 1)
					{
						RotateLR(parent);
					}
					else
					{
						assert("平衡因子更新错误!");
					}
					break;
				}
			}
			return true;
		}
		int Height()
		{
			return Height(_root);
		}
		int Height(Node* root)
		{
			if (root == nullptr)
				return 0;
			int left = Height(root->_left);
			int right = Height(root->_right);
			return left > right ? left + 1 : right + 1;
		}
		bool Isbalance()
		{
			return Isbalance(_root);
		}
		bool Isbalance(Node* root)
		{
			if (root == nullptr)
				return true;
			int lheight = Height(root->_left);
			int rheight = Height(root->_right);
			if (root->_bf != rheight - lheight || abs(rheight - lheight) > 1)
			{
				cout << "平衡因子异常:" << root->_kv.first << "->" << root->_bf << endl;
				return false;
			}
			return Isbalance(root->_left) && Isbalance(root->_right);
		}
		int GetRoateCount()
		{
			return RotateCount;
		}
	private:
		Node* _root = nullptr;
		int RotateCount = 0;
	};
}
富家不用买良田,书中自有千钟粟。
安居不用架高堂,书中自有黄金屋。
出门无车毋须恨,书中有马多如簇。
娶妻无媒毋须恨,书中有女颜如玉。
男儿欲遂平生志,勤向窗前读六经。
— — 赵恒《劝学诗》




















