手撕二叉平衡树

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

#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树和红黑树的优缺点。期待下一篇内容吧!!谢谢大家支持!!!

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

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

相关文章

Oracle常用权限处理

对于Oracle来说&#xff0c;用户等于Schema&#xff0c;创建用户即创建Schema -- 创建用户 create user TCK_TEXT identified by "TCKTCK"; --赋予登陆权限 grant connect to TCK_TEXT; --查看权限列表 select * from user_role_privs ; select * from user_sys_priv…

postgresql-常用数学函数

postgresql-常用数学函数 案例 案例 --求余 1 select 5%2 as t; --绝对值 17.4 select abs(-17.4) as t2; -- 大于等于最小整数 -42 select ceil(-42.8) as t3; -- 小于等于的最大整数 42 select floor(42.3) as t4; -- 四舍五入 44 select round(43.6) as t5; -- 向零取整 12…

vue开发桌面exe应用

vue开发桌面exe应用 Electron-vue 参考 Electron-vue搭建vue全家桶Element UI客户端&#xff08;一&#xff09; 如何使用Vue.js构建桌面应用程序

CXL寄存器介绍(2)- CXL DVSEC

&#x1f525;点击查看精选 CXL 系列文章&#x1f525; &#x1f525;点击进入【芯片设计验证】社区&#xff0c;查看更多精彩内容&#x1f525; &#x1f4e2; 声明&#xff1a; &#x1f96d; 作者主页&#xff1a;【MangoPapa的CSDN主页】。⚠️ 本文首发于CSDN&#xff0c…

TiDB 一栈式综合交易查询解决方案获“金鼎奖”优秀金融科技解决方案奖

日前&#xff0c;2023“金鼎奖”评选结果揭晓&#xff0c; 平凯星辰&#xff08;北京&#xff09;科技有限公司研发的 TiDB 一栈式综合交易查询解决方案获“金鼎奖”优秀金融科技解决方案奖 &#xff0c; 该方案已成功运用于 多家国有大行、城商行和头部保险企业 。 此次获奖再…

【AI】《动手学-深度学习-PyTorch版》笔记(二十一):目标检测

AI学习目录汇总 1、简述 通过前面的学习,已经了解了图像分类模型的原理及实现。图像分类是假定图像中只有一个目标,算法上是对整个图像做的分类。 下面我们来学习“目标检测”,即从一张图像中找出需要的目标,并标记出位置。 2、边界框 边界框:bounding box,就是一个方…

Mysql忽略大小写问题

Mysql忽略大小写问题 一、字段忽略大小写 编码格式入手 二、表名忽略大小写 配置文件入手my.conf文件。lower_case_table_names 1

Python 面试:单元测试unit testing 使用pytest

1. 对于函数进行单元测试 calc.py def add(x, y):"""Add Function"""return x ydef subtract(x, y):"""Subtract Function"""return x - ydef multiply(x, y):"""Multiply Function""…

我想开通期权?如何开通期权账户?

场内期权的合约由交易所统一标准化定制&#xff0c;大家面对的同一个合约对应的价格都是一致的&#xff0c;比较公开透明&#xff0c;期权开户当天不能交易的&#xff0c;期权开户需要满足20日日均50万及半年交易经验即可操作&#xff0c;下文科普我想开通期权&#xff1f;如何…

Java设计模式:四、行为型模式-10:访问者模式

一、定义&#xff1a;访问者模式 访问者模式&#xff1a;核心在于同一个事物不同视角下的访问信息不同。 在一个稳定的数据结构下&#xff0c;例如用户信息、雇员信息等&#xff0c;增加易变的业务访问逻辑。为了增强扩展性&#xff0c;将两部分的业务解耦的一种设计模式。 二…

详解 SpringMVC 中获取请求参数

文章目录 1、通过ServletAPI获取2、通过控制器方法的形参获取请求参数3、[RequestParam ](/RequestParam )4、[RequestHeader ](/RequestHeader )5、[CookieValue ](/CookieValue )6、通过POJO获取请求参数7、解决获取请求参数的乱码问题总结 在Spring MVC中&#xff0c;获取请…

自建音乐播放器之一

这里写自定义目录标题 1.1 官方网站 2. Navidrome 简介2.1 简介2.2 特性 3. 准备工作4. 视频教程5. 界面演示5.1 初始化页5.2 专辑页 前言 之前给大家介绍过 Koel 音频流服务&#xff0c;就是为了解决大家的这个问题&#xff1a;下载下来的音乐&#xff0c;只能在本机欣赏&…

【pyqt5界面化工具开发-12】QtDesigner图形化界面设计

目录 0x00 前言 一、启动程序 二、基础的使用 三、保存布局文件 四、加载UI文件 0x00 前言 关于QtDesigner工具的配置等步骤&#xff08;网上链接也比较多&#xff09; 下列链接非本人的&#xff08;如果使用pip 在命令行安装过pyqt5以及tools&#xff0c;那么就可以跳过…

springboot整合SpringSecurity

先写了一个配置类 给这个访问路径&#xff0c;加上角色权限 package com.qf.config;import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; impo…

【网络编程上】

目录 一.什么是互联网 1.计算机网络的定义与分类&#xff08;了解&#xff09; &#xff08;1&#xff09;计算机网络的定义 &#xff08;2&#xff09;计算机网络的分类 ① 按照网络的作用范围进行分类 ②按照网络的使用者进行分类 2.网络的网络 &#xff08;理解&#xf…

苹果Mac系统如何优化流畅的运行?提高运行速度

Mac系统的稳定性和流畅性一直备受大家称赞&#xff0c;这也是大多数人选择Mac的原因&#xff0c;尽管如此&#xff0c;我们仍不时地对Mac进行优化、调整&#xff0c;以使其比以前更快、更流畅地运行。以下是小编分享给各位的Mac优化方法&#xff0c;记得保存哦~ 一、释放被过度…

【笔记】常用 js 函数

数组去重 Array.from(new Set()) 对象合并 Object.assign . 这里有个细节&#xff1a;当两个对象中含有key相同value不同时&#xff0c;会以 后面对象的key&#xff1a;value为准 保留小数点后几位 toFixed 注意&#xff1a; Number型&#xff0c;用该方法处理完&#xff0c;会…

Markdown Preview Plus Chrome插件使用

Markdown Preview Plus Chrome插件使用 1.插件说明2.插件下载3.插件配置4.文档样式4.1 网页显示4.2 导出PDF 系统&#xff1a;Win10 Chrome&#xff1a;113.0.5672.127 Markdown Preview Plus&#xff1a;0.7.3 1.插件说明 一般 markdown 工具自带的预览功能比较简单&#xff…

触发JVM fatal error并配置相关JVM参数

1. 絮絮叨叨 工作中&#xff0c;Java服务因为fatal error&#xff08;致命错误&#xff0c;笔者称其为jvm crash&#xff09;&#xff0c;在服务运行日志中出现了致命错误的概要信息&#xff1a; # # A fatal error has been detected by the Java Runtime Environment: # # S…

【力扣每日一题03】数组篇--移除元素

坚持日更第三天&#xff0c;习惯逐渐养成了&#xff0c;这两天都期待着每日的刷题写博客环节~ 一、题目 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必…