当前位置: 首页 > news >正文

手撕二叉平衡树

今天给大家带来的是平衡树的代码实现,如下:

#pragma once 
#include <iostream>
#include <map>
#include <set>
#include <assert.h>
#include <math.h>
using namespace std;
namespace cc
{template<class K, class V>struct AVLnode{int _bf = 0;pair<K, V> _val;AVLnode<K, V>* _left;AVLnode<K, V>* _right;AVLnode<K, V>* _parent;AVLnode(const pair<K, V>& val = pair<K, V>(), AVLnode<K, V>* left = nullptr, AVLnode<K, V>* right = nullptr, AVLnode<K, V>* parent = nullptr): _val(val), _left(left), _right(right), _parent(parent){}};template<class K, class V>class AVL{public:typedef AVLnode<K, V> node;//此parent其实就相当于是旋转点void revolveL(node* parent){//需要改变的节点node* sub = parent->_right;node* subl = sub->_left;//如果根节点是parentif (_root == parent){_root = sub;sub->_parent = nullptr;sub->_left = parent;parent->_parent = sub;parent->_right = subl;if (subl)subl->_parent = parent;}//此旋转点不是根节点else{node* pparent = parent->_parent;if (pparent->_left == parent)pparent->_left = sub;elsepparent->_right = sub;sub->_parent = pparent;sub->_left = parent;parent->_parent = sub;parent->_right = subl;if (subl)subl->_parent = parent;}//旋转完成,更新平衡因子sub->_bf = 0;parent->_bf = 0;}void revolveR(node* parent){node* sub = parent->_left;node* subr = sub->_right;if (_root == parent){_root = sub;sub->_parent = nullptr;sub->_right = parent;parent->_parent = sub;parent->_left = subr;if (subr)subr->_parent = parent;}else{node* pparent = parent->_parent;if (pparent->_left == parent)pparent->_left = sub;elsepparent->_right = sub;sub->_parent = pparent;sub->_right = parent;parent->_parent = sub;parent->_left = subr;if (subr)subr->_parent = parent;}sub->_bf = 0;parent->_bf = 0;}bool insert(const pair<K, V>& x){//根节点为空if (_root == nullptr){_root = new node(x);//节点中父指针已经指向nullptrreturn true;}node* parent = nullptr;node* cur = _root;while (cur){if (x.first < (cur->_val).first){parent = cur;cur = cur->_left;}else if (x.first > (cur->_val).first){parent = cur;cur = cur->_right;}elsereturn false;}cur = new node(x);if ((parent->_val).first > x.first)parent->_left = cur;elseparent->_right = cur;cur->_parent = parent;//插入完成,更新平衡因子while (parent){if (parent->_right == cur)parent->_bf++;else if (parent->_left == cur)parent->_bf--;//此情况说明cur既不是做孩子也不是右孩子,所以直接报错,说明插入的时候出现了问题elseassert(false);if (abs(parent->_bf) == 0)break;else if (abs(parent->_bf) == 1){cur = parent;parent = parent->_parent;}else if (abs(parent->_bf) == 2){//旋转if (parent->_bf == 2 && cur->_bf == 1){revolveL(parent);break;}else if (parent->_bf == -2 && cur->_bf == -1){revolveR(parent);break;}else if (parent->_bf == -2 && cur->_bf == 1){node* sub = parent->_left;node* subr = sub->_right;int bf = subr->_bf;revolveL(sub);revolveR(parent);if (bf == 0){parent->_bf = 0;sub->_bf = 0;subr->_bf = 0;}else if (bf == 1){sub->_bf = -1;parent->_bf = 0;subr->_bf = 0;}else if (bf == -1){sub->_bf = 0;parent->_bf = 1;subr->_bf = 0;}elseassert(false);break;}else if (parent->_bf == 2 && cur->_bf == -1){node* sub = parent->_right;node* subl = sub->_left;int bf = subl->_bf;revolveR(sub);revolveL(parent);subl->_bf = 0;if (bf == 0){// subl->_bf = 0;sub->_bf = 0;parent->_bf = 0;}else if (bf == 1){sub->_bf = 0;// subl->_bf = 0;parent->_bf = -1;}else if (bf == -1){sub->_bf = 1;//subl->_bf = 0;parent->_bf = 0;}elseassert(false);break;}//如果走到这步,说明这棵树的平衡因子有问题elseassert(false);}elseassert(false);}return true;}int high(){return _high(_root);}bool check(){return _check(_root);}private:node* _root = nullptr;bool _check(node* root){if (root == nullptr)return true;int bf = _high(root->_right) - _high(root->_left) ;if (bf != root->_bf){cout << "bf:不一样" << endl;return false;}cout << "bf:" << bf <<" " << "root:" << (root->_val).first << endl;return _check(root->_left) && _check(root->_right);}int _high(node *root){if (root == nullptr)return 0;int left = _high(root->_left);int right = _high(root->_right);if (left > right)return left + 1;elsereturn right + 1;}};
}

这个仅仅是平衡树的插入,其实二叉平衡树的插入并不难,逻辑就是那么几个,但是难得是细节处的实现,尤其是平衡因子更新的那一块,特别的容易弄混人,只要记住四种模型就可以了。两种单旋,两种双旋,还是有点难以理解的,下面一一讲解

1.左单旋

左单旋大概的图示这样子的:

如上图,如果没有插入100,那么此时的二叉平衡树是平衡的,但是此时如果插入100,此时30这个节点的平衡因子是2,所以此时需要旋转来降低这棵树的高度,此时就是左旋。其实此处还分为好几种情况,但是这几种情况都是一样的旋转方法,因为都在60这个节点的右子树中,所以此时就把30当做旋转点,让60左旋,在把60这个节点的左子树链接到30的右指针处就好了。

2.右单旋

和左单旋一样,因为新插入的节点导致30这个节点的平衡因子为-2,所以此时就要旋转来降低这棵树的高度,此时是要右旋,这个和左旋一样,30是旋转点,25进行右旋,把25这个节点的右子树连接到30这个节点的左子树处就可以了。

3.先左旋,在右旋

这个就是先左旋,在右旋,也是插入了新的节点导致的。这里没有写节点具体的值是因为,因为40这个节点的右子树或是左子树中的值不确定,所以就用了一个空节点来代替插入的值。其实就是两次单旋的结果,先把40以30为旋转点进行左旋,再把60当旋转点进行右旋,此时就旋转完成。而30与40的子树怎么连接参考左单旋与右单旋的旋转方式

4.先右旋,在左旋

这个模型就是先右旋,在左旋。先把60当旋转点进行旋转,再把30当旋转点进行旋转,至于子树怎么连接,与先左旋,在右旋相同。

以上就是平衡二叉树的旋转方式,下面来总结一下思路:

1.与二叉搜索树的插入一样

2.链接parent指针

3.更新平衡因子

4.如果左右不平衡,那么就开始旋转

增删查时间复杂度:

首先我们知道的是,他是一个二叉树,所以他的高度是log n,而我们在增删查的时候,我们最坏的结果就是要查叶子结点或者所查的节点不在此树,此时它的时间复杂度就是log n,但是他的空间复杂度也是logn,所以个人认为他是以空间换区时间的一种数据结构,但是他的效率确实很高,而对于我们所说的红黑树,其实严格意义来说也是一种logn的算法,但是他没有平衡二叉树这么严谨,平衡二叉树旋转的次数比较多,但是红黑树却没有,旋转其实也是一种消耗,但是旋转的时间复杂度是O(1),严格来说,有消耗,但是也没那么严重,但是对于红黑树来说,就是尽可能不旋转,减少这种消耗。

对于红黑树的代码以及讲解,下一篇会详细讲解,也会对比AVL树和红黑树的优缺点。期待下一篇内容吧!!谢谢大家支持!!!

相关文章:

手撕二叉平衡树

今天给大家带来的是平衡树的代码实现&#xff0c;如下&#xff1a; #pragma once #include <iostream> #include <map> #include <set> #include <assert.h> #include <math.h> using namespace std; namespace cc {template<class K, clas…...

超图嵌入论文阅读1:对偶机制非均匀超网络嵌入

超图嵌入论文阅读1&#xff1a;对偶机制非均匀超网络嵌入 原文&#xff1a;Nonuniform Hyper-Network Embedding with Dual Mechanism ——TOIS&#xff08;一区 CCF-A&#xff09; 背景 超边&#xff1a;每条边可以连接不确定数量的顶点 我们关注超网络的两个属性&#xff1…...

Qt xml解析之QXmlStreamReader

文章目录 背景QXmlStreamReader简单介绍使用QXmlStreamReader添加头文件<QXmlStreamReader>toString()toInt()完整代码 背景 项目中遇到需要解析某个方法返回的xml字符串&#xff0c;奈何C/C中没有原生的方法可供调用&#xff0c;只能使用第三方库&#xff0c;搜了一圈资…...

Selenium基础 — CSS选择器定位大全

1、css属性定位 css选择器策略示例说明#id#telA选择id"telA"的所有元素。.class.telA选择 class"telA”的所有元素。[属性名属性值][nametelA]除了id和class属性&#xff0c;其他属性的定位格式[attribute][target]选择带有target 属性所有元素。**选择所有元素…...

vue3中keep-alive的使用及结合transition使用

正确用法 在组件中使用&#xff08;这里结合了 transition 内置动画组件 &#xff09; <template><div class"layout clearfix"><router-view v-slot"{ Component, route }"><transition name"fade-transform" mode"…...

【提示工程】询问GPT返回Json结构数据

theme: orange 众所周知&#xff0c;我们可以通过构建的Prompt获取期望的内容&#xff0c;但是通常都是以自然语言返回的&#xff0c;假如我们想得到结构化的数据&#xff0c;比如Json&#xff0c;XML那么怎么办&#xff0c;这篇文章给你一个思路。 理所当然的想法 要实现询问大…...

CSS水平垂直居中方案

1 前言 水平居中、垂直居中是前端面试百问不厌的问题。其实现方案也是多种多样&#xff0c;常叫人头昏眼花。 水平方向可以认为是内联方向&#xff0c;垂直方向认为是块级方向。 2 内联元素的水平垂直居中 首先&#xff0c;常见内联元素有&#xff1a;a、span、em、b、stro…...

SpringBoot入门篇3 - 整合junit、整合mybatis、基于SpringBoot实现ssm整合

目录 1.整合JUnit Spring整合JUnit SpringBoot整合JUnit 测试类注解&#xff1a;SpringBootTest 作用&#xff1a;设置JUnit加载的SpringBoot启动类 2.整合mybatis ①使用spring initializr初始化项目的时候&#xff0c;添加依赖。 ②设置数据源application.yml spring:d…...

C#,《小白学程序》第七课:列表(List)应用之一“编制高铁车次信息表”

1 文本格式 /// <summary> /// 车站信息类 class /// </summary> public class Station { /// <summary> /// 编号 /// </summary> public int Id { get; set; } 0; /// <summary> /// 车站名 /// </summary>…...

周报/月报 Prompt

前言 用 AI 写好一份周报或月报。 文章目录 前言一、目的二、Prompt 设计原则三、模板 一、目的 简单的日程&#xff0c;扩写成一篇高质量的周报&#xff1b; 二、Prompt 设计原则 角色 目标 背景 要求 三、模板 内容生成模板 你是我的周报助手&#xff0c;根据我的工作…...

c++ 学习 之 构造函数的分类和调用类型 深入学习

正文 构造函数是在C中用于创建和初始化对象的特殊函数。构造函数可以根据不同的特性和参数进行分类&#xff0c;以下是一些常见的构造函数分类和详细讲解它们的调用方式&#xff1a; 默认构造函数&#xff1a; 默认构造函数是一个特殊的构造函数&#xff0c;它没有参数&#x…...

Royal TSX 6 Mac多协议远程软件

Royal TSX是一款功能强大的远程桌面管理软件&#xff0c;适用于Mac操作系统。它允许用户通过一个集成的界面来管理和访问多个远程计算机和服务器。 Royal TSX支持多种远程协议&#xff0c;包括RDP、VNC、SSH、Telnet和FTP等&#xff0c;可以方便地连接到Windows、Linux、Mac和其…...

远程管理通道安全SSH协议主机验证过程

可以使用SSH协议进行远程管理通道安全保护&#xff0c;其中涉及的主要安全功能包括主机验证、数据加密性和数据完整性保护。 这里要注意的是【主机验证】和【身份验证】的区别&#xff0c;主机验证是客户端确认所访问的服务端是目标访问对象&#xff0c;比如从从客户端A(192.16…...

.NET 操作 TDengine .NET ORM

TDengine 是国内比较流的时序库之一&#xff0c;支持群集并且免费&#xff0c;在.NET中资料比较少&#xff0c;这篇文章主要介绍SqlSugar ORM来操作TDengine 优点&#xff1a; 1、SqlSugar支持ADO.NET操作来实现TDengine&#xff0c;并且支持了常用的时间函数、支持联表、分…...

SQL Server对象类型(3)——视图(View)

1. 视图概念 与Oracle中的视图类似,SQL Server中的视图也是一种虚的、通过一个查询定义的逻辑对象,主要用于集中、简化、定制用户需求,控住其底层表安全,以及应用系统提供向后兼容等方面。 --注: 1)上述内容中的“虚的”,表示视图本身并不实际包含和存储数据,SQL Ser…...

【LeetCode】剑指 Offer <二刷>(1)

目录 前言&#xff1a; 题目&#xff1a;剑指 Offer 03. 数组中重复的数字 - 力扣&#xff08;LeetCode&#xff09; 题目的接口&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 写在最后&#xff1a; 前言&#xff1a; …...

MySQL事物和存储引擎

事务 一、MySQL事务的概念 事务是一种机制、一个操作序列&#xff0c;包含了一组数据库操作命令&#xff0c;并且把所有的命令作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这一组数据库命令要么都执行&#xff0c;要么都不执行。 事务是一个不可分割的工作逻辑单…...

代码随想录算法训练营Day51 | 309. 最佳买卖股票时机含冷冻期 | 714. 买卖股票的最佳时机含手续费 | 股票总结

文章目录 309. 最佳买卖股票时机含冷冻期标准 dp机智的分析解法 714. 买卖股票的最佳时机含手续费贪心算法 股票总结 309. 最佳买卖股票时机含冷冻期 题目链接 | 解题思路 标准 dp 本题多了冷却期的条件&#xff0c;将原本的两个状态变得更复杂了。变化在于&#xff0c;如果…...

C#,《小白学程序》第八课:列表(List)应用之二“编制高铁列车时刻表”

1 文本格式 /// <summary> /// 车站信息类 class /// </summary> public class Station { /// <summary> /// 编号 /// </summary> public int Id { get; set; } 0; /// <summary> /// 车站名 /// </summary&g…...

2、QT的信号与槽

一、什么是信号与槽 一个对象发送一个信号出去&#xff0c;另外一个对象接收到该信号后&#xff0c;会触发相应的槽函数 二、信号与槽的语法 connect(信号的发送者&#xff0c;SIGNAL(信号名称),信号的接收者,SLOT(槽函数)); 1、写法&#xff1a; QT 4 的写法 connect(sende…...

Java代码审计15之Apache log4j2漏洞

文章目录 1、log4j简介2、复现2.1、高版本测试2.2、测试代码2.3、补充之dns探测2.3.1、rmi、ldap也可以dnslog探测 2.3.2、dnslog外带信息 3、漏洞原理3.1、漏洞的危害大的背景3.2、具体的代码调试 4、靶场测试4.1、dns探测4.2、工具下载与使用4.3、测试4.4、手工可以测出&…...

c语言每日一练(13)

前言&#xff1a;每日一练系列&#xff0c;每一期都包含5道选择题&#xff0c;2道编程题&#xff0c;博主会尽可能详细地进行讲解&#xff0c;令初学者也能听的清晰。每日一练系列会持续更新&#xff0c;上学期间将看学业情况更新。 五道选择题&#xff1a; 1、程序运行的结果…...

H5 + C3基础(六)(2D转换transform 位移 旋转 缩放)

2D转换transform & 2D转换transform平移利用平移百分比优化盒子水平垂直居中 旋转指定2d变换的中心点 transform-origin 缩放2d转换简写 2D转换transform 所谓2D转换&#xff0c;就是在二维坐标系内进行各种操作&#xff0c;包括平移&#xff0c;转动&#xff0c;缩放等等…...

2023最新 Electron.js 桌面应用开发教程(基础篇)更新中

Electron是什么&#xff1f; Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面应用程序的框架。 嵌入 Chromium 和 Node.js 到 二进制的 Electron 允许您保持一个 JavaScript 代码代码库并创建 在Windows上运行的跨平台应用 macOS和Linux Electron Fiddle 运行实例 Ele…...

【ES】笔记-Set集合实践

JS <script>let arr[1,2,3,4,5,4,3,2,1];//1.数组去重let result0[...new Set(arr)];console.log(数组去重${result0});//2.交集let arr2[4,5,6,5,6];let result[...new Set(arr)].filter(item>{let s2new Set(arr2);//4 5 6if(s2.has(item)){return true;}else{retur…...

缺陷或负样本难以收集怎么办?使用生成式模型自动生成训练样本,image-to-image Stable diffusion

文章大纲 样本稀疏与对应的解决方案如何解决工业缺陷检测小样本问题参考1:AIDG(Artificial Intelligent Defect Generator)参考2:灵感来源 : Image-to-Image Diffusion Models参考文献与学习路径参考博文数据集算法缺陷检测库hugging face样本稀疏与对应的解决方案 1.数据层面…...

ZMTP协议

ZoreMQ Transport Protocol是一个传输层协议&#xff0c;用于ZMQ的连接的信息交互&#xff0c;本文档描述的是3.0协议&#xff0c;主要分析基于NULL Security Mechanism 协议语法 ZMTP由三部分组成&#xff0c;分别是 greeting、handshake、traffic 部分描述构成greeting描述…...

ubuntu18安装中文环境

1. 安装中文语言包 首先&#xff0c;我们需要安装中文语言包。打开终端&#xff0c;输入以下命令&#xff1a; sudo apt-get install language-pack-zh-hans 这个命令会下载并安装中文语言包。安装完成后&#xff0c;我们需要重新启动系统(reboot)。 2. 安装中文输入法 安…...

怎么提取视频中的音乐保存到本地?其实方法很简单

当你想要使用视频中的音乐时&#xff0c;你可以考虑将它从视频中提取出来。这可以用于制作音频样本集&#xff0c;制作铃声或其他音频素材&#xff0c;或者向其他人展示视频的音乐部分而无需显示视频本身。如果你是一位音乐制作人员&#xff0c;你可能会需要一些特定类型的音效…...

线性代数的学习和整理18:矩阵的秩的各种定理, 秩和维度(未完成)

目录 1 矩阵的秩 矩阵的秩 2 求秩的方法 矩阵的维度秩 矩阵的维度 向量的模&#xff0c;矩阵的模-没有把&#xff0c;难道是面积&#xff1f; 矩阵的平直概念 5 矩阵的初等变换&#xff08;矩阵等价概念的引出&#xff09; 1 为什么要引入矩阵的“秩” 这个概念&#x…...