树形结构——二叉树类型
本文主要介绍树形结构中的二叉树类型,包括二叉树、平衡二叉树、二叉查找树和完全二叉树;
1.二叉树
二叉树是一种树形结构,其中每个节点最多有两个子节点,通常称为左子节点和右子节点。二叉树具有以下特点:
- 每个节点最多有两个子节点,分别为左子节点和右子节点。
- 左子节点的值小于或等于当前节点的值,而右子节点的值大于当前节点的值。这个特性使得二叉树在查找和排序方面有很大的应用价值。
- 二叉树可以为空树,此时没有节点。
二叉树可以用递归或迭代方式来遍历。常见的二叉树遍历方法包括:
- 前序遍历(Preorder Traversal):先访问根节点,再依次访问左子树和右子树。
- 中序遍历(Inorder Traversal):先访问左子树,再访问根节点,最后访问右子树。中序遍历的结果是有序的。
- 后序遍历(Postorder Traversal):先访问左子树,再访问右子树,最后访问根节点。
- 层序遍历(Level Order Traversal):按层级顺序从上到下逐层遍历,即先访问根节点,然后访问第二层的所有节点,接着访问第三层的所有节点,以此类推。
1.1 二叉树的创建
创建一个二叉树的类型
class TreeNode
{
public:int data;TreeNode* left;TreeNode* right;TreeNode(int val) : data(val), left(nullptr), right(nullptr) {}
};class BinaryTree
{
public:BinaryTree() : root(nullptr) {}~BinaryTree(){}// 创建二叉树void createTree(int val) { root = createNode(val); }// 创建新节点TreeNode* createNode(int val) {if (val == -1) {return nullptr;} else {return new TreeNode(val);}}
private:TreeNode* root;
};
1.2 二叉树元素的插入
通过递归插入新的元素,这里需要区分根节点是否空;
// 插入节点void insert(int val) {if (root == nullptr) {root = new TreeNode(val);return;}insertNode(root, val);}// 递归插入节点void insertNode(TreeNode* node, int val) {if (val <= node->data) {if (node->left == nullptr) {node->left = new TreeNode(val);} else {insertNode(node->left, val);}} else {if (node->right == nullptr) {node->right = new TreeNode(val);} else {insertNode(node->right, val);}}}
1.3 二叉树的删除
二叉树的删除使用递归的思想
// 删除二叉树void deleteTree(TreeNode* node) {if (node == nullptr) {return;}deleteTree(node->left);deleteTree(node->right);delete node;}
1.4 二叉树的遍历
二叉树的遍历分为前序遍历,中序遍历和后序遍历
//二叉树的遍历——先序(先访问根节点)void preorderTraversal(TreeNode* root) {if (root == nullptr) {return;}// 访问根节点std::cout << root->data << " ";// 先序遍历左子树preorderTraversal(root->left);// 先序遍历右子树preorderTraversal(root->right);}//二叉树的遍历——中序(先访问左子树)void inorderTraversal(TreeNode* root) {if (root == nullptr) {return;}// 中序遍历左子树inorderTraversal(root->left);// 访问根节点std::cout << root->data << " ";// 中序遍历右子树inorderTraversal(root->right);}//二叉树的遍历——后序(先访问右子树)void postorderTraversal(TreeNode* root) {if (root == nullptr) {return;}// 后序遍历左子树postorderTraversal(root->left);// 后序遍历右子树postorderTraversal(root->right);// 访问根节点std::cout << root->data << " ";}
1.5 二叉树的判空
// 判断二叉树是否为空bool isEmpty() {return root == nullptr;}
1.6 二叉树的大小计算和深度计算
二叉树的大小计算指的是树形结构中有多少个元素;
二叉树的深度计算指的是树形结构中有多少层;
// 获取二叉树的深度int getTreeDepth(TreeNode* root) {if (root == nullptr) {return 0;}int leftDepth = getTreeDepth(root->left);int rightDepth = getTreeDepth(root->right);return std::max(leftDepth, rightDepth) + 1;}int getTreeSize(TreeNode* root){int re= 0;//tree为空的时候if(root == nullptr){return re;}int left_size = getTreeSize(root->left);int right_size = getTreeSize(root->right);re = left_size + right_size + 1;return re;}
1.7 二叉树的查找
查找某一个元素是否在二叉树中,如果存在则返回true,不存在则返回false
// 查询节点值是否存在于二叉树中bool search(int val) {return searchNode(root, val);}// 递归查询节点值是否存在于二叉树中bool searchNode(TreeNode* node, int val) {if (node == nullptr) {return false;}if (node->data == val) {return true;}return searchNode(node->left, val) || searchNode(node->right, val);}TreeNode* getRootValue(){return root;}
二叉树的使用
2.平衡二叉树(AVL树)
平衡二叉树是一种特殊的二叉树,它的左子树和右子树的高度差不超过1,并且左子树和右子树也都是平衡二叉树。平衡二叉树的设计目的是为了保持树的平衡,提高查找、插入和删除操作的效率,避免出现退化成链表的情况。实现一个平衡二叉树可以使用各种方法,最常用的是AVL树和红黑树。这些方法都通过旋转、重新平衡等操作来保持树的平衡性。在插入或删除节点时,需要根据树的平衡性进行相应调整,以保证树仍然是平衡的。平衡二叉树的平衡性可以确保树上的操作的时间复杂度为O(log n),提供了高效的数据访问和操作能力。
平衡二叉树与二叉树相比,在元素插入的时候需要根据平衡因子,进行左旋操作或者右旋操作;
2.1 平衡二叉树的结构
class TreeNode
{
public:int val;int height;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), height(1), left(nullptr), right(nullptr) {}
};
2.3 获取平衡因子
这里的平衡因子指的是左子树和右子树的高度差值
// 获取节点的平衡因子int getBalanceFactor(TreeNode* node) {if (node == nullptr) {return 0;}return getHeight(node->left) - getHeight(node->right);}// 获取节点的高度int getHeight(TreeNode* node) {if (node == nullptr) {return 0;}return node->height;}// 更新节点的高度void updateHeight(TreeNode* node) {node->height = std::max(getHeight(node->left), getHeight(node->right)) + 1;}
2.2 左旋操作和右旋操作
左旋操作和右旋操作是为了调整树形结构的高度;
// 右旋操作,根节点TreeNode* rotateRight(TreeNode* y) {TreeNode* x = y->left;TreeNode* T2 = x->right;x->right = y;y->left = T2;updateHeight(y);updateHeight(x);return x;}// 左旋操作,根节点TreeNode* rotateLeft(TreeNode* x) {TreeNode* y = x->right;TreeNode* T2 = y->left;y->left = x;x->right = T2;updateHeight(x);updateHeight(y);return y;}
2.3 元素的插入
在每一个元素插入的时候,都需要根据平衡因子来判断是否需要进行左旋和右旋的操作;
void insert(int val){if (root == nullptr) {root = new TreeNode(val);return;}root = insertNode(root, val); }// 插入节点TreeNode* insertNode(TreeNode* node, int val) { if (val < node->val) {if(node->left == nullptr ) {node->left = new TreeNode(val);}else{node->left = insertNode(node->left, val);} } else if (val > node->val) {if(node->right == nullptr){node->right = new TreeNode(val);}else{node->right = insertNode(node->right, val);}} else {std::cout<<"same data!"<<std::endl; return node;}updateHeight(node);int balanceFactor = getBalanceFactor(node); if(node->left != nullptr){// Left-Left caseif (balanceFactor > 1 && val < node->left->val) {return rotateRight(node);}// Left-Right caseif (balanceFactor > 1 && val > node->left->val) {node->left = rotateLeft(node->left);return rotateRight(node);} }if(node->right != nullptr){ // Right-Right caseif (balanceFactor < -1 && val > node->right->val) {return rotateLeft(node);}// Right-Left caseif (balanceFactor < -1 && val < node->right->val) {node->right = rotateRight(node->right);return rotateLeft(node);} }return node;}
3.二叉查找树(BST)
二叉查找树(Binary Search Tree,BST)是一种特殊的二叉树,它满足以下条件:
- 对于树中的每个节点,其左子树中的所有节点的值都小于该节点的值。
- 对于树中的每个节点,其右子树中的所有节点的值都大于该节点的值。
- 对于树中的每个节点,其左子树和右子树都是二叉查找树。
因此,二叉查找树具有以下特点:
- 左子节点的值小于父节点的值,右子节点的值大于父节点的值。
- 中序遍历二叉查找树得到的值序列是递增有序的。
- 可以快速插入、删除和查找节点。
4.完全二叉树
完全二叉树(Complete Binary Tree)是一种特殊类型的二叉树,在完全二叉树中,除了最后一层,其他层的节点都是满的,且最后一层的节点尽可能地靠左排列。
具体来说,完全二叉树的定义如下:
- 如果一个二叉树的高度为h(h >= 0),并且它的节点从第 1 层到第 h-1 层都是满的,第 h 层的节点都连续地排列在最左边若干位置上,那么这棵二叉树就是完全二叉树。
- 如果一个完全二叉树的最后一层有缺失节点,则缺失节点只能集中在层末尾的若干位置上,并且缺失的节点都是从左到右依次缺失的(即不会出现在中间位置缺失)。
完全二叉树主要涉及到元素的插入
void insert(int value) {Node* newNode = new Node(value);// root是根节点if (root == nullptr) {root = newNode;return;}std::queue<Node*> q;q.push(root);while (!q.empty()) {Node* front = q.front();q.pop(); //删除队首元素if (front->left == nullptr) {front->left = newNode;return;} else if (front->right == nullptr) {front->right = newNode;return;}q.push(front->left); q.push(front->right);}
}
相关文章:
树形结构——二叉树类型
本文主要介绍树形结构中的二叉树类型,包括二叉树、平衡二叉树、二叉查找树和完全二叉树; 1.二叉树 二叉树是一种树形结构,其中每个节点最多有两个子节点,通常称为左子节点和右子节点。二叉树具有以下特点: 每个节点…...
JavaScript对象的方法与原型链
在JavaScript中,对象是一种非常重要的数据类型,它允许我们将多个属性和方法组织在一起。对象的方法和原型链是理解JavaScript中面向对象编程的关键概念。本文将详细讲解对象的方法和原型链,用通俗易懂的方式帮助你深入理解这些概念。 1. 对象…...
Oracle入门初探---第一章 批量创建表、索引并插入测试数据
Oracle系列文章目录 第一章 批量创建表并插入测试数据 文章目录 Oracle系列文章目录前言一、创建表和索引二、向表中加入数据总结 前言 使用数据库,首先要向数据库中加入大量数据,本篇文章提供了一些测试数据 一、创建表和索引 -- 创建数据库和索引 -…...
全面讲解最小二乘法
常见的最小二乘法我们就不多说了,下面主要介绍一下最小二乘法的一些先进方法。 正则化的最小二乘法 在使用常见的最小二乘法进行回归分析时,常常会遇到过拟合的问题,也就是在训练数据集上表现的很好,但是在测试数据集上表现的很…...

【阻止IE强制跳转到Edge浏览器】
由于微软开始限制用户使用Internet Explorer浏览网站,IE浏览器打开一些网页时会自动跳转到新版Edge浏览器,那应该怎么禁止跳转呢? 1、点击电脑左下角的“搜索框”或者按一下windows键。 2、输入“internet”,点击【Internet选项…...
C++/Linux项目——日志系统(简介)
一,日志系统的目的 1.⽣产环境的产品为了保证其稳定性及安全性是不允许开发⼈员附加调试器去排查问题, 可以借助⽇志系统来打印⼀些⽇志帮助开发⼈员解决问题 2.上线客⼾端的产品出现bug⽆法复现并解决, 可以借助⽇志系统打印⽇志并上传到服…...
【Redis面试题整理一】
一、Redis定义 Redis 是一种基于内存的数据库,对数据的读写操作都是在内存中完成,读写速度非常快,被广泛应用于缓存方向。并且,Redis 存储的是 KV 键值对数据。 二、Redis为什么不存在并发竞争 对数据类型的操作都是原子性的&a…...
前端权限验证之自定义指令v-permission
自定义指令 在前端处理按钮权限详细代码 在前端处理按钮权限 使用vue自定义指令来v-permission 来控制按钮 详细代码 //index.js文件 import permission from ./permissionconst install function(Vue) {Vue.directive(permission, permission) }if (window.Vue) {window[p…...
c++使用条件变量实现生产消费问题(跨平台)
1. 生产者线程 思路:队列满了的情况下, 触发条件变量wait, 等待消费线程消费后唤醒继续生产. void ProducerThreadFunc() {while(1) { while(/* 容器已满 */) { /* 线程等待, 直到消费者消费后唤醒继续执行 */ }/* 生产动作 */ } }2. 消…...

怎么快速搭建BI?奥威BI系统做出了表率
搭建BI系统有两大关键,分别是环境搭建和数仓建设。这两点不管是哪一个都相当地费时费力,那要怎么才能快速搭建BI平台,顺利实现全企业数字化运营决策?奥威BI系统方案,你值得拥有! 奥威BI系统方案࿰…...

Kafka3.4 SASL/kerberos/ACL 证以及 SSL 加密连接
Kafka3.4 SASL/kerberos ACL 证以及 SSL 加密连接 序 前面我们使用 kafka3.3.1 on zookeeper 的模式进行多网段监听的 kafka 集群,顺便搭建起 kafkaui 后发现一些问题,我们 kafka 集群没有连接认证,万一谁知道了我们的 kafka 连接地址&…...

UE中低延时播放RTSP监控视频解决方案
第1章 方案简介 1.1 行业痛点 在各种智慧城市、智慧社区、智慧水利、智慧矿山等数字孪生项目中,经常使用通UE来开发三维可视化场景。在这些场景中通常都需要把现场的各种监控视频在UE的可视化场景中接入,主要包含海康威视、大华、宇视、华为等众多监控…...

iOS - 开发者账号续订会员资格更换订阅的账号
文章目录 前言开发环境续订会员资格转让账户持有人验证身份1. 实名认证2. 联系信息 更换订阅的账号最后 前言 公司有一个开发者账号快到期了需要续订会员资格,刚注册时是用我自己的个人账号完成的订阅购买。现在想来有点不妥,于是尝试更换用于订阅的账号…...
大数据课程F3——HIve的基本操作
文章作者邮箱:yugongshiye@sina.cn 地址:广东惠州 ▲ 本章节目的 ⚪ 掌握HIve的基本SQL语句和注意问题; ⚪ 掌握HIve的表结构; ⚪ 掌握HIve的数据类型; ⚪ 掌握HIve的基础函数和窗口函数; 一、基本SQL 1. SQL的执行方式 1. 通过hive -e的方式来执行指…...
top解析
top - 13:52:26 up 26 days, 20:56, 2 users, load average: 0.00, 0.01, 0.05 当前时间 系统运行时间,格式为时:分 当前登陆用户数2 系统负载,即任务队列的平均长度。三个数值分别为1分钟,5分钟,15分钟前到现在的平均…...

如何让子组件,router-view,呈现左右分布格局
1.用浮动进行浮动布局,定义一个大盒子,把浮动的样式写在公共样式里(这里在main.js里定义一下全局布局)。 2、能够在右边显示了...

计算机网络—TCP和UDP、输入url之后显示主页过程、TCP三次握手和四次挥手
TCP基本认识 TCP是面向连接的、可靠的,基于字节流的传输层通信协议。 图片来源小林coding 序号:传输方向上字节流的字节编号。初始时序号会被设置一个随机的初始值(ISN),之后每次发送数据时,序号值 ISN…...

使用反汇编工具IDA查看发生异常的汇编代码的上下文去辅助分析C++软件异常
目录 1、概述 2、如何使用IDA打开并查看二进制文件的汇编代码 3、在IDA中找到发生崩溃的那条汇编指令的位置 3.1、如何在IDA中找到发生异常的那条汇编指令 3.2、示例 4、阅读汇编代码上下文需要掌握一定的基础汇编知识 5、最后 VC常用功能开发汇总(专栏文章列…...

怎么合并多个视频?简单视频合并方法分享
合并多个视频可以将它们组合成一个更长的视频,这对于需要播放多个短视频的情况非常有用。此外,合并视频还可以使视频编辑过程更加高效,因为不必将多个独立的视频文件分别处理。最后,合并视频可以减少文件数量,从而使整…...

webpack基础知识九:如何提高webpack的构建速度?
一、背景 随着我们的项目涉及到页面越来越多,功能和业务代码也会随着越多,相应的 webpack 的构建时间也会越来越久 构建时间与我们日常开发效率密切相关,当我们本地开发启动 devServer 或者 build 的时候,如果时间过长ÿ…...
内存分配函数malloc kmalloc vmalloc
内存分配函数malloc kmalloc vmalloc malloc实现步骤: 1)请求大小调整:首先,malloc 需要调整用户请求的大小,以适应内部数据结构(例如,可能需要存储额外的元数据)。通常,这包括对齐调整,确保分配的内存地址满足特定硬件要求(如对齐到8字节或16字节边界)。 2)空闲…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

前端开发面试题总结-JavaScript篇(一)
文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包(Closure)?闭包有什么应用场景和潜在问题?2.解释 JavaScript 的作用域链(Scope Chain) 二、原型与继承3.原型链是什么?如何实现继承&a…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...

免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...

Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]
报错信息:libc.so.6: cannot open shared object file: No such file or directory: #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...