浅谈AVL树

news2024/12/26 11:09:39

文章目录

  • 1.介绍
    • 1.1定义
    • 1.2来源
    • 1.3概念
      • 1.特性
      • 2.平衡因子[ Balance Factor-- _bf ]
  • 2.BST==>AVL
    • 1.示例分析
    • 2.情况分类
    • 3.代码剖析
      • 3.1左左型-右单旋
      • 3.2右右型-左单旋
      • 3.3左右型-左右旋
      • 3.4右左型:右左旋
      • 3.5总图
  • 3.完整代码
    • 3.1AVLTree.h
    • 3.2Test.cpp

在这里插入图片描述

1.介绍

1.1定义

AVL树 – 平衡二叉树 – 平衡二叉搜索(排序)树 – 高度平衡搜索树
Balanced Binary Tree (BBT)

1.2来源

AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis,他们在1962年的论文《An algorithm for the organization of information》中发表了它。
二叉搜索树可以缩短查找的效率,但在数据有序或接近有序时它将退化为单支树,查找元素相当于在顺序表中搜索,效率低下。两位俄罗斯的数学家发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

1.3概念

1.特性

  • 一棵空树或左右两个子树高度差绝对值不超过1
  • 左右两个子树也都是一棵高度平衡搜索树

2.平衡因子[ Balance Factor-- _bf ]

  1. 结点的平衡因子 == 右子树高度 - 左子树高度
  2. | _bf | <= 1
  3. AVL树不一定有平衡因子, 使用平衡因子只是一种较为简单的实现方式
    在这里插入图片描述

2.BST==>AVL

[设定 _bf = 右子树高度 - 左子树高度]

1.示例分析

先看一下下面的图 了解一下什么叫做旋转 以及旋转的目的

在这里插入图片描述
在这里插入图片描述

2.情况分类

在这里插入图片描述

3.代码剖析

3.1左左型-右单旋

在这里插入图片描述

	void RotateRight(Node* dad)
	{
		Node* Grandpa = dad->_parent;
		Node* sonL = dad->_left;
		Node* sonLR = sonL->_right;

		//dad连接sonLR sonLR不空-继承dad 为空不继承
		dad->_left = sonLR;
		if (sonLR)
			sonLR->_parent = dad;
        //sonL连接dad dad继承sonL
		sonL->_right = dad;
		dad->_parent = sonL;
		//G为空 表明dad为根结点 
		if (Grandpa == nullptr)
		{
			//更新根结点
			_root = sonL;
			//更新根结点成员属性
			_root->_parent = nullptr;
		}
		else
		{
			//父连子
			if (Grandpa->_left == dad)
				Grandpa->_left = sonL;
			else
				Grandpa->_right = sonL;
			//子继承父
			sonL->_parent = Grandpa;
		}
		//更新_bf
		sonL->_bf = dad->_bf = 0;
	}

3.2右右型-左单旋

在这里插入图片描述

void RotateLeft(Node* dad)
{
	Node* Grandpa = dad->_parent;
	Node* sonR = dad->_right;
	Node* sonRL = sonR->_left;

	//dad连接sonRL sonRL不空继承dad 为空不继承
	dad->_right = sonRL;
	if (sonRL)
		sonRL->_parent = dad;
	//sonR连接dad dad继承sonR
	sonR->_left = dad;
	dad->_parent = sonR;
	//Grandpa为空--dad为根节点 更新后 sonR为根节点 根节点的_parent置空
	if (Grandpa == nullptr)
	{
		_root = sonR;
		_root->_parent = nullptr;
	}
	//不为空 依实际连接
	else
	{
		//父连子
		if (Grandpa->_left == dad)
			Grandpa->_left = sonR;
		else
			Grandpa->_right = sonR;
		//子继承父
		sonR->_parent = Grandpa;
	}
	//左旋目的达到 更新_bf
	dad->_bf = sonR->_bf = 0;
}

3.3左右型-左右旋

在这里插入图片描述
在这里插入图片描述

void RotateLR(Node* dad)
{
	Node* sonL = dad->_left;
	Node* sonLR = sonL->_right;
	int bf = sonLR->_bf;

	RotateLeft(sonL);
	RotateRight(dad);

	if (bf == 1)
	{
		sonLR->_bf = 0;
		sonL->_bf = -1;
		dad->_bf = 0;
	}
	else if (bf == -1)
	{
		sonLR->_bf = 0;
		sonL->_bf = 0;
		dad->_bf = 1;
	}
	else if (bf == 0)
	{
		sonLR->_bf = 0;
		sonL->_bf = 0;
		dad->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

3.4右左型:右左旋

在这里插入图片描述
在这里插入图片描述

void RotateRL(Node* dad)
{
	Node* sonR = dad->_right;
	Node* sonRL = sonR->_left;
	int bf = sonRL->_bf;//最终根结点的_bf

	RotateLeft(dad->_right);
	RotateRight(dad);

	if (bf == 1)
	{
		sonRL->_bf = 0;
		dad->_bf = -1;
		sonR->_bf = 0;
	}
	else if (bf == -1)
	{
		sonRL->_bf = 0;
		dad->_bf = 0;
		sonR->_bf = 1;
	}
	else if (bf == 0)
	{
		sonRL->_bf = 0;
		dad->_bf = 0;
		sonR->_bf = 0;1
	}
	else
	{
		assert(false);
	}
}

3.5总图

在这里插入图片描述

3.完整代码

3.1AVLTree.h

#pragma once
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <array>
#include <time.h>
#include <queue>
#include <stack>
#include <string>
#include <set>
#include <map>
#include <functional>
#include <assert.h>
#include <math.h>
using namespace std;
template<class K, class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	pair<K, V> _pair;
	int _bf;           // balance factor

	AVLTreeNode(const pair<K, V>& pair)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _pair(pair)
		, _bf(0)
	{

	}
};

//高度平衡搜索树
template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:

	//插入--创建二叉树
	bool Insert(const pair<K, V>& pair)
	{
		//空树--new结点
		if (_root == nullptr)
		{
			_root = new Node(pair);
			return true;
		}

		//非空--插入
		//1.定位到合适位置
		Node* parent = nullptr;
		Node* cp = _root;
		while (cp)
		{
			if (pair.first > cp->_pair.first)
			{
				parent = cp;
				cp = cp->_right;
			}
			else if (pair.first < cp->_pair.first)
			{
				parent = cp;
				cp = cp->_left;
			}
			else
			{
				//搜索树不可有数据重复 -- 插入失败
				return false;
			}
		}

		//2.链接
		cp = new Node(pair);
		if (pair.first < parent->_pair.first)
		{
			parent->_left = cp;
		}
		else
		{
			parent->_right = cp;
		}
		//cp继承parent
		cp->_parent = parent;

		//构建AVL树
		while (parent)
		{
			//一、更新平衡因子

			//插入结点在右子树
			if (cp == parent->_right)
			{
				parent->_bf++;
			}
			//插入结点在左子树
			else
			{
				parent->_bf--;
			}

			//二、分类讨论

			// _bf == 1/-1 原为0 现高度受到影响
			// 回溯直至遇到根源结点 即_bf==2/-2的结点
			if (parent->_bf == 1 || parent->_bf == -1)
			{
				parent = parent->_parent;
				cp = cp->_parent;
			}
			//_bf == 0 不做处理 原为-1/1 现已满足 不继续更新
			else if (parent->_bf == 0)
			{
				break;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				//旋转处理目的:
				//1.使得当前子树平衡 2.降低当前子树的高度

				//左单旋
				if (parent->_bf == 2 && cp->_bf == 1)
				{
					RotateL(parent);
				}
				//右单旋
				else if (parent->_bf == -2 && cp->_bf == -1)
				{
					RotateR(parent);
				}
				//左右旋
				else if (parent->_bf == -2 && cp->_bf == 1)
				{
					RotateLR(parent);
				}
				//右左旋
				else if (parent->_bf == 2 && cp->_bf == -1)
				{
					RotateRL(parent);
				}
				else
				{
					assert(false);
				}

				break;
			}
			else
			{
				assert(false);
			}
		}

		return true;
	}

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

	//高度接口
	int Height()
	{
		return _Height(_root);
	}

	//判断是否满足AVL树平衡
	bool IsBalance()
	{
		return _IsBalance(_root);
	}

private:

	// 中序遍历
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_InOrder(root->_left);
		cout << root->_pair.first << " ";
		_InOrder(root->_right);
	}

	//高度接口
	int _Height(Node* root)
	{
		if (root == NULL)
			return 0;

		int leftH = _Height(root->_left);
		int rightH = _Height(root->_right);

		return leftH > rightH ? leftH + 1 : rightH + 1;
	}

	//判断是否满足AVL树平衡
	bool _IsBalance(Node* root)
	{
		if (root == NULL)
		{
			return true;
		}

		int leftH = _Height(root->_left);
		int rightH = _Height(root->_right);

		if (rightH - leftH != root->_bf)
		{
			cout << root->_pair.first << "Abnormal node balance factor!" << endl;
			return false;
		}

		return abs(leftH - rightH) < 2
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right);
	}

	//左单旋
	void RotateL(Node* parent)
	{
		//记录Grandpa
		Node* Grandpa = parent->_parent;

		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		//parent链接subRL subRL不空继承parent 空没必要继承
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;

		//subR连接parent parent继承subR
		subR->_left = parent;
		parent->_parent = subR;

		//Grandpa为空--parent为根节点 更新后 subR为根节点 根节点的_parent置空
		if (Grandpa == nullptr)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		//不为空 依实际连接
		else
		{
			//父连子
			if (Grandpa->_left == parent)
			{
				Grandpa->_left = subR;
			}
			else
			{
				Grandpa->_right = subR;
			}
			//子继承父
			subR->_parent = Grandpa;
		}

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

	//右单旋
	void RotateR(Node* parent)
	{
		Node* Grandpa = parent->_parent;

		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

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

		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (Grandpa->_left == parent)
			{
				Grandpa->_left = subL;
			}
			else
			{
				Grandpa->_right = subL;
			}
			subL->_parent = Grandpa;
		}

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

	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf = subLR->_bf;

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

		if (bf == 1)
		{
			parent->_bf = 0;
			subLR->_bf = 0;
			subL->_bf = -1;
		}
		else if (bf == -1)
		{
			parent->_bf = 1;
			subLR->_bf = 0;
			subL->_bf = 0;
		}
		else if (bf == 0)
		{
			parent->_bf = 0;
			subLR->_bf = 0;
			subL->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

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

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

		if (bf == 1)
		{
			subR->_bf = 0;
			parent->_bf = -1;
			subRL->_bf = 0;
		}
		else if (bf == -1)
		{
			subR->_bf = 1;
			parent->_bf = 0;
			subRL->_bf = 0;
		}
		else if (bf == 0)
		{
			subR->_bf = 0;
			parent->_bf = 0;
			subRL->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}


private:
	Node* _root = nullptr;
};

void Test_AVLTree1()
{
	int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	int b[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	AVLTree<int, int> tree;
	for (auto e : b)
	{
		tree.Insert(make_pair(e, e));
		if (tree.IsBalance())
		{
			cout << e << "插入成功!" << endl;
		}
		else
		{
			cout << e << "插入失败!" << endl;
		}
	}

	cout << "此树中序遍历:" << endl;
	tree.InOrder();

	if (tree.IsBalance())
	{
		cout << "此树为一棵AVL树" << endl;
	}
	else
	{
		cout << "此树不为一棵AVL树!" << endl;
	}
}

void Test_AVLTree2()
{
	srand(time(0));
	const size_t N = 10;
	AVLTree<int, int> tree;
	for (size_t i = 0; i < N; ++i)
	{
		size_t x = rand() + i;
		tree.Insert(make_pair(x, x));
		if (tree.IsBalance())
		{
			cout << x << "插入成功!" << endl;
		}
		else
		{
			cout << x << "插入失败!" << endl;
		}
	}

	cout << "此树中序遍历:" << endl;
	tree.InOrder();

	if (tree.IsBalance())
	{
		cout << "此树为一棵AVL树" << endl;
	}
	else
	{
		cout << "此树不为一棵AVL树!" << endl;
	}

	cout << "此树高度为:" << tree.Height() << endl;
}

3.2Test.cpp

#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <array>
#include <time.h>
#include <queue>
#include <stack>
#include <string>
#include <set>
#include <map>
#include <functional>
using namespace std;
#include "AVLTree.h"

int main()
{
	Test_AVLTree1();
	return 0;
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1051086.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CVE-2020-11978 Apache Airflow 命令注入漏洞分析与利用

简介 漏洞软件&#xff1a;Apache Airflow影响版本&#xff1a;< 1.10.10 环境 Vulhub 漏洞测试靶场 复现步骤 进入 /root/vulhub/airflow/CVE-2020-11978/ 目录运行以下命令启动环境 # 初始化数据库 docker compose run airflow-init # 开启服务 docker compose up -…

字符串常量池位于JVM哪里

Java6 和6之前&#xff0c;常量池是存放在方法区&#xff08;永久代&#xff09;中的。Java7&#xff0c;将常量池是存放到了堆中。Java8 之后&#xff0c;取消了整个永久代区域&#xff0c;取而代之的是元空间。运行时常量池和静态常量池存放在元空间中&#xff0c;而字符串常…

web:[ACTF2020 新生赛]Upload

题目 点进页面&#xff0c;是一个文件上传&#xff0c;能联想到getshell 先尝试随便上传一个文件试试 显示上传的文件以jpg、png、gif结尾的图片 构造一句话木马&#xff0c;再将文件后缀改为jpg <?php eval($_POST[1234]);?> 显示上传成功&#xff0c;但是显示无法…

MySQL学习笔记24

MySQL的物理备份&#xff1a; xtrabackup备份介绍&#xff1a; xtrabackup优缺点&#xff1a; 优点&#xff1a; 1、备份过程快速、可靠&#xff08;因为是物理备份&#xff09;&#xff1b;直接拷贝物理文件。 2、支持增量备份&#xff0c;更为灵活&#xff1b; 3、备份…

【数据结构】——顺序表详解

大家好&#xff01;当我们学习了动态内存管理后&#xff0c;就可以写一个管理数据的顺序表了&#xff01;&#xff01;&#xff01; 顺序表的理解&#xff1a; 线性表是最基本、最简单、也是最常用的一种数据结构。线性表&#xff08;linear list&#xff09;是数据结构的一种…

self-attention、transformer、bert理解

参考李宏毅老师的视频 https://www.bilibili.com/video/BV1LP411b7zS?p2&spm_id_frompageDriver&vd_sourcec67a2725ac3ca01c38eb3916d221e708 一个输入&#xff0c;一个输出&#xff0c;未考虑输入之间的关系&#xff01;&#xff01;&#xff01; self-attention…

CSS详细基础(三)复合选择器

前两章介绍了CSS中的基础属性&#xff0c;以及一些基础的选择器&#xff0c;本贴开始介绍复合选择器的内容~ ​ 在 CSS 中&#xff0c;可以根据选择器的类型把选择器分为基础选择器和复合选择器&#xff0c;复合选择器是建立在基础选择器之上&#xff0c;对基本选择器进行组合形…

c语言练习70:反转两次的数字

反转两次的数字 题⽬描述&#xff1a; 反转 ⼀个整数意味着倒置它的所有位。 例如&#xff0c;反转 2021 得到 1202 。反转 12300 得到 321 &#xff0c;不保留前导零 。 给你⼀个整数 num &#xff0c;反转 num 得到 reversed1 &#xff0c;接着反转 reversed1 得到 revers…

使用KEIL自带的仿真器仿真遇到问题解决

*** error 65: access violation at 0x40021000 : no read permission 修改debug选项设置为下方内容。

Java之多线程的生产者消费者问题的详细解析

3.生产者消费者 3.1生产者和消费者模式概述【应用】 概述 生产者消费者模式是一个十分经典的多线程协作的模式&#xff0c;弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻。 所谓生产者消费者问题&#xff0c;实际上主要是包含了两类线程&#xff1a; 一类是生产者…

MJ 种的摄影提示词关键字

景别 Front view photo 正面照 Front view photo of a Boston Terrier with smileSide view photo 侧身照 Side view photo of a Boston Terrier with smileBack view photo 背影照 Back view photo of a Boston TerrierFull body 全身照 Full body photo of a Boston Ter…

electron之快速上手

前一篇文章已经介绍了如何创建一个electron项目&#xff0c;没有看过的小伙伴可以去实操一下。 接下来给大家介绍一下electron项目的架构是什么样的。 electron之快速上手 electron项目一般有两个进程&#xff1a;主进程和渲染进程。 主进程&#xff1a;整个项目的唯一入口&…

2.物联网射频识别,RFID通信原理,RFID读写器与标签无线交互方式、数据反馈方式,RFID调制与解调、编码方式,不同RFID标签与读写器

一。RFID无线识别的原理 1.RFID系统无线通信基本原理 如下图所示&#xff0c;左边是读写器&#xff08;刷卡器&#xff09;&#xff0c;右边是标签&#xff08;卡&#xff09;&#xff0c;中间通过无线通信方式。 标签&#xff1a;&#xff08;卡&#xff09; 读写器&#xff…

Sound/播放提示音, Haptics/触觉反馈, LocalNotification/本地通知 的使用

1. Sound 播放提示音 1.1 音频文件: tada.mp3&#xff0c; badum.mp3 1.2 文件位置截图: 1.3 实现 import AVKit/// 音频管理器 class SoundManager{// 单例对象 Singletonstatic let instance SoundManager()// 音频播放var player: AVAudioPlayer?enum SoundOption: Stri…

python二维码识别tesseract

window安装tesseract 下载路径&#xff1a; https://digi.bib.uni-mannheim.de/tesseract/ 选择 双击安装在D:\sore\teeseract-OCR后&#xff1a; 配置环境变量 配置环境变量Path&#xff1a;D:\sore\teeseract-OCR 配置语言包的环境变量TESSDATA_PREFIX&#xff1a; D:\s…

搭建自己的搜索引擎之五

一、前言 接上文 搭建自己的搜索引擎之四&#xff0c;下面继续介绍茴香豆茴字的另外两种写法。 二、Jest Jest是ES的Java Http Rest客户端&#xff0c;它主要是为了弥补以前ES自有API缺少HttpRest接口客户端的不足&#xff0c;但因为现在ES官方已经提供了RestClient ,该项目已…

Dynamic CRM开发 - 实体窗体(二)主窗体

主窗体是功能最丰富,使用场景最多的窗体。 主窗体界面如下图: 下面按照图中的序号,简述一下窗体的主要功能: 0、窗体的主要布局部分,即用户看到的内容,可以拖动右侧的字段到窗体中想要放置的地方。 默认有标题、常规(选项卡)、页脚三部分,常规处于高亮状态,即可以…

第十二章 类和对象

C面向对象的三大特性为&#xff1a;封装、继承、多态 C认为万事万物都皆为对象&#xff0c;对象上有其属性和行为 例如&#xff1a; 人可以作为对象&#xff0c;属性有姓名、年龄、身高、体重...&#xff0c;行为有走、跑、跳、吃饭、唱歌... 车也可以作为对象&#xff0c;…

docker安装apisix全教程包含windows和linux

docker安装apisix 一、Windows安装1、首先需要安装docker和docker compose&#xff0c;如果直接安装docker desktop&#xff0c;会自动安装docker compose。2、重新启动电脑3、访问 Docker 的下载&#xff08;[https://www.docker.com/products/docker-desktop](https://www.do…

RocketMQ 版本升级测试

一、背景 RocketMQ 版本升级&#xff0c;3.6.4升级到5.1.3。 二、机器资源 应用端&#xff1a; 10.XX.67.249【机器 1】 cd /home/product/logs/dolphin_task_test_logs/ vim info.logMQ 服务端&#xff1a; 旧MQ 10.XX.108.249 broker-001。用于测试升级NameServer【机器 2…