C++笔记归纳14:AVL树

AVL树

目录

AVL树

一、AVL树的由来

二、AVL树的概念

三、AVL树的优势

[四、平衡因子(balance factor)](#四、平衡因子(balance factor))

五、AVL树的实现

5.1.AVL树的结构

5.2.AVL树的插入

5.3.平衡因子的更新

5.4.旋转的原则

5.5.AVL树实现总代码

5.6.测试代码


一、AVL树的由来

在前苏联科学家G.M.A delson-V elsky和E.M.Landis的论文

《An algorithm for the organization of information》中发表

二、AVL树的概念

**AVL树:**最先发明的自平衡二叉搜索树

(注:通过控制高度差去控制平衡,又称高度平衡二叉搜索树)

**性质:**左右子树都是AV树,左右子树的高度差的绝对值不超过1

问:为什么要求高度差不超过1,而不是高度差为0?

在一棵树是两个节点、四个节点的情况下,高度差最好就是1

三、AVL树的优势

AVL树的节点数量和分布与完全二叉树类似

高度可以控制在logN

增删查改的效率可以控制在O(logN)

四、平衡因子(balance factor)

每个节点都有一个平衡因子

任何节点的平衡因子等于右子树的高度减去左子树的高度

平衡因子的大小:0、1、-1

平衡的AVL树

不平衡的非AVL树

五、AVL树的实现

5.1.AVL树的结构

树节点结构

cpp 复制代码
template<class K,class V>
struct AVLTreeNode
{
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;//需要parent指针
	int _bf;//平衡因子

	AVLTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_bf(0)
	{ }
};

树的结构

cpp 复制代码
template<class K,class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	//...
private:
	Node* _root = nullptr;
};

5.2.AVL树的插入

**步骤1:**按照二叉搜索树的方式插入新节点

**步骤2:**判断是否需要更新平衡因子,如果出现问题,则需更新平衡因子

5.3.平衡因子的更新

更新规则

  • 平衡因子 = 右子树高度 - 左子树高度
  • 只有子树高度变化才会影响当前节点的平衡因子
  • 新节点在parent的右子树,parent的平衡因子++
  • 新节点在parent的左子树,parent的平衡因子--
  • parent所在子树高度是否变化决定了是否继续向上更新

更新的停止条件

**情况1:**更新后的parent平衡因子等于0

更新中的parent平衡因子变化为 -1 --> 0 或者 1 --> 0

说明更新前parent子树一边高一边低,新增的节点插入在低的那一边

插入后parent子树的高度不变,parent子树符合平衡要求

不会影响parent的父节点的平衡因子,因为高度不变,更新结束

**情况2:**更新后的parent平衡因子等于1或者-1

更新中的parent平衡因子变化为 0 --> 1 或者 0 --> -1

说明更新前parent子树两边一样高,新增的节点插入在任意两边

插入后parent子树一边高一边低,parent子树仍符合平衡要求

会影响parent的父节点的平衡因子,因为高度增加1,需要继续向上更新

**情况3:**更新后的parent平衡因子等于2或者-2

更新中的parent平衡因子变化为 1 --> 2 或者 -1 --> -2

说明更新前parent子树一边高一边低,新增的节点插入在高的那一边

插入后parent子树高的那边更高,parent子树不符合平衡要求,需要旋转处理

旋转后,parent子树平衡,parent子树恢复到插入节点之前的高度,需要继续向上更新

**极端情况:**不断更新到根节点,根节点的平衡因子是1或-1,更新结束

终止条件总结

更新后的parent平衡因子为0

更新到根节点,根节点平衡因子为-1或1

插入以及平衡因子计算代码

cpp 复制代码
#pragma once

template<class K,class V>
struct AVLTreeNode
{
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;//需要parent指针
	int _bf;//平衡因子

	AVLTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_bf(0)
	{}
};

template<class K,class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool Insert(const pair<K, V>& kv)
	{
		//如果为空树
		if (_root == nullptr)
		{
			//将根节点赋为新节点
			_root = new Node(kv);
			//返回插入成功
			return true;
		}
		//父节点为空
		Node* parent = nullptr;
		//当前节点为根节点
		Node* cur = _root;
		//遍历二叉树
		//循环条件为cur不为空
		while (cur)
		{
			//如果当前节点的key值小于新节点的key值
			if (cur->_kv.first < kv.first)
			{
				//更新父节点
				parent = cur;
				//更新当前节点(指向右子树)
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)//如果当前节点的key值大于新节点的key值
			{
				//更新父节点
				parent = cur;
				//更新当前节点(指向左子树)
				cur = cur->_left;
			}
			else//当前节点的key值等于新节点的key值
			{
				//存在重复值,返回插入失败
				return false;
			}
		}
		//将当前节点(指向空)赋为新节点
		cur = new Node(kv);
		//如果父节点的key值小于新节点的key值
		if (parent->_kv.first < kv.first)
		{
			//将新节点赋为父节点的右子树
			parent->_right = cur;
		}
		else//如果父节点的key值大于新节点的key值
		{
			//将新节点赋为父节点的左子树
			parent->_left = cur;
		}
		//将当前节点与父节点链接
		cur->_parent = parent;

		//控制平衡
		//更新平衡因子
		//循环条件为parent不为空
		while (parent)
		{
			//如果新节点在parent的左边
			if (cur == parent->_left)
			{
				//parent的平衡因子--
				parent->_bf--;
			}
			else//如果新节点在parent的右边
			{
				//parent的平衡因子++
				parent->_bf++;
			}
			//如果更新后parent的平衡因子为0
			if (parent->_bf == 0)
			{
				//停止更新
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)//如果更新后parent的平衡因子为1或-1
			{
				//继续向上更新
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)//如果更新后parent的平衡因子为2或-2
			{
				//旋转
				break;
			}
			else
			{
				assert(false);
			}
		}
		return true;
	}
private:
	Node* _root = nullptr;
};

5.4.旋转的原则

保持搜索树的规则

让树从不平衡变为平衡,降低树的高度

左单旋、右单旋、左右双旋、右左双旋

左单旋

一棵根为10的树,有a、b、c三棵高度为h的子树(h>=0,符合AVL树)

(注:10可能是整棵树的根,也可能是局部子树的根)

在a子树中插入一个新节点,a子树的高度从h变为h+1,不断向上更新平衡因子

导致10的平衡因子从1变为2,10为根的树左右高度差超过1,违反了平衡规则

因为10 < b子树 < 15,将b变为10的右子树,将10变为15的左子树,将15变为这棵树新的根

既控制了树的平衡,又把这棵树的高度恢复为h+2,符合旋转规则

左单旋代码

cpp 复制代码
void RotateL(Node* parent)
{
	//获取节点15
	Node * subR = parent->_right;
	//获取子树b
	Node * subRL = subR->_left;
	//节点10的右子树更新为子树b
	parent->_right = subRL;
	//如果子树b不为空树
	if (subRL)
	{
		//子树b的父亲节点更新为节点10
		subRL->_parent = parent;
	}
	//获取parent节点的父亲节点
	Node * pParent = parent->_parent;
	//节点15的左子树更新为节点10
	subR->_left = parent;
	//节点10的父亲节点更新为节点15
	parent->_parent = subR;
	//如果旋转前,parent为根
	if (pParent == nullptr)
	{
		//更新根节点
		_root = subR;
		//将根节点的父亲节点置空
		subR->_parent = nullptr;
	}
	else//如果旋转前,parent为局部子树
	{
		//如果parent为父亲节点的左子树
		if (parent == pParent->_left)
		{
			//让parent的父亲节点的左子树指向节点15
			pParent->_left = subR;
		}
		else//如果parent为父亲节点的右子树
		{
			//让parent的父亲节点的右子树指向节点15
			pParent->_right = subR;
		}
		//更新节点15的父亲节点为parent的父亲节点
		subR->_parent = pParent;
	}
	//更新节点15和节点10的平衡因子
	parent->_bf = subR->_bf = 0;
}

右单旋

一棵根为10的树,有a、b、c三棵高度为h的子树(h>=0,符合AVL树)

(注:10可能是整棵树的根,也可能是局部子树的根)

在a子树中插入一个新节点,a子树的高度从h变为h+1,不断向上更新平衡因子

导致10的平衡因子从1变为2,10为根的树左右高度差超过1,违反了平衡规则

因为5 < b子树 < 10,将b变为10的左子树,将10变为5的右子树,将5变为这棵树新的根

既控制了树的平衡,又把这棵树的高度恢复为h+2,符合旋转规则

**情况1:**插入前a、b、c高度h == 0

**情况2:**插入前a、b、c高度h == 1

**情况3:**插入前a、b、c高度为h == 2

b、c为x、y、z中的任意一种情况

a必须为x的情况

(注:当a为y、z时,插入节点后y、z高度h+1,y、z自身需要旋转)

a中插入节点会引发10节点旋转的插入位置有4个

(左子树的左边与右边,右子树的左边与右边)

所以一共有3 * 3 * 4 == 36种情况

**情况4:**插入前a、b、c高度为h == 3

b、c可以是x、y-C中的任意一种情况

a如果是x,插入位置可以是4个叶子的任意孩子位置,有8种情况

a如果是y-C,4个叶子保留3个有4种情况,插入位置在有两个节点的任意孩子位置,有4种情况

所以一共有15 * 15 * ( 8 + 4 * 4 )= 5400种情况

右单旋代码

cpp 复制代码
//右单旋
void RotateR(Node* parent)
{
	//获取节点5
	Node* subL = parent->_left;
	//获取子树b
	Node* subLR = subL->_right;
	//节点10的左子树更新为子树b
	parent->_left = subLR;
	//如果子树b不为空子树
	if (subLR)
	{
		//子树b的父亲节点更新为节点10
		subLR->_parent = parent;
	}
	//获取parent节点的父亲节点
	Node* pParent = parent->_parent;
	//节点5的右子树更新为节点10
	subL->_right = parent;
	//节点10的父亲节点更新为节点5
	parent->_parent = subL;
	//如果旋转前,parent为根
	if (parent == _root)
	{
		//更新根节点
		_root = subL;
		//将根节点的父亲节点置空
		_root->_parent = nullptr;
	}
	else//如果旋转前,parent为局部子树
	{
		//如果parent为父亲节点的左子树
		if (pParent->_left == parent)
		{
			//让parent的父亲节点的左子树指向节点5
			pParent->_left = subL;
		}
		else//如果parent为父亲节点的右子树
		{
			//让parent的父亲节点的右子树指向节点5
			pParent->_right = subL;
		}
		//更新节点5的父亲节点为parent的父亲节点
		subL->_parent = pParent;
	}
	//更新节点5和节点10的平衡因子
	subL->_bf = 0;
	parent->_bf = 0;
}

左边高时,如果插入位置不是a子树,而是插入在b子树

b子树的高度从h变为h+1,引发了旋转,右单旋后依旧不平衡

右单旋解决的纯粹的左边高,所以插入在b子树,10为根的子树不再是单纯的左边高

对于10是左边高,5是右边高的情况,需要旋转两次才能解决

以5为旋转点进行一个左单旋,以10为旋转点进行一个右单旋

情况1: 插入前a、b、c高度h == 0

**情况2:**插入前a、b、c高度h == 1

左右双旋

节点8变成新的根节点,节点5变成8的左子树,节点10变成8的右子树

节点8的左子树变为节点5的右子树,节点8的右子树变为节点10的左子树

可以根据节点8的平衡因子来判断是那种场景

左右双旋代码

cpp 复制代码
//左右双旋
void RotateLR(Node* parent)
{
	//获取节点5
	Node* subL = parent->_left;
	//获取节点8
	Node* subLR = subL->_right;
	//获取节点8的平衡因子
	int bf = subLR->_bf;

	//节点5左单旋
	RotateL(parent->_left);
	//节点10右单旋
	RotateR(parent);

	//如果节点8的平衡因子插入后变为-1
	if (bf == -1)
	{
		//旋转后节点8的平衡因子更新为0
		subLR->_bf = 0;
		//旋转后节点5的平衡因子更新为0
		subL->_bf = 0;
		//旋转后节点10的平衡因子更新为1
		parent->_bf = 1;
	}
	else if (bf == 1)//如果节点8的平衡因子插入后变为1
	{
		//旋转后节点8的平衡因子更新为0
		subLR->_bf = 0;
		//旋转后节点5的平衡因子更新为-1
		subL->_bf = -1;
		//旋转后节点10的平衡因子更新为0
		parent->_bf = 0;
	}
	else if(bf == 0)//如果节点8的平衡因子插入后变为0
	{
		//旋转后节点8的平衡因子更新为0
		subLR->_bf = 0;
		//旋转后节点5的平衡因子更新为0
		subL->_bf = 0;
		//旋转后节点10的平衡因子更新为0
		parent->_bf = 0;
	}
	else//保证平衡因子出现异常时能够知晓
	{
		assert(false);
	}
}

右左双旋

节点12变成新的根节点,节点10变成12的左子树,节点15变成12的右子树

节点12的左子树变为节点10的右子树,节点12的右子树变为节点15的左子树

可以根据节点12的平衡因子来判断是那种场景

右左双旋代码

cpp 复制代码
void RotateRL(Node* parent)
{
	//获取节点15
	Node * subR = parent->_right;
	//获取节点12
	Node * subRL = subR->_left;
	//获取节点12的平衡因子
	int bf = subRL->_bf;
	//节点15右单旋
	RotateR(parent->_right);
	//节点10左单旋
	RotateL(parent);

	if (bf == 0)//如果节点12的平衡因子插入后变为0
	{
		//旋转后节点15的平衡因子更新为0
		subR->_bf = 0;
		//旋转后节点12的平衡因子更新为0
		subRL->_bf = 0;
		//旋转后节点10的平衡因子更新为0
		parent->_bf = 0;
	}
	else if (bf == 1)//如果节点12的平衡因子插入后变为1
	{
		//旋转后节点15的平衡因子更新为0
		subR->_bf = 0;
		//旋转后节点12的平衡因子更新为0
		subRL->_bf = 0;
		//旋转后节点10的平衡因子更新为-1
		parent->_bf = -1;
	}
	else if (bf == -1)//如果节点12的平衡因子插入后变为-1
	{
		//旋转后节点15的平衡因子更新为1
		subR->_bf = 1;
		//旋转后节点12的平衡因子更新为0
		subRL->_bf = 0;
		//旋转后节点10的平衡因子更新为0
		parent->_bf = 0;
	}
	else//保证平衡因子出现异常时能够知晓
	{
		assert(false);
	}
}

5.5.AVL树实现总代码

cpp 复制代码
#pragma once
#include <iostream>
#include "assert.h"
#include "math.h"
using namespace std;

template<class K,class V>
struct AVLTreeNode
{
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;//需要parent指针
	int _bf;//平衡因子

	AVLTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_bf(0)
	{}
};

template<class K,class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool Insert(const pair<K, V>& kv)
	{
		//如果为空树
		if (_root == nullptr)
		{
			//将根节点赋为新节点
			_root = new Node(kv);
			//返回插入成功
			return true;
		}
		//父节点为空
		Node* parent = nullptr;
		//当前节点为根节点
		Node* cur = _root;
		//遍历二叉树
		//循环条件为cur不为空
		while (cur)
		{
			//如果当前节点的key值小于新节点的key值
			if (cur->_kv.first < kv.first)
			{
				//更新父节点
				parent = cur;
				//更新当前节点(指向右子树)
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)//如果当前节点的key值大于新节点的key值
			{
				//更新父节点
				parent = cur;
				//更新当前节点(指向左子树)
				cur = cur->_left;
			}
			else//当前节点的key值等于新节点的key值
			{
				//存在重复值,返回插入失败
				return false;
			}
		}
		//将当前节点(指向空)赋为新节点
		cur = new Node(kv);
		//如果父节点的key值小于新节点的key值
		if (parent->_kv.first < kv.first)
		{
			//将新节点赋为父节点的右子树
			parent->_right = cur;
		}
		else//如果父节点的key值大于新节点的key值
		{
			//将新节点赋为父节点的左子树
			parent->_left = cur;
		}
		//将当前节点与父节点链接
		cur->_parent = parent;

		//控制平衡
		//更新平衡因子
		//循环条件为parent不为空
		while (parent)
		{
			//如果新节点在parent的左边
			if (cur == parent->_left)
			{
				//parent的平衡因子--
				parent->_bf--;
			}
			else//如果新节点在parent的右边
			{
				//parent的平衡因子++
				parent->_bf++;
			}
			//如果更新后parent的平衡因子为0
			if (parent->_bf == 0)
			{
				//停止更新
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)//如果更新后parent的平衡因子为1或-1
			{
				//继续向上更新
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)//如果更新后parent的平衡因子为2或-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);
			}
		}
		return true;
	}

	//右单旋
	void RotateR(Node* parent)
	{
		//获取节点5
		Node* subL = parent->_left;
		//获取子树b
		Node* subLR = subL->_right;
		//节点10的左子树更新为子树b
		parent->_left = subLR;
		//如果子树b不为空子树
		if (subLR)
		{
			//子树b的父亲节点更新为节点10
			subLR->_parent = parent;
		}
		//获取parent节点的父亲节点
		Node* pParent = parent->_parent;
		//节点5的右子树更新为节点10
		subL->_right = parent;
		//节点10的父亲节点更新为节点5
		parent->_parent = subL;
		//如果旋转前,parent为根
		if (parent == _root)
		{
			//更新根节点
			_root = subL;
			//将根节点的父亲节点置空
			_root->_parent = nullptr;
		}
		else//如果旋转前,parent为局部子树
		{
			//如果parent为父亲节点的左子树
			if (pParent->_left == parent)
			{
				//让parent的父亲节点的左子树指向节点5
				pParent->_left = subL;
			}
			else//如果parent为父亲节点的右子树
			{
				//让parent的父亲节点的右子树指向节点5
				pParent->_right = subL;
			}
			//更新节点5的父亲节点为parent的父亲节点
			subL->_parent = pParent;
		}
		//更新节点5和节点10的平衡因子
		subL->_bf = 0;
		parent->_bf = 0;
	}

	//左单旋
	void RotateL(Node* parent)
	{
		//获取节点15
		Node* subR = parent->_right;
		//获取子树b
		Node* subRL = subR->_left;
		//节点10的右子树更新为子树b
		parent->_right = subRL;
		//如果子树b不为空树
		if (subRL)
		{
			//子树b的父亲节点更新为节点10
			subRL->_parent = parent;
		}
		//获取parent节点的父亲节点
		Node* pParent = parent->_parent;
		//节点15的左子树更新为节点10
		subR->_left = parent;
		//节点10的父亲节点更新为节点15
		parent->_parent = subR;
		//如果旋转前,parent为根
		if (pParent == nullptr)
		{
			//更新根节点
			_root = subR;
			//将根节点的父亲节点置空
			subR->_parent = nullptr;
		}
		else//如果旋转前,parent为局部子树
		{
			//如果parent为父亲节点的左子树
			if (parent == pParent->_left)
			{
				//让parent的父亲节点的左子树指向节点15
				pParent->_left = subR;
			}
			else//如果parent为父亲节点的右子树
			{
				//让parent的父亲节点的右子树指向节点15
				pParent->_right = subR;
			}
			//更新节点15的父亲节点为parent的父亲节点
			subR->_parent = pParent;
		}
		//更新节点15和节点10的平衡因子
		parent->_bf = subR->_bf = 0;
	}

	//左右双旋
	void RotateLR(Node* parent)
	{
		//获取节点5
		Node* subL = parent->_left;
		//获取节点8
		Node* subLR = subL->_right;
		//获取节点8的平衡因子
		int bf = subLR->_bf;

		//节点5左单旋
		RotateL(parent->_left);
		//节点10右单旋
		RotateR(parent);

		if (bf == -1)//如果节点8的平衡因子插入后变为-1
		{
			//旋转后节点8的平衡因子更新为0
			subLR->_bf = 0;
			//旋转后节点5的平衡因子更新为0
			subL->_bf = 0;
			//旋转后节点10的平衡因子更新为1
			parent->_bf = 1;
		}
		else if (bf == 1)//如果节点8的平衡因子插入后变为1
		{
			//旋转后节点8的平衡因子更新为0
			subLR->_bf = 0;
			//旋转后节点5的平衡因子更新为-1
			subL->_bf = -1;
			//旋转后节点10的平衡因子更新为0
			parent->_bf = 0;
		}
		else if(bf == 0)//如果节点8的平衡因子插入后变为0
		{
			//旋转后节点8的平衡因子更新为0
			subLR->_bf = 0;
			//旋转后节点5的平衡因子更新为0
			subL->_bf = 0;
			//旋转后节点10的平衡因子更新为0
			parent->_bf = 0;
		}
		else//保证平衡因子出现异常时能够知晓
		{
			assert(false);
		}
	}

	//右左双旋
	void RotateRL(Node* parent)
	{
		//获取节点15
		Node* subR = parent->_right;
		//获取节点12
		Node* subRL = subR->_left;
		//获取节点12的平衡因子
		int bf = subRL->_bf;
		//节点15右单旋
		RotateR(parent->_right);
		//节点10左单旋
		RotateL(parent);

		if (bf == 0)//如果节点12的平衡因子插入后变为0
		{
			//旋转后节点15的平衡因子更新为0
			subR->_bf = 0;
			//旋转后节点12的平衡因子更新为0
			subRL->_bf = 0;
			//旋转后节点10的平衡因子更新为0
			parent->_bf = 0;
		}
		else if (bf == 1)//如果节点12的平衡因子插入后变为1
		{
			//旋转后节点15的平衡因子更新为0
			subR->_bf = 0;
			//旋转后节点12的平衡因子更新为0
			subRL->_bf = 0;
			//旋转后节点10的平衡因子更新为-1
			parent->_bf = -1;
		}
		else if (bf == -1)//如果节点12的平衡因子插入后变为-1
		{
			//旋转后节点15的平衡因子更新为1
			subR->_bf = 1;
			//旋转后节点12的平衡因子更新为0
			subRL->_bf = 0;
			//旋转后节点10的平衡因子更新为0
			parent->_bf = 0;
		}
		else//保证平衡因子出现异常时能够知晓
		{
			assert(false);
		}
	}

	//中序遍历打印
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	//AVL平衡检测
	bool IsBalanceTree()
	{
		return _IsBalanceTree(_root);
	}

	//AVL高度计算
	int Height()
	{
		return _Height(_root);
	}

	//AVL节点计算
	int Size()
	{
		return _Size(_root);
	}

	//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;
	}
private:

	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}

	int _Height(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);

		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

	int _Size(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}
		return _Size(root->_left) + _Size(root->_right) + 1;
	}

	bool _IsBalanceTree(Node* root)
	{
		//空树也是AVL树
		if (nullptr == root)
		{
			return true;
		}

		//计算pRoot结点的平衡因子
		//即pRoot左右子树的高度差
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		int diff = rightHeight - leftHeight;

		//如果计算出的平衡因子与pRoot的平衡因子不相等
		//pRoot平衡因子的绝对值超过1,则一定不是AVL树

		if (abs(diff) >= 2)
		{
			cout << root->_kv.first << "高度差异常" << endl;
			return false;
		}

		if (root->_bf != diff)
		{
			cout << root->_kv.first << "平衡因子异常" << endl;
			return false;
		}

		//pRoot的左和右如果都是AVL树
		//则该树一定是AVL树
		return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
	}

	Node* _root = nullptr; 
};

5.6.测试代码

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "AVLTree.h"
#include <vector>


//测试插入,中序遍历代码
void TestAVLTree1()
{
	AVLTree<int, int> t;
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	for (auto e : a)
	{
		t.Insert({ e, e });
	}
	t.InOrder();
	cout << t.IsBalanceTree() << endl;
}

//插入一堆随机值,测试平衡,高度和性能
void TestAVLTree2()
{
	const int N = 1000000;
	vector<int> v;
	v.reserve(N);
	srand(time(0));

	for (size_t i = 0; i < N; i++)
	{
		v.push_back(rand() + i);
	}

	size_t begin2 = clock();
	AVLTree<int, int> t;
	for (auto e : v)
	{
		t.Insert(make_pair(e, e));
	}
	size_t end2 = clock();

	cout << "Insert:" << end2 - begin2 << endl;
	cout << t.IsBalanceTree() << endl;

	cout << "Height:" << t.Height() << endl;
	cout << "Size:" << t.Size() << endl;

	size_t begin1 = clock();

	////确定在的值
	///*for (auto e : v)
	//{
	//	t.Find(e);
	//}*/

	// 随机值
	for (size_t i = 0; i < N; i++)
	{
		t.Find((rand() + i));
	}

	size_t end1 = clock();
	cout << "Find:" << end1 - begin1 << endl;
}

int main()
{
	TestAVLTree1();
	TestAVLTree2();
	return 0;
}
相关推荐
NAGNIP2 小时前
一文搞懂深度学习中的池化!
算法·面试
执笔画流年呀2 小时前
PriorityQueue(堆)续集
java·开发语言
山川行2 小时前
关于《项目C语言》专栏的总结
c语言·开发语言·数据结构·vscode·python·算法·visual studio code
呜喵王阿尔萨斯2 小时前
C and C++ code
c语言·开发语言·c++
旺仔.2912 小时前
顺序容器:deque双端队列 详解
c++
左左右右左右摇晃2 小时前
JDK 1.7 ConcurrentHashMap——分段锁
java·开发语言·笔记
xcLeigh2 小时前
Python入门:Python3基础练习题详解,从入门到熟练的 25 个实例(六)
开发语言·python·教程·python3·练习题
烤麻辣烫3 小时前
I/O流 基础流
java·开发语言·学习·intellij-idea
Jasonakeke3 小时前
我的编程来时路
java·c++·python