二叉树之AVL树

文章目录

    • 1. AVL树的概念(logN)
      • 1.1背景
      • 1.2规则
    • 2.AVL树节点的定义
    • 3.AVL树的插入
    • 4. AVL树的旋转(重点)
      • 4.1 新节点插入较高的右子树的右侧:左单璇;
      • 4.2 新节点插入较高左子树的左侧:右单璇;
      • 4.3(双旋) 新节点插入较高右子树的左侧---右左:先右单旋再左单旋
        • 4.3.1添加节点位置的三种情况
      • 4.4 新节点插入较高左子树的右侧---左右:先左单旋再右单旋
    • 5.判断AVL树是否平衡

1. AVL树的概念(logN)

1.1背景

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树查找元素相当于在顺序表中搜索元素效率低下

1.2规则

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

它的左右子树都是AVL树
左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

平衡因子:右子树节点个数减左子树的个数

注意:平衡因子不是必须的,只是辅助;

问题:为什么平衡因子不能超过1呢;
原因:因为实现不了0,最低的高度差就是1;

2.AVL树节点的定义

AVL树节点的定义

template<class T>
struct AVLTreeNode
{
 AVLTreeNode(const T& data)
     : _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
 , _data(data), _bf(0)
 {}
 AVLTreeNode<T>* _pLeft;   // 该节点的左孩子
 AVLTreeNode<T>* _pRight;  // 该节点的右孩子
 AVLTreeNode<T>* _pParent; // 该节点的双亲
 T _data;
 int _bf;                  // 该节点的平衡因子
};

3.AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么
AVL树的插入过程可以分为两步:

  1. 按照二叉搜索树的方式插入新节点
  2. 调整节点的平衡因子
bool Insert(const T& data)
{
    // 1. 先按照二叉搜索树的规则将节点插入到AVL树中
    // ...
    
    // 2. 新节点插入后,AVL树的平衡性可能会遭到破坏,此时就需要更新平衡因子,并检测是否
破坏了AVL树
    //   的平衡性
    
 /*
 pCur插入后,pParent的平衡因子一定需要调整,在插入之前,pParent
 的平衡因子分为三种情况:-1,0, 1, 分以下两种情况:
  1. 如果pCur插入到pParent的左侧,只需给pParent的平衡因子-1即可
  2. 如果pCur插入到pParent的右侧,只需给pParent的平衡因子+1即可
  
 此时:pParent的平衡因子可能有三种情况:0,正负1, 正负2
  1. 如果pParent的平衡因子为0,说明插入之前pParent的平衡因子为正负1,插入后被调整
成0,此时满足
     AVL树的性质,插入成功
  2. 如果pParent的平衡因子为正负1,说明插入前pParent的平衡因子一定为0,插入后被更
新成正负1,此
     时以pParent为根的树的高度增加,需要继续向上更新
  3. 如果pParent的平衡因子为正负2,则pParent的平衡因子违反平衡树的性质,需要对其进
行旋转处理
 */
 while (pParent)
 {
   // 更新双亲的平衡因子
 if (pCur == pParent->_pLeft)
 pParent->_bf--;
 else
 pParent->_bf++;
 // 更新后检测双亲的平衡因子
 if (0 == pParent->_bf)
       {    
            break;
       }
 else if (1 == pParent->_bf || -1 == pParent->_bf)
 {
  // 插入前双亲的平衡因子是0,插入后双亲的平衡因为为1 或者 -1 ,说明以双亲
为根的二叉树
  // 的高度增加了一层,因此需要继续向上调整
 pCur = pParent;
 pParent = pCur->_pParent;
 }
 else
 {
 // 双亲的平衡因子为正负2,违反了AVL树的平衡性,需要对以pParent
 // 为根的树进行旋转处理
               if(2 == pParent->_bf)
             {
                  // ...
             }
              else
             {
                  // ...
             }
 }
 }
 return true;
}

4. AVL树的旋转(重点)

如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:
注意:前两者是单旋他们的模型是相同的;
后俩者是双旋他们的模型相同;
在这里插入图片描述

4.1 新节点插入较高的右子树的右侧:左单璇;

左单璇的情况是:根的左子树是单身汉(成对的少);
解决方法:将根右子树的左子树过继根的左子树的右子树;这样根的右子树就帮了根的忙;根就认了右子树为义父;
在这里插入图片描述
代码实现

	void _RotateL(Node* pParent)
	{
		Node* ppParent = pParent->_parent;
		Node* rChild = pParent->_right;
		Node* rlChild = pParent->_right->_left;
		rChild->_left = pParent;
		pParent->_right = rlChild;

		if(rlChild)//这里判断rlchild是否为空
		rlChild->_parent = pParent;
		pParent->_parent = rChild;
		if (_root == pParent)
		{
			_root = rChild;
			rChild->_parent = nullptr;
		}
		else
		{
			if (ppParent->_right == pParent)
			{
				ppParent->_right = rChild;
			}
			else
			{
				ppParent->_left = rChild;
			}
			rChild->_parent = ppParent;

		}
		pParent->_bf = rChild->_bf = 0;//这里的目的就是将者里的2和1的节点干成0;
	}

4.2 新节点插入较高左子树的左侧:右单璇;

在这里插入图片描述
代码实现

void _RotateR(Node* pParent)
	{
		Node* ppParent = pParent->_parent;
		Node* lChild = pParent->_left;
		Node* lrChild = pParent->_left->_right;
		lChild->_right = pParent;
		pParent->_left = lrChild;

		if (lrChild)//这里判断rlchild是否为空
			lrChild->_parent = pParent;
		pParent->_parent = lChild;
		if (_root == pParent)
		{
			_root = lChild;
			lChild->_parent = nullptr;
		}
		else
		{
			if (ppParent->_right == pParent)
			{
				ppParent->_right = lChild;
			}
			else
			{
				ppParent->_left = lChild;
			}
			lChild->_parent = ppParent;

		}
		pParent->_bf = lChild->_bf = 0;//这里的目的就是将者里的2和1的节点干成0;
	}

4.3(双旋) 新节点插入较高右子树的左侧—右左:先右单旋再左单旋

4.3.1添加节点位置的三种情况

在这里插入图片描述
代码实现

//主函数
else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateR(parent);
				}
//函数体
void _RotateRL(Node* pParent)
	{
		_RotateR(pParent->_right);
		_RotateL(pParent);
		Node* subR = pParent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;
		if (bf == -1)
		{
			subRL->_bf = 0;
			subR->_bf = 1;
			pParent->_bf = 0;
		}
		else if (bf == -1)
		{
			subRL->_bf = 0;
			subR->_bf = 0;
			pParent->_bf = -1;
		}
		else if (bf == 0)
		{
			subRL->_bf = 0;
			subR->_bf = 0;
			pParent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

4.4 新节点插入较高左子树的右侧—左右:先左单旋再右单旋

在这里插入图片描述

5.判断AVL树是否平衡

这里不可以使用遍历平衡因子,原因:平衡因子有可能是错的;
使用左右子树的高度想减的方法:

	//先想空
	//在使用递归
	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;
	}


	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
		{
			return true;
		}
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);
		if (rightHeight - leftHeight != root->_bf)
		{
			cout << root->_kv.first << "平衡因子异常" << endl;
			return false;
		}
		return abs(rightHeight - leftHeight) < 2
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right);

	}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

文献速递:深度学习胶质瘤诊断---使用深度学习在 MRI 图像中进行低级别胶质瘤的脑肿瘤分割和分级

Title 题目 Brain tumor segmentation and grading of lower-grade glioma using deeplearning in MRI images 使用深度学习在 MRI 图像中进行低级别胶质瘤的脑肿瘤分割和分级 01文献速递介绍 胶质瘤是最常见的脑肿瘤&#xff0c;根据肿瘤的恶性程度和生长速率具有不同的分级…

【高阶数据结构】并查集 -- 详解

一、并查集的原理 1、并查集的本质和概念 &#xff08;1&#xff09;本质 并查集的本质&#xff1a;森林。 &#xff08;2&#xff09;概念 在一些应用问题中&#xff0c;需要将 n 个不同的元素划分成一些不相交的集合。 开始时&#xff0c;每个元素自成一个单元素集合&…

车载诊断的基本框架和概念

车载诊断的基本框架和概念 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不…

Android开发——ViewPager

适配器 package com.example.myapplication; import android.view.View; import android.view.ViewGroup; import androidx.annotation.AnimatorRes; import androidx.annotation.NonNull; import androidx.viewpager.widget.PagerAdapter; import java.util.ArrayList; publi…

springboot+vue全栈开发【4.前端篇之Vue组件化开发】

目录 前言NPM使用NPM简介nodejs安装npm命令 Vue CLI使用用vue CLI创建一个vue项目 组件化开发组件的构成组件怎么用1.创建一个组件2.在父组件中使用子组件3. 传递数据给子组件4. 监听子组件事件 前言 hi&#xff0c;这个系列是我自学开发的笔记&#xff0c;适合具有一定编程基…

RHCE:网络服务综合项目

基础配置&#xff1a; 1.配置主机名&#xff0c;静态IP地址 2.开启防火墙并配置 3.部分开启SElinux并配置 4.服务器之间使用同ntp.aliyun.com进行时间同步 5.服务器之间实现SSH免密登录 业务需求&#xff1a; 1.Server-NFS-DNS主机配置NFS服务器&#xff0c;将博客网…

Visual Studio 2022 Professional、Enterprise安装教程

Visual Studio 2022 Professional、Enterprise安装教程 下载安装包安装 我是电脑已经有VS2019&#xff0c;现在加装一个VS2022。 下载安装包 首先下载安装包&#xff0c;进入官网进行下载&#xff0c;VS官网下载地址。 进入之后&#xff0c;会显示如下界面&#xff0c;选择Pro…

二、python+前端 实现MinIO分片上传

python前端 实现MinIO分片上传 一、背景二、流程图三、代码 一、背景 问题一&#xff1a;前端 -> 后端 ->对象存储 的上传流程&#xff0c;耗费带宽。 解决方案&#xff1a;上传流程需要转化为 前端 -> 对象存储&#xff0c;节省上传带宽 问题二&#xff1a;如果使用…

近期分享学习心得4

1、带有多的条件的if的语句 逻辑 || 的简写 if (x true || x 2523 || x 小明) {}// 简化操作if ([true, 2523, 小明].includes(x)) {}2、查找两个数组的交集 var numOne [0, 2, 4, 6, 8, 8]; var numTwo [1, 2, 3, 4, 5, 6]; var cross [...new Set(numOne)].filter(item…

《QT实用小工具·三十四》Qt/QML使用WebEngine展示的百度ECharts图表Demo

1、概述 源码放在文章末尾 该项目实现了百度ECharts图表的样式&#xff0c;效果demo如下所示&#xff1a; 项目部分代码如下所示&#xff1a; #include <QGuiApplication> #include <QQmlApplicationEngine> #include <QtWebEngine>int main(int argc, ch…

C++学习————第八天(C/C++内存管理)

目录 1、1.C/C内存分布 2、 C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free 3、C内存管理方式 3.1 new/delete操作内置类型 3.2 new和delete操作自定义类型 4.operator new与operator delete函数 5. new和delete的实现原理 5.1 内置类型 5.2 自定…

书生·浦语实战营第二期(七)——OpenCompass

一、OpenCompass: 上海人工智能实验室科学家团队正式发布了大模型开源开放评测体系 “司南” (OpenCompass2.0)&#xff0c;用于为大语言模型、多模态模型等提供一站式评测服务。其主要特点如下&#xff1a; 开源可复现&#xff1a;提供公平、公开、可复现的大模型评测方案全…

【JavaEE初阶系列】——网络层IP协议(地址管理和路由选择)

目录 &#x1f6a9;网络层 &#x1f388;IP协议 &#x1f469;&#x1f3fb;‍&#x1f4bb;IP协议"拆包组包"功能 &#x1f388;地址管理 &#x1f469;&#x1f3fb;‍&#x1f4bb;IP地址的分类 &#x1f469;&#x1f3fb;‍&#x1f4bb;NAT机制如何工作的…

Matlab新手快速上手2(粒子群算法)

本文根据一个较为简单的粒子群算法框架详细分析粒子群算法的实现过程&#xff0c;对matlab新手友好&#xff0c;源码在文末给出。 粒子群算法简介 粒子群算法&#xff08;Particle Swarm Optimization&#xff0c;PSO&#xff09;是一种群体智能优化算法&#xff0c;灵感来源于…

Oracle正則匹配練習一

1.使用分割符號 select regexp_substr(A_B_C, [^_], 1, 2) FROM DUAL

Android 获取手机整体流量使用情况以及某个应用的流量的统计

源代码下载地址&#xff1a; 链接&#xff1a;https://pan.quark.cn/s/b6ab9000c0bd

CSS基础:position定位的5个类型详解!

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合…

鸿蒙OpenHarmony【轻量系统编写“Hello World”程序】 (基于Hi3861开发板)

编写“Hello World”程序 下方将通过修改源码的方式展示如何编写简单程序&#xff0c;输出“Hello world”。请在下载的源码目录中进行下述操作。 前提条件 已参考鸿蒙开发指导文档&#xff1a;gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到…

QT中的OpenGL学习-----3D图形

一、3D坐标系 记住V_clip M_projection * M_view * M_model * V_local就行&#xff0c;可以在顶点着色器里面添加位置信息&#xff1a; #version 330 core layout (location 2) in vec3 aPos;//location属性位置有16个 layout (location 3) in vec3 aColor; layout (locati…

基础算法前缀和与差分

前言 本次博客会介绍一维和二维的前缀和&#xff0c;以及一维二维差分的基本使用&#xff0c;尽量画图&#xff0c;多使用配合文字 使大家理解&#xff0c;希望有所帮助吧 一维前缀和 问题描述 这里有一个长度为n的数组&#xff0c;我们要算出【2,5】区间的元素和 暴力思…