波奇学C++:AVL树

news2024/9/20 11:01:08

AVL解决二叉搜索树退化成链表,保证左右子树高度不差过1,尽可能接近满二叉树

AVL树的性质:高度差(平衡因子)的绝对值不超过1(-1/0/1)

平衡因子:右子树高度-左子树高度

用平衡因子控制高度

AVL树节点

class AVLTreeNode
{
	pair<K, V>_kv; //key/value
	AVLTreeNode<K, V>* _left; // 左
	AVLTreeNode<K, V>* _right;// 右
	AVLTreeNode<K, V>* _partent;// 父母
	int _bf;// 平衡因子
	AVLTreeNode(const pair<K,V>& kv)
		:_kv(kv)
		,_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_bf(0)
	{}
};

 控制逻辑:增加一个新节点cur

 parent的bf值-1,在右 ,parent的bf加1

如果parent_bf=0,则不再改变,否则继续向上调整,直到parent为0,或者为cur为根节点时。

如果parent的平衡因子为2或者-2,说明树不平衡,要进行旋转调整。

当左边高时(parent=2,cur=1)

柱子a,b,c表示高度为h的AVL树

parent->right=cur->left
cur->left=parent

对于左单旋的理解:1.根节点向左转,2.c树高度增加后a树高2,于是把c的父节点当成根节点使得c的高度方向-1,a的树的方向+1,刚好平衡

同时调整平衡因子数量和parent指针

左单旋

void RotateL(Node* parent)
	{
		Node* cur = parent->_right; //确定cur
		Node* curleft = parent->_left;
		parent->_left = curleft;
        //当h不为0时
		if (curleft)
		{
            //调整parent指针
			curleft->_partent = parent;
		}
		cur->_left = parent;
        //调整新的根节点的parent指针
		Node* ppnode = parent->_parent;
		parent->_parent = cur;
        //根节点特殊情况
		if (parent == _root)
		{
			_root = cur;
			cur->_partent = nullptr;
		}
		else
		{
            // 确定parent是ppnode的左指针还是右指针
			if (ppnode->_left==parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode_right = cur;
			}
			cur->_partent = ppnode;
		}
		parent->_bf = cur->_bf = 0;
	}

 

 

void RotateR()
{
	void RotateR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curright = cur->_right;
		parent->_left = curright;
		cur->_right = parent;

		Node* ppnode = parent->_parent;
		if (curright)
		{
			curright->_partent = parent;
		}
		parent->_parent = cur;
		if (ppnode == root)
		{
			_root = cur;
			cur->_partent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else(ppnode->_right == parent)
			{
				ppnode->_right = cur;
			}
		}
		cur->_bf = parent->_bf = 0;
		
	}
}

 双旋:我们发现左右双旋都是在都是a,c树高度发生变化,具体体现在cur和parent的平衡因子都是相差1,而如果在b树发生变化,无论是左边高还是右边高都会产生双旋问题。具体可以分为四种情况,现在以一种情况为例:

如下图是右边高的b树(现为60-b-c子树)发生改变。

 从图上来讲是90进行右旋,把高的树的树移到外面,再30进行左旋,构建平衡。
 

 

对于父节点为60的树来说,左树成为30节点的右树,右树成为90节点的左树。

所以60的节点的双旋后30,60,90的_bf值不恒为0,30,90节点会受到60的影响。

当60->_bf=1, 30,60,90 =-1 ,0,0

60->_bf=-1, 30,60,90 = 1,0,0

左高右高的区别都是60做根节点,且_bf变化是一样的。

左高:先左旋后右旋,右高,先右旋后左旋。

插入代码和旋转代码

bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(kv);
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else if (parent->_kv.first > kv.first)
   		{
			parent->_left = cur;
		}
		cur->_parent = parent;
		return true;
		// 更新节点
		while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}
			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)
				{
					RotateR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					//右高双旋
					RotateRL(parent);
				}
				else
				{
					//左高双旋
					RotateLR(parent);
				}

			}
			else
			{
				assert(false);
			}
		}
		return true; 
	}
	void RotateL(Node* parent)
	{
		Node* cur = parent->_right;
		Node* curleft = parent->_left;
		parent->_left = curleft;
		if (curleft)
		{
			curleft->_parent = parent;
		}
		cur->_left = parent;
		Node* ppnode = parent->_parent;
		parent->_parent = cur;
		if (parent == _root)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left==parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode->_right = cur;
			}
			cur->_parent = ppnode;
		}
		parent->_bf = cur->_bf = 0;
	}	void RotateR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curright = cur->_right;
		parent->_left = curright;
		cur->_right = parent;

		Node* ppnode = parent->_parent;
		if (curright)
		{
			curright->_parent = parent;
		}
		parent->_parent = cur;
		if (ppnode == _root)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode->_right = cur;
			}
		}
		cur->_bf = parent->_bf = 0;
		
	}
	//右高先右后左
	void RotateRL(Node* parent)
	{
		Node* cur = parent->_right;
		Node* curleft = cur->_left;
		int bf = curleft->_bf;
		RotateR(parent->_right);
		RotateL(parent);
		if (bf == 1)
		{
			cur->_right->_bf = 0;
			cur->_left->_bf= -1;

		}
		else if (bf == -1)
		{
			cur->_right->_bf = 0;
			cur->_left->_bf = 1;
		}
		else
		{
			;
		}
	}
	void RotateLR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curright = cur->_right;
		int bf = curright->_bf;
		RotateL(cur);
		RotateR(parent);
		if(bf == 1)
		{
			cur->_right->_bf = 0;
			cur->_left->_bf = -1;

		}
		else if(bf==-1)
		{
		cur->_right->_bf = 0;
		cur->_left->_bf = 1;
		}
		else
		{
			;
		}
	}

检验AVL树

int Height(Node* root)
	{
		if (root == nullptr)
			return true;
		int leftHight = Height(root->_left);
		int right = Height(root->_right);
		return leftHeight > rightHeight ? leftHeight + 1 : rightHeigth + 1;
	}
	bool IsBalance(Node* root)
	{
		if (root == nullptr)
			return true;
		int leftHight = Height(root->_left);
		int rightHight = Height(root->_right);
		if (rightHight-leftHight!=root->_bf)
		{
			cout<<"平衡因子异常"
		}
		return abs(rightHight - leftHight) < 2
			&& IsBalance(root->_left)
			&& IsBalance(root->_right);
	}

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

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

相关文章

Netron可视化深度学习网络结构

有时候&#xff0c;我们构建网络模型想要直观的查看网络详细结构图&#xff0c;但是苦于没有办法。但是有了Netron以后&#xff0c;我们就可以将对应的onnx模型直接可视化&#xff0c;这样不仅可以观察网络的详细结构图&#xff0c;还可以查看网络每一层的具体参数&#xff0c;…

【算法练习Day12】树的递归遍历非递归遍历

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 递归遍历前序遍历中序遍历后…

23.3 Bootstrap 框架4

1. 轮播 1.1 轮播样式 在Bootstrap 5中, 创建轮播(Carousel)的相关类名及其介绍: * 1. carousel: 轮播容器的类名, 用于标识一个轮播组件. * 2. slide: 切换图片的过渡和动画效果. * 3. carousel-inner: 轮播项容器的类名, 用于包含轮播项(轮播图底下椭圆点, 轮播的过程可以显…

[论文必备]最强科研绘图分析工具Origin(2)——简单使用教程

本篇将介绍Origin的简单使用教程。 安装教程见上篇&#xff1a;[论文必备]最强科研绘图分析工具Origin&#xff08;1&#xff09;——安装教程 目录 &#x1f4e2;一、工具栏介绍 &#x1f4e3;1.1 行 1.1.1 标准栏 1.1.2 导入栏 1.1.3 工作表数据 1.1.4 图表数据 &a…

第二章 线性表

线性表 线性表的基本概念线性表的顺序存储线性表顺序存储的类型定义线性表基本运算在顺序表上的实现顺序表实现算法的分析 线性表的链接存储单链表的类型定义线性表的基本运算在单链表上的实现 其他运算在单链表上的实现建表删除重复结点 其他链表循环链表双向循环链表 顺序实现…

【Docker】搭建 Docker 镜像仓库

文章目录 前言&#xff1a;公有仓库和私有仓库公共镜像仓库私有镜像仓库 一、搭建 Docker 镜像仓库1.1 搭建简化版的镜像仓库1.2 搭建带有图形化界面的镜像仓库1.3 配置 Docker 信任地址 二、向私有镜像仓库推送和拉取镜像2.1 推送本地镜像到私有仓库2.2 拉取私有仓库中的镜像 …

【Redis】基础数据结构-skiplist跳跃表

有序集合Sorted Set zadd zadd用于向集合中添加元素并且可以设置分值&#xff0c;比如添加三门编程语言&#xff0c;分值分别为1、2、3&#xff1a; 127.0.0.1:6379> zadd language 1 java (integer) 1 127.0.0.1:6379> zadd language 2 c (integer) 1 127.0.0.1:6379…

Swift 5.9 与 SwiftUI 5.0 中新 Observation 框架应用之深入浅出

0. 概览 Swift 5.9 一声炮响为我们带来全新的宏&#xff08;Macro&#xff09;机制&#xff0c;也同时带来了干霄凌云的 Observation 框架。 Observation 框架可以增强通用场景下的使用&#xff0c;也可以搭配 SwiftUI 5.0 而获得双剑合璧的更强威力。 在本篇博文&#xff0c…

计算机网络笔记3 数据链路层

计算机网络系列笔记目录&#x1f447; 计算机网络笔记6 应用层计算机网络笔记5 运输层计算机网络笔记4 网络层计算机网络笔记3 数据链路层计算机网络笔记2 物理层计算机网络笔记1 概述 文章前言 &#x1f497; 站在巨人的肩膀上&#xff0c;让知识的获得更加容易&#xff01…

给列起别名(关键字:as)

MySQL从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129334507?spm1001.2014.3001.5502 语法格式: select 列名1 as 别名1, 列名2 as 别名2, 列名n as 别名n from 表名; 说明&#xff1a;可以省略as&#xff0c;列名和别名之间使用空格…

力扣 -- 873. 最长的斐波那契子序列的长度

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:int lenLongestFibSubseq(vector<int>& nums) {int nnums.size();unordered_map<int,int> hash;for(int i0;i<n;i){hash[nums[i]]i;}int ret2;vector<vector<int>> dp(n,v…

关于内存对齐你需要了解的事

简介 这篇文章的目的是带你了解什么是内存对齐&#xff0c;具体的内存对齐的细节、处理方式、不同架构则不会去详细讲解&#xff0c;只做科普文用。 1、什么是内存对齐 内存对齐和数据在内存中的位置有关。内存对齐以字节为单位进行&#xff0c;一个变量的内存地址如果正好等于…

集合(容器)-List接口及实现类

容器的特征&#xff1a;①数据长度可变&#xff1b;②数据保存方式不同。 集合体系概述&#xff1a;JAVA的集合框架是由很多接口、抽象类、具体类组成。都位于java.util包中。 Java中集合类中默认可以存储任意数据类型&#xff0c;Java中的集合提供泛型机制&#xff0c;在定义…

mysql5.7停止维护时间

mysql5.7将于2023年10月停止官网支持和更新&#xff1b;老项目要准备升级&#xff0c;新项目的mysql必须是mysql8.0&#xff08;2023-10&#xff09; 官方升级咨询地址 oracle官方升级咨询地址https://go.oracle.com/LP116153?elq_mid247718&sh1518132002061316121320310…

.Net开源迁移框架FluentMigrator的使用。

在实际的开发过程中&#xff0c;经常会遇到数据库结构变动&#xff0c;比如新增表、删除表&#xff1b;已有的表新增字段&#xff0c;删除字段&#xff1b;修改字段属性等等。而且需要开发环境、测试环境和生产环境进行同步。如果使用的是EF&#xff0c;还是挺方便的。而非EF环…

Android笔记:Android 组件化方案探索与思考

组件化项目&#xff0c;通过gradle脚本&#xff0c;实现module在编译期隔离&#xff0c;运行期按需加载&#xff0c;实现组件间解耦&#xff0c;高效单独调试。 先来一张效果图 组件化初衷 APP版本不断的迭代&#xff0c;新功能的不断增加&#xff0c;业务也会变的越来越复杂…

【多模态融合】TransFusion学习笔记(2)

接上篇【多模态融合】TransFusion学习笔记(1)。 从TransFusion-L到TransFusion ok,终于可以给出论文中那个完整的框架图了&#xff0c;我第一眼看到这个图有几个疑问: Q&#xff1a;Image Guidance这条虚线引出的Query Initialization是什么意思? Q&#xff1a;图像分支中的…

flex布局与几个实例(含源码)

本文简单的说明下flex布局 有源码实例&#xff0c;后续会持续添加 flex默认主轴是横轴 容器主要有6个属性 flex-direction 决定主轴的方向 flex-direction: row | row-reverse | column | column-reverse; flex-wrap 决定是否换行 flex-wrap: nowrap | wrap | wrap-revers…

JavaAPI---replace

package daysreplace;public class ReplaceTest {public static void main(String[] args) {String str "wwxhhhhhhhhhhh333";System.out.println("替换前的字符串" str);String newstr str.replace("333", "111");System.out.prin…

(第2遍中)内存的堆空间不够 error: MSB3073 超过了 PCH 的虚拟内存范围

压缩包里打开的新工程文件&#xff0c;运行 GenerateProjectFiles.bat 后&#xff0c;再点击 .sln 文件&#xff0c;编译工程&#xff0c;编译了 1.5h 左右&#xff0c;快结束的时候报错如下&#xff1a; 编译器的堆空间不足在第2遍中编译器的堆空间不足error MSB3073: 命令“…