一文学会二叉搜索树,AVL树,红黑树

文章目录

  • 二叉搜索树
    • 查找
    • 插入
    • 删除
  • AVL树
    • 概念
    • 插入
    • 旋转
    • AVL验证
  • 红黑树
    • 概念
    • 插入
    • 检测

二叉搜索树

也称二叉排序树二叉查找树

二叉搜索树可以为空,若不为空满足以下性质
⭐1,非空左子树小于根节点的值
⭐2,非空右子大于根节点的值
⭐3,左右子树都是二叉搜索树
在这里插入图片描述

二叉搜索树节点代码:

template<class K>
struct TreeNode
{TreeNode<K>* _left;TreeNode<K>* _right;K _key;TreeNode(const K& key):_left(nullptr),_right(nullptr),_key(key){}
};

查找

a,大于根节点向右找小于向左找
b,最多查找高度次,若走到空了,说没没有这个数。

bool find(const K& key)
{Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_right;}else if (cur->_key > key){cur = cur->_left;}elsereturn true;}return false;

插入

a, 若树是空=
=,新增节点,将节点赋值给_root;
b, 若树不为空,按
二叉搜索树性质==找到插入位置,插入
在这里插入图片描述
代码实现

bool Insert(const K& key)
{if (_root == nullptr){_root = new Node(key);return true;}Node* cur = _root;Node* parent = _root;while (cur){if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}elsereturn false;}cur = new Node(key);if (cur->_key > parent->_key){parent->_right = cur;}else{parent->_left = cur;}return true;
}

删除

删除要分4种情况:
a, 删除节点无左右子树
b, 删除节点只有左子树
c, 删除节点只有右子树
d, 删除节点左右子树都有

实际情况中,a可以和b/c结合起来,因此情况有三种

b让父亲节点指向左子树(两种情况,要删除的节点是父亲的左节点还是右节点),删除节点
c让父亲节点指向右子树,删除节点
d:找到左子树最大节点与根节点值替换❤因为最大,所以该节点无左子树,或只能有左子树再按照b方法处理,删除替换的节点。 //////////////或者找右子树最小节点,再按照c方法处理,删除替换节点。

代码实现:

bool Erase(const K& key)
{Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_key == key)break;else if (cur->_key < key){parent = cur;cur = cur->_right;}else{parent = cur;cur = cur->_left;}}if (cur == nullptr)return false;//左空 a情况的左右子树都空也进入这里if (cur->_left == nullptr){if (cur == _root)_root = cur->_right;else{if (parent->_left == cur){parent->_left = cur->_right;}elseparent->_right = cur->_right;}}//右空else if (cur->_right == nullptr){if (cur == _root)_root = cur->_left;else{if (parent->_left == cur){parent->_left = cur->_left;}elseparent->_right = cur->_left;}}else //替换{parent = cur;Node* leftmax = cur->_left;while (leftmax->_right){parent = leftmax;leftmax = leftmax->_right;}swap(cur->_key, leftmax->_key);if (parent->_left == leftmax){parent->_left = leftmax->_left;}elseparent->_right = leftmax->_left;cur = leftmax;}delete cur;return true;return false;
}```c
template<class K>
class BSTree
{typedef TreeNode<K> Node;
public:BSTree():_root(nullptr){}
void Inorder()
{_Inorder(_root);
}
void _Inorder(Node* cur)
{if (cur == nullptr)return;_Inorder(cur->_left);cout << cur->_key << " ";_Inorder(cur->_right);
}
private:Node* _root;
};

AVL树

概念

虽然二叉搜索树加快了查找效率,但是如果数据接近顺序,那么二叉树就退化成单侧树了,查找元素就相当于在顺序表查找,效率低下,所以发明了AVL树
在这里插入图片描述
AVL树:是空树满足以下性质的二叉搜索树
⭐1,左右树都是二叉搜索树
⭐2,左右子树高度之差(简称平衡因子)绝对值不超过1
在这里插入图片描述
搜索时间复杂度log2n
AVL节点代码:

struct AVLTreeNode
{AVLTreeNode(const T& date = T()):_left(nullptr),_right(nullptr),_parent(nullptr),_date(date),_bf(0){}AVLTreeNode* _left;AVLTreeNode* _right;AVLTreeNode* _parent;T _date;int _bf;
};

插入

AVL树插入分两步
1,按二叉搜索树性质插入节点
2,调整平衡因子

插入新节点,那父节点平衡因子一定要调整,如果插在父节点左子树,则父节点平衡–,反之++
接下来会有三种情况:
a,如果父节点平衡因子=0,说明平衡了,不用再调整了
b,如果父节点等于±1,说明还需要向上调整,祖父节点的平衡因子也要调整
c,如果父节点平衡因子等于±2,说明不平衡了需要旋转

旋转

旋转分4种
1,如果新节点插在较高左子树左侧左左右单旋
在这里插入图片描述
2,如果新节点插在较高右子树右侧右右左单旋
在这里插入图片描述
3,如果新节点插在较高左子树右侧:左右先左单旋再右单旋
在这里插入图片描述
旋转后考虑平衡因子更新,据图得知90父节点为130子节点为0

如果新插入节点是60,则父子为0,
根据新插入节点左右子树不同,平衡因子更新不同

4,如果新节点插在较高右子树左侧右左,先右单旋再左单旋
在这里插入图片描述

参考左右单旋

总结

  • 当父节点等于2时,

若子节点=1,则左单旋
若子节点=-1,则右单旋再单旋

  • 当父节点等于-2

若字节点=-1,则右单旋
若子节点=1,则左右单旋

代码实现:

template<class T>
class AVLTree
{typedef AVLTreeNode<T> Node;
public:AVLTree():_root(nullptr){}bool Insert(const T& date){if (nullptr == _root){_root = new Node(date);return true;}Node* pCur = _root;Node* pParent = nullptr;while (pCur){if (pCur->_date < date){pParent = pCur;pCur = pCur->_right;}else if (pCur->_date > date){pParent = pCur;pCur = pCur->_left;}else{return false;}}pCur = new Node(date);if (pParent->_date > date){pParent->_left = pCur;}else if (pParent->_date < date){pParent->_right = pCur;}pCur->_parent = pParent;while (pParent){if (pParent->_left == pCur){pParent->_bf--;}elsepParent->_bf++;if (pParent->_bf == 0)break;else if (pParent->_bf == -1 || pParent->_bf == 1){pCur = pParent;pParent = pParent->_parent;}else if (pParent->_bf == 2 || pParent->_bf == -2){if (pParent->_bf == 2 && pCur->_bf == 1){RotateL(pParent);}else if (pParent->_bf == -2 && pCur->_bf == -1){RotateR(pParent);}else if (pParent->_bf == 2 && pCur->_bf == -1){RotateRL(pParent);}else if (pParent->_bf == -2 && pCur->_bf == 1){RotateLR(pParent);}elsereturn false;}}return true;}void RotateL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;Node* pnode = parent->_parent;parent->_right = curleft;if (curleft){curleft->_parent = parent;}cur->_left = parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (pnode->_right == parent){pnode->_right = cur;}elsepnode->_left = cur;cur->_parent = pnode;}parent->_bf = cur->_bf = 0;}void RotateR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;Node* pnode = parent->_parent;parent->_left = curright;if (curright){curright->_parent = parent;}cur->_right = parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (pnode->_right == parent){pnode->_right = cur;}elsepnode->_left = cur;cur->_parent = pnode;}cur->_bf = parent->_bf = 0;}void RotateRL(Node* parent){Node* cur = parent->_right;Node* curleft = cur->_left;int bf = curleft->_bf;RotateR(cur);RotateL(parent);if (bf == 0){cur->_bf = parent->_bf = curleft->_bf = 0;}else if (bf == 1){parent->_bf = -1;cur->_bf = 0;curleft->_bf = 0;}else if (bf == -1){cur->_bf = 1;curleft->_bf = 0;parent->_bf = 0;}elseassert(false);}void RotateLR(Node* parent){Node* cur = parent->_left;Node* curright = cur->_right;int bf = curright->_bf;RotateL(cur);RotateR(parent);if (bf == 0){cur->_bf = parent->_bf = curright->_bf = 0;}else if (bf == 1){cur->_bf = -1;parent->_bf = 0;curright->_bf = 0;}else if (bf == -1){cur->_bf = 1;curright->_bf = 0;parent->_bf = 0;}elseassert(false);}void Inorder(Node* root){if (root == nullptr){return;}Inorder(root->_left);cout << root->_date;Inorder(root->_right);}void Inorder(){Inorder(_root);}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 left = Height(root->_left);int right = Height(root->_right);if (right - left != root->_bf){std::cout << "平衡因子错误" << root->_bf << std::endl;}return abs(left - right) < 2&& IsBalance(root->_left)&& IsBalance(root->_right);}private:Node* _root;
};

AVL验证

1,中序遍历,如果是升序则说明是二叉平衡树

2,判断左右子树高度差不超过1,确认平衡性

红黑树

概念

AVL树限制比较大,旋转次数多,所以发明了红黑树。
红黑树:一种二叉搜索树,每个节点加了颜色限制红或黑,通过对每一条路径颜色限制,确保红黑树最短路径不会超过最短路径的两倍,因而平衡。

❤红黑树性质:
1,根节点必须是黑色的
2,每个节点不是黑色的就是红色的
3,每一条路径黑色节点数相同
4,如果一个节点是红色的,那么两个孩子都是黑色的(不能出现连续的红色)
5,叶子节点是黑色的(此处叶子节点是空)
在这里插入图片描述

插入

💪如果树是空的,说明插入的是根节点,直接设置为黑
否则来的节点就设置为红节点,因为每条路径黑节点数相同,如果新来的变为黑,那么整个树都要做调整
如果插入的红节点父亲是黑节点,那么无事发生,但如果父节点也是红,那就违反红黑树性质(红节点的孩子必须是黑节点),需要调整,接下来分两种情况分析
💪1,孩子的叔叔(与父节点相对的另一支)存在且为红,则把叔叔和父亲都设为黑,祖父设为红(依旧保持黑节点数不变),祖父设为红了,还要检查祖父的父亲是否为红,继续向上调整
在这里插入图片描述
💪2,叔叔节点 不存在/存在且为黑,旋转,旋转又分两种情况,单旋和双旋
在这里插入图片描述
在这里插入图片描述
旋转之后父节点为黑,祖父为红
代码实现:

bool insert(const pair<key,value>& kv){if (_root == nullptr){_root = new Node(kv);_root->_col = BLACK;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}elsereturn false;}cur = new Node(kv);cur->_col = RED;if (parent->_kv.first <cur->_kv.first){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent&&parent->_col==RED){Node* grandparent = parent->_parent;if(grandparent->_left==parent){Node* uncle = grandparent->_right;//uncle存在且为红if (uncle && uncle->_col==RED){parent->_col = uncle->_col = BLACK;grandparent->_col = RED;cur = grandparent;parent = cur->_parent;}else//uncle不存在或存在且为黑{if (parent->_left == cur){//g//p//cRotateR(grandparent);parent->_col = BLACK;grandparent->_col = RED;}else if (parent->_right == cur){//g//p//cRotateL(parent);RotateR(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}	}else{Node* uncle = grandparent->_left;if (uncle && uncle->_col == RED){uncle->_col = parent->_col = BLACK;grandparent->_col = RED;cur = grandparent;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandparent);grandparent->_col = RED;parent->_col = BLACK;}else{RotateR(parent);RotateL(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}}//把根设为黑_root->_col = BLACK;return true;}void RotateL(Node* parent)
{Node* cur = parent->_right;Node* curleft = cur->_left;Node* pnode = parent->_parent;parent->_right = curleft;if (curleft){curleft->_parent = parent;}cur->_left = parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (pnode->_right == parent){pnode->_right = cur;}elsepnode->_left = cur;cur->_parent = pnode;}parent->_bf = cur->_bf = 0;
}
void RotateR(Node* parent)
{Node* cur = parent->_left;Node* curright = cur->_right;Node* pnode = parent->_parent;parent->_left = curright;if (curright){curright->_parent = parent;}cur->_right = parent;parent->_parent = cur;if (parent == _root){_root = cur;cur->_parent = nullptr;}else{if (pnode->_right == parent){pnode->_right = cur;}elsepnode->_left = cur;cur->_parent = pnode;}
}

检测

检测是否为二叉树分两步
1,中序遍历是否是升序
2,是否满足红黑树性质

bool checkcolour(Node* root, int blacknum, int benchblack){//黑节点数目不一样错误if (root == nullptr){if (blacknum != benchblack)return false;return true;}//遇到黑++if (root->_col == BLACK){++blacknum;}//是否连续红节点if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << "flase" << root->_kv.first;return false;}return checkcolour(root->_left,blacknum,benchblack) && checkcolour(root->_right,blacknum,benchblack);}bool isbalance(){return isbalance(_root);}bool isbalance(Node* root){//空树正确//根不黑直接错误if (root == nullptr)return true;if (root->_col != BLACK)return false;Node* cur = root;int benchblack = 0;while (cur){//确定一条路上的黑节点if (cur->_col == BLACK)++benchblack;cur = cur->_left;			}return checkcolour(root,0,benchblack);}

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

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

相关文章

Android实战进阶 - 启动页

场景&#xff1a;当启动页处于倒计时阶段&#xff0c;用户将其切换为后台的多任务卡片状态&#xff0c;倒计时会继续执行&#xff0c;直到最后执行相关逻辑&#xff08;一般会跳转引导页、进入主页等&#xff09; 期望&#xff1a;而综合市场来看&#xff0c;一般我们期望的是当…

无标记点动捕技术:重塑展厅展馆的沉浸式数字交互新时代

在元宇宙浪潮的持续推进下&#xff0c;虚拟数字人正逐渐成为连接虚实世界的重要媒介。在展厅展馆中&#xff0c;数字人不仅能够扮演导览员、讲解员角色&#xff0c;更可通过情感化交互提升参观体验&#xff0c;使文化传播更具感染力和沉浸感。虚拟人的引入&#xff0c;为传统展…

轻松Linux-7.Ext系列文件系统

天朗气清&#xff0c;惠风和煦&#xff0c;今日无事&#xff0c;遂来更新。 1.概述 总所周知&#xff0c;我们存的数据都是在一个叫硬盘的东西里面&#xff0c;这个硬盘又像个黑盒&#xff0c;这章就来简单解析一下Linux中文件系统。 现在我们用的大都是固态硬盘&#xff0c;…

Matlab机器人工具箱使用4 蒙特卡洛法绘制工作区间

原理&#xff1a;利用rand随机数&#xff0c;给各个关节设置随机关节变量&#xff0c;通过正运动学得到末端位姿变换矩阵&#xff0c;然后利用变换矩阵2三维坐标标记出末端坐标&#xff0c;迭代多次就可以构成点云。教程视频&#xff1a;【MATLAB机器人工具箱10.4 机械臂仿真教…

【项目】在AUTODL上使用langchain实现《红楼梦》知识图谱和RAG混合检索(三)知识图谱和路由部分

首先在数据集 - 开放知识图谱下载红楼梦的知识图谱&#xff0c;这个网站上有各种各样的知识图谱&#xff0c;可以挑你感兴趣的做( • ̀ω•́ ) 这个知识图谱的作者们已经将三元组抽取出来了&#xff0c;我们可以直接用&#xff0c;如果你对三元组是如何生成的感兴趣&#xf…

pycharm 最新版上一次编辑位置

2025nipycharm方法一&#xff1a;用快捷键&#xff08;最方便&#xff09;跳回上一次编辑位置&#xff1a;Windows/Linux: Ctrl Alt ←macOS: ⌘ Option ←跳到前一次位置&#xff1a;Windows/Linux: Ctrl Alt →macOS: ⌘ Option →方法二&#xff1a;显示工具栏按钮在…

前端性能监控与优化:从 Lighthouse 到 APM

在当今竞争激烈的数字环境中&#xff0c;用户对Web应用性能的要求日益提高。一个缓慢或响应迟钝的应用不仅会流失用户&#xff0c;更可能损害品牌形象和商业价值。因此&#xff0c;前端性能的监控与优化已成为前端开发不可或缺的关键环节。本文将深入探讨从基础的性能评估工具L…

TC_Motion多轴运动-电子齿轮

目录 电子齿轮 【基本概念】 【应用示例】 【开发总结】 END 电子齿轮 【基本概念】 定义:通过软件方法实现机械齿轮的速比调节功能(两个轴成线性比例旋转) 优点 免维护,告别机械损耗 易调节,任意修改齿轮比 精度高,无机械背隙 应用场景 多台电机拖动同一负载,要求多台…

CentOS 7 下载教程

访问阿里云镜像站 阿里巴巴开源镜像站 选择centos 点这个 选择7版本 进入isos目录 点这个 选择这个版本 因为这个镜像的日期更新 推荐下载 DVD 版&#xff1a;包含完整系统和常用软件&#xff0c;无需额外联网安装组件Minimal 版&#xff1a;精简版&#xff0c;仅包含基础系…

MAC在home下新建文件夹报错“mkdir: test: Operation not supported”

在Mac电脑中&#xff0c;home文件夹下不能直接mkdir&#xff0c;sudo 也还是不行&#xff0c;提示“mkdir: test: Operation not supported”。网上找的解决方案不好使&#xff0c;因为没有关闭系统完整性保护关闭系统完整性保护查看SIP当前的状态csrutil status如果是开启状态…

交叉导轨从测试仪到工作台的精密运动控制

在精密仪器领域微米级的运动精度与纳米级的稳定性往往是决定设备性能上限的核心指标。而支撑这一技术鸿沟跨越的&#xff0c;往往隐匿于机械结构的“毫厘之间”——交叉导轨。以下是其在不同精密仪器中的具体应用&#xff1a;光学测试仪&#xff1a;光学测试仪主要用于各种高精…

内网穿透的应用-Navidrome与cpolar本地搭建跨网络访问的云音乐服务器

文章目录前言1. 安装Docker2. 创建并启动Navidrome容器3. 公网远程访问本地Navidrome3.1 内网穿透工具安装3.2 创建远程连接公网地址3.3 使用固定公网地址远程访问前言 音乐收藏存在平台版权限制、音质压缩和访问不便等问题。Navidrome 开源音乐服务器与 cpolar 内网穿透服务的…

FastAPI 访问不了API文档或配置不生效的解决方法

FastAPI中文教程 本文背景 FastAPI框架自带交互式api文档,通过路由/docs或者/redoc 访问&#xff0c;但是FastAPI 的文档界面&#xff08;如 /docs 和 /redoc&#xff09;依赖于外部的 JavaScript 和 CSS 库&#xff0c;如果项目部署环境网络不佳或者无法访问外网的时候&…

IAR 集成开发环境入门指南:字体设置与调试实战

一、IAR 的基本使用教程1. IAR 颜色字体大小设置打开设置路径&#xff1a;点击顶部菜单栏 Tools → 选择 Options&#xff0c;打开 IDE 配置窗口。进入字体颜色设置界面&#xff1a;在弹出的 “IDE Options” 窗口中&#xff0c;双击展开 Editor 选项&#xff0c;然后点击 Colo…

10:00开始面试,10:06就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%,这…

Flink 状态管理的核心能力

我们来看一个复杂的实际案例&#xff1a;阿里巴巴菜鸟的实时物流追踪系统。 该系统处理来自多个电商平台&#xff08;天猫、淘宝、速卖通&#xff09;的订单包裹&#xff0c;通过一个复杂的处理流程&#xff1a; 合并与去重&#xff1a;通过聚合操作将不同来源的订单合并并去重…

基于go语言的云原生TodoList Demo 项目,验证云原生核心特性

以下是一个基于 Go 语言 的云原生 TodoList Demo 项目&#xff0c;涵盖 容器化、Kubernetes 编排、CI/CD、可观测性、弹性扩缩容 等核心云原生特性&#xff0c;代码简洁且附详细操作指南&#xff0c;适合入门学习。项目概览 目标&#xff1a;实现一个支持增删改查&#xff08;C…

手机能看、投屏 / 车机不能看与反向链接验证类似吗?

有一定关联&#xff0c;但两者的技术逻辑并非完全等同 ——“手机能看、投屏 / 车机不能看” 的核心原因更复杂&#xff0c;反向链接验证是其中一种可能的限制手段&#xff0c;但不是唯一甚至不是最主要的手段。要理清这个问题&#xff0c;需要先拆解 “投屏 / 车机播放受限” …

25年9月通信基础知识补充1:NTN-TDL信道建模matlab代码(satellite-communications toolbox学习)

看文献过程中不断发现有太多不懂的基础知识&#xff0c;故长期更新这类blog不断补充在这过程中学到的知识。由于这些内容与我的研究方向并不一定强相关&#xff0c;故记录不会很深入请见谅。 【通信基础知识补充10】25年9月通信基础知识补充1&#xff1a;NTN-TDL信道建模matlab…

洛谷P3370 【模板】字符串哈希 (哈希表)详解

题目如下&#xff1a;&#xff08;注&#xff1a;解此题我只需左手一根指头&#xff0c;哈哈哈哈哈哈哈&#xff09;注意&#xff0c;哈希表的好处是能大幅度减少寻找遍历的时间可能有人不理解哈希值&#xff0c; 这里哈希的模的值一般得是比较大的质数&#xff0c;如标准的100…