平衡二叉搜索树插入的实现

前言

        因为二叉搜索树在插入的时候最坏的情况可能会变成一条单一链表,从而使查找或者插入的时候消耗大量的时间。所以为了解决这一情况诞生了平衡二叉搜索树,其作用是为了减少二叉搜索树的整体高度,从而使查找插入删除的效率提高。

一、平衡二叉搜索树插入的实现思路

        平衡二叉搜索树是二叉搜索树的升级版,通过插入和删除时旋转子树的方法,提高了查找的效率。

        那么对于插入的升级将会是本节的重点内容。主要思路如下:

        a. 插入结点之后仍然是AVL树,;

        b. 插入结点之后不再满足AVL树条件,则进行调整,根据导致不平衡的原因,分为:

        (a) LL型――单旋转调整

        (b) LR型――双旋转调整

        (c)RL型――双旋转调整

        (d) RR型――单旋转调整

        那么如何确定子树需要旋转呢?我们会在树的节点中增加高度差计数,通常情况下为“右子树高度-左子树高度”。如果高度差超过2则需要进行调整。

1、插入节点后仍然是AVL树

        如果插入节点之后向上修改节点记录高度的数据仍然能够报纸平衡,那么AVL树将不需要进行调整:

图1-1 节点平衡

        节点平衡的情况很多,主要在于两种情况,插入的时候二叉树的高度没有改变。或者改变之后仍然在高度差的范围之内。

        那么在代码中我们不难看出能够分为三种情况:

        (1)没有父节点继续修改记录值。

        (2)差入的时候子树高度没有改变,就不需要继续向上修改父节点的记录值。

        (3)向上遍历的时候发现子树的高度改变了,就需要判断子树在父节点的哪一边,从而对父节点的记录值进行加减。并继续向上修改父节点记录值。

2、插入之后不是AVL树

        这种情况我们通常就需要旋转子树节点以达到旋转之后使子树仍然保持是AVL树。

        需要旋转的情况大致分为4种,左单旋、右单旋、左右双旋、右左双旋。

        那么接下来分别讲讲需要旋转的4种情况。

2.1、左单旋

        左单旋的情况出现在父节点右节点右子树超高的情况:

图1-2 需要左单旋的例图

        其中右图为n=0的特殊情况。

        左旋转就将图中的2节点改为1的父节点,b改为1的右子树,这样2的左子树是1。这样总体高度相较于插入之前没有改变都是n+2。就不需要继续向上查找父节点了。

图1-3 左单旋

2.2、右单旋

        右单旋就相当于是左单旋镜面对称之后的情况,是父节点的左节点的左子树超高需要旋转。

图1-4 右单旋

        和左单旋类似,将1移动为0的右子树,0成为1的父节点,把0的右子树b给1当成左子树。

2.3、左右双旋

        左右双旋发生在父节点的左子树超高,但是左节点的右子树更高一点。

图1-5 需要左右双旋举例图

        其中”A“为基本情况,当”n“等于0的时候会比较特殊,这个时候”-1“右子树的记录值为0。“B”、“C”是“n”不等于0的时候的两种状况,表示为0的节点记录值分别为1或者-1.

图1-6 左右双旋

        需要注意的是根据“0”节点位置的记录值的不同,旋转完成后的子节点记录值会不同。例如当“b2”更高的时候,节点“-1”的右子树会矮一截。相反如果“b1”比“b2”更高,则节点“1”的左子树会矮一截。如果从左向右看方框形态的子树,会发现他们的顺序并没有改变,只是链接的节点改变了。

2.4、右左双旋

        和左右双旋的情况正好相反。

 图1-7 右左双旋

        和左右双旋一样,根据节点“2”的记录值不同,旋转完成后子节点的记录值也会不同。如果记录值为0,那么“1”和“3”节点的记录值都为0,如果为1则节点“1”的记录值为-1,如果为-1则节点“3”的记录值为1。

二、平衡二叉搜索树实现

#include <iostream>
#include <string>
#include <assert.h>
#include <utility>
#include <algorithm>
#include <cmath>using namespace std;template<class T>
struct AVLTreeNode
{AVLTreeNode(const T& data = T()): _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr), _data(data), _bf(0){}AVLTreeNode<T>* _pLeft;AVLTreeNode<T>* _pRight;AVLTreeNode<T>* _pParent;T _data;int _bf;   // 节点的平衡因子
};// AVL: 二叉搜索树 + 平衡因子的限制
template<class T>
class AVLTree
{typedef AVLTreeNode<T> Node;
public:AVLTree(): _pRoot(nullptr){}// 在AVL树中插入值为data的节点bool Insert(const T& data){// 根节点为空直接插入并修改if(nullptr == _pRoot){_pRoot = new Node(data);return true;}Node* parent = nullptr;Node* cur = _pRoot;// 找到对应的插入节点while(cur){parent = cur;if(data < cur->_data){cur = cur->_pLeft;}else if(data > cur->_data){cur = cur->_pRight;}else{return false;}}// 插入节点cur = new Node(data);if(data < parent->_data){parent->_pLeft = cur;cur->_pParent = parent;}else if(data > parent->_data){parent->_pRight = cur;cur->_pParent = parent;}else{assert(false);return false;}// AVL树向上检查,修改平衡因子while(parent){// 修改平衡因子if(parent->_pLeft == cur){parent->_bf--;}else if(parent->_pRight == cur){parent->_bf++;}else{// 节点存储的有问题assert(false);}// 考虑是否旋转子树// 1. 为0不需要继续向上修改if(0 == parent->_bf){break;}// 2. 等于1或者-1需要继续向上修改平衡因子else if(1 == parent->_bf || -1 == parent->_bf){cur = parent;parent = parent->_pParent;}// 3. 因子等于2或者-2就说明需要旋转else if(-2 == parent->_bf || 2 == parent->_bf){// 继续分为4种情况// 3.1. 左子树的左子树超高if(-2 == parent->_bf && -1 == cur->_bf){RotateR(parent);}// 3.2. 右子树的右子树超高else if(2 == parent->_bf && 1 == cur->_bf){RotateL(parent);}// 3.3. 左子树的右子树超高else if(-2 == parent->_bf && 1 == cur->_bf){RotateLR(parent);}// 3.4. 右子树的左子树超高else if(2 == parent->_bf && -1 == cur->_bf){RotateRL(parent);}break;}}return true;}// AVL树的验证bool IsAVLTree(){return _IsAVLTree(_pRoot).second;}private:// 根据AVL树的概念验证pRoot是否为有效的AVL树pair<int, bool> _IsAVLTree(Node* pRoot){if(pRoot == nullptr){return make_pair(0, true);}pair<int, bool> lson = _IsAVLTree(pRoot->_pLeft);cout << pRoot->_data << " ";pair<int, bool> rson = _IsAVLTree(pRoot->_pRight);int hight = max(rson.first, lson.first) + 1;bool Is = rson.second && lson.second && abs(rson.first - lson.first) <= 1;return make_pair(hight, Is);}// size_t _Height(Node* pRoot)// {//     if(nullptr == pRoot)//     {//         return 0;//     }//     else{//         return max<size_t>(_Height(pRoot->_pLeft), _Height(pRoot->_pRight)) + 1;//     }// }// 右单旋void RotateR(Node* pParent){Node* SubL = pParent->_pLeft;Node* SubLR = SubL->_pRight;Node* parent = pParent->_pParent;pParent->_pLeft = SubLR;pParent->_pParent = SubL;if(SubLR)SubLR->_pParent = pParent;SubL->_pRight = pParent;SubL->_pParent = parent;if(pParent == _pRoot){_pRoot = SubL;}else{if(parent->_pLeft == pParent){parent->_pLeft = SubL;}else if(parent->_pRight == pParent){parent->_pRight = SubL;}else{assert(false);}}pParent->_bf = SubL->_bf = 0;}// 左单旋void RotateL(Node* pParent){Node* SubR = pParent->_pRight;Node* SubRL = SubR->_pLeft;Node* parent = pParent->_pParent;pParent->_pRight= SubRL;pParent->_pParent = SubR;if(SubRL)SubRL->_pParent = pParent;SubR->_pLeft = pParent;SubR->_pParent = parent;if(pParent == _pRoot){_pRoot = SubR;}else{if(parent->_pLeft == pParent){parent->_pLeft = SubR;}else if(parent->_pRight == pParent){parent->_pRight = SubR;}else{assert(false);}}pParent->_bf = SubR->_bf = 0;}// 右左双旋void RotateRL(Node* pParent){Node* SubR = pParent->_pRight;Node* SubRL = SubR->_pLeft;int bf = SubRL->_bf;RotateR(SubR);RotateL(pParent);if(0 == bf){SubR->_bf = SubRL->_bf = pParent->_bf = 0;}else if(-1 == bf){SubRL->_bf = pParent->_bf = 0;SubR->_bf = 1;}else if(1 == bf){SubRL->_bf = SubR->_bf = 0;pParent->_bf = -1;}else{assert(false);}}// 左右双旋void RotateLR(Node* pParent){Node* SubL = pParent->_pLeft;Node* SubLR = SubL->_pRight;int bf = SubLR->_bf;RotateL(SubL);RotateR(pParent);if(0 == bf){SubL->_bf = SubLR->_bf = pParent->_bf = 0;}else if(1 == bf){SubLR->_bf = pParent->_bf = 0;SubL->_bf = -1;}else if(-1 == bf){SubLR->_bf = SubL->_bf = 0;pParent->_bf = 11;}else{assert(false);}}private:Node* _pRoot;
};

        需要注意的是旋转之后改变了节点的顺序,那么需要重新连接父节点或者改变根节点。

三、测试

#include "AVLtree.hpp"int main()
{AVLTree<int> avltree;for(int i = 0; i < 100; ++i){if(i == 2){int j = 0;}avltree.Insert(i);}if(avltree.IsAVLTree()){cout << "avltree是二叉搜索树" << endl;}else{cout << "avltree不是二叉搜索树" << endl;}return 0;
}

作者结语

        平衡二叉搜索树的难度在于旋转的时候很容易转晕,序结合图形仔细编写代码。容易出错的点还在于不要把“=”写成“==”,不然带代码出错了找半天不知道在哪错了。

        以此类推平衡二叉搜索树的删除大致也需要这么几种状况。父节点记录值等于1或者-1不需要旋转,父节点的节点为0,继续向上修改记录值,父节点的记录值为2或者-2,就需要旋转了。

        如果有时间,或者有小伙伴有需要的话,下一节就将平衡二叉搜索树的删除实现出来。

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

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

相关文章

Sublime Text4的下载安装以及汉化

sublime官网&#xff1a;https://www.sublimetext.com/ 按照指示一步步操作即可 汉化操作&#xff1a; 等一会就会弹出搜索框&#xff0c; 帮助菜单这里可以切换语言&#xff0c;

【C++报错已解决】std::bad_alloc

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

react项目中接入(集成)prettier

目的 让我们的代码不管经过多少人的改动&#xff0c;始终都会保持同样的编写格式&#xff0c;保证多人开发时&#xff0c;大家使用的按键格式都是一样的 prettier官网 安装 固定版本安装 --save-exact 记录该软件包的精确版本号(官方推荐的安装方式) npm i -D --save-exact…

常见组件详解(六):torch.nn.AvgPool2d()、torch.nn.AdaptiveAvgPool2d()

文章目录 一、torch.nn.AvgPool2d()&#xff1a;二维平均池化1.1参数介绍1.2代码案例 二、torch.nn.AdaptiveAvgPool2d()&#xff1a;自适应平均池化2.1参数介绍2.2代码案例2.3使用场景 一、torch.nn.AvgPool2d()&#xff1a;二维平均池化 1.1参数介绍 torch.nn.AvgPool2d( k…

第T2周:TensorFlow实现彩色图片分类(CIFAR10数据集),并实现自己的真实图片分类

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目标&#xff1a; 加载CIFAR-10数据集进行训练&#xff0c;然后能够对彩色图片进行分类 具体实现&#xff1a; &#xff08;一&#xff09;环境&#xff1a; 语…

Python | Leetcode Python题解之第442题数组中重复的数据

题目&#xff1a; 题解&#xff1a; class Solution:def findDuplicates(self, nums: List[int]) -> List[int]:ans []for x in nums:x abs(x)if nums[x - 1] > 0:nums[x - 1] -nums[x - 1]else:ans.append(x)return ans

可以免费制作表情包的AI工具来了!

一直想自己制作一套表情包&#xff0c;但一直没有找到好用的工具&#xff0c;要么就是太麻烦&#xff0c;要么就是不免费。 今天AI表情包免费制作工具来了&#xff0c;手机就可以直接做表情包&#xff0c;非常方便。 先看效果~ 工具用到的是通义APP&#xff0c;可以在频道中找…

一款革命性的AI写作工具——文字游侠AI大模型重大升级,创作效率提高高达20倍,小白也能轻松实现月入过万!

在自媒体创作的浪潮中&#xff0c;如何高效地生产高质量内容成为许多创作者的难题。然而&#xff0c;随着AI技术的飞速发展&#xff0c;这一难题得到了完美的解决。今天&#xff0c;我要为大家介绍一款革命性的AI写作工具——文字游侠AI大模型&#xff0c;它不仅能够大幅提高创…

Rust赋能前端:为WebAssembly 瘦身

❝ 凡事你一旦接纳了&#xff0c;就不存在了&#xff1b;你看不惯它&#xff0c;它就一直折磨你 大家好&#xff0c;我是柒八九。一个专注于前端开发技术/Rust及AI应用知识分享的Coder ❝ 此篇文章所涉及到的技术有 WebAssembly Rust SIMD LLVM binaryen 因为&#xff0c;行文字…

Llama 3.1 技术研究报告-5

5.3 人工评测 除了在标准基准测试集上的评估外&#xff0c;我们还进⾏了⼀系列⼈类评估。这些评估使我能够测量和优化模型性能的更微妙⽅⾯&#xff0c;例如模型的语调、冗⻓性和对细微差别及⽂化背景的理解。精⼼设计的⼈类评估密切反映了⽤⼾体验&#xff0c;提供了模型在现…

PacketSender使用说明

1、Packet Sender介绍 Packet Sender是一个开源实用程序&#xff0c;允许发送和接收TCP、UDP和SSL&#xff08;加密TCP&#xff09;数据包&#xff0c;以及HTTP/HTTPS请求和面板生成。主线分支正式支持Windows、Mac和桌面Linux&#xff08;带Qt&#xff09;。其他地方可能会重…

隧道灯光远程控制系统的设计与实现(论文+源码)_kaic

摘要 随着互联网的发展&#xff0c;物联网的时代己经到来。无线控制技术的应用已经普及到了我们生活中的各个角落。节能环保的意识也在不断的加强&#xff0c;隧道照明作为隧道建设的一个主要的环节&#xff0c;一个好的隧道照明系统不仅仅能保障隧道车辆的正常通行&#xff0c…

无需科学!Copilot网页版GPT-4无限制对话来了!

之前本公众号讲过&#xff1a; 微软copilot分为免费版copilot、个人家庭版copilot pro&#xff08;每月20刀&#xff09;和商业版copilot for Microsoft 365&#xff08;每月30刀&#xff09;。 其中免费版和个人家庭版的copilot无论在任何情况下使用都需要科学手段。 商业版…

什么是前缀索引?

什么是前缀索引&#xff1f; 1、什么是前缀索引&#xff1f;2、为什么要使用前缀索引&#xff1f;3、如何选择前缀长度&#xff1f;4、创建前缀索引的SQL语法5、示例 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 在处理包含长字符串的数据…

3款照片人物开口说话AI工具,跟真人说话一样~免费!短视频带货必备!(附教程)

大家好&#xff0c;我是画画的小强 今天给大家分享一个AI图片口播数字人讲认知思维&#xff0c;单号佣金赚5W的AI带货信息差玩法&#xff0c;许多小伙伴表示对这类AI带货玩法感兴趣。 说实话&#xff0c;现在AI照片人物对口型工具&#xff0c;越来越逼真&#xff0c;很难辨识出…

8.使用 VSCode 过程中的英语积累 - Help 菜单(每一次重点积累 5 个单词)

前言 学习可以不局限于传统的书籍和课堂&#xff0c;各种生活的元素也都可以做为我们的学习对象&#xff0c;本文将利用 VSCode 页面上的各种英文元素来做英语的积累&#xff0c;如此做有 3 大利 这些软件在我们工作中是时时刻刻接触的&#xff0c;借此做英语积累再合适不过&a…

牛犇啊!LSTM+Transformer炸裂创新,精准度高至95.65%!

【LSTMTransformer】作为一种混合深度学习模型&#xff0c;近年来在学术界和工业界都受到了极大的关注。它巧妙地融合了长短期记忆网络&#xff08;LSTM&#xff09;在处理时序数据方面的专长和Transformer在捕捉长距离依赖关系上的优势&#xff0c;从而在文本生成、机器翻译、…

做中视频计划,哪里找素材?推荐几个热门中视频素材下载网站

在做中视频计划时&#xff0c;寻找合适的素材至关重要。抖音上那些热门的中视频素材都是从哪里下载的呢&#xff1f;以下五大高清素材库值得收藏&#xff0c;赶紧来看看吧&#xff01; 蛙学网 蛙学网提供了百万级的中视频素材&#xff0c;质量高且是4K高清无水印&#xff0c;视…

Android使用RecyclerView仿美团分类界面

RecyclerView目前来说对大家可能不陌生了。由于在公司的项目中&#xff0c;我们一直用的listview和gridview。某天产品设计仿照美团的分类界面设计了一个界面&#xff0c;我发现用gridview不能实现这样的效果&#xff0c;所以就想到了RecyclerView&#xff0c;确实是一个很好的…

(最新已验证)stm32 + 新版 onenet +dht11+esp8266/01s + mqtt物联网上报温湿度和控制单片机(保姆级教程)

物联网实践教程&#xff1a;微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——汇总 前言 之前在学校获得了一个新玩意&#xff1a;ESP-01sWIFI模块&#xff0c;去搜了一下这个小东西很有玩点&#xff0c;远程控制LED啥的&#xff0c;然后我就想…