【新春不断更】数据结构与算法之美:二叉树
Hello大家好,我是但凡!很高兴我们又见面啦!
眨眼间已经到了2024年的最后一天,在这里我要首先感谢过去一年陪我奋斗的每一位伙伴,是你们给予我不断前行的动力。银蛇携福至,万象启新程。蛇年新春之际,愿你们万事顺遂,岁月皆安,新的一年所想皆如愿,所行皆坦途 。
好了,给生活添点passion,开始今天的编程之路!
我的博客:<但凡.
我的专栏:《编程之路》、《数据结构与算法之美》、《题海拾贝》
欢迎点赞,关注!
目录
1、 二叉树的动态模拟
1.1新建节点
1.2建树
1.3计算树的节点个数
1.3.1方法一
1.3.2方法二
1.4计算树的叶子节点个数
1.5 计算树的第K层节点个数
1.6 树的深度
1.7查找节点
1.8 遍历
1.8.1前序遍历
1.8.2中序遍历
1.8.3后序遍历
1.8.4层序遍历(广度优先遍历)
1.9判断二叉树是否为完全二叉树
1.10销毁二叉树
2、二叉树的静态模拟实现
1、 二叉树的动态模拟
我们用链式结构实现二叉树。一般链式结构实现二叉树,我们的结构中包含三个元素,一是该节点的数据,二是左子树节点,三是右子树节点
typedef char BTtype;
typedef struct BinaryTreeNode
{BTtype x;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTnode;
1.1新建节点
BTnode* BTbuybode(char a)//你这声明和定义都不是一个意思好的
{BTnode* p = (BTnode*)malloc(sizeof(BTnode));if (p == NULL){perror("malloc error!");exit(1);}p->left = NULL;p->right = NULL;p->x = a;return p;
}
1.2建树
需要注意的是,我们的树是需要手动去建的,所以我在这只是给大家一个示例:
//建树
BTnode* nodeA=BTbuybode('A');
BTnode* nodeB = BTbuybode('B');
BTnode* nodeC = BTbuybode('C');
BTnode* nodeD = BTbuybode('D');
BTnode* nodeE = BTbuybode('E');
nodeA->left = nodeB;
nodeA->right = nodeC;
nodeB->left = nodeD;
nodeC->left = nodeE;
BTnode* root = nodeA;
建的树是这样的:
那么接下来我们就实现以下和树相关的操作。准备好迎接递归的极致暴力美学!
1.3计算树的节点个数
1.3.1方法一
方法一就是咱们把size作为一个函数的形参,然后把这个树遍历一遍,每遍历一个节点就size(节点个数)加一。但需要注意的是,我们需要传入size的地址才能改变size的值。
void BinaryTreeSize(BTnode* root,int* size)
{if (root == NULL){return;}(*size)++;BinaryTreeSize(root->left,size);BinaryTreeSize(root->right, size);
}
1.3.2方法二
方法二是纯递归: 节点个数=左子树节点个数+右子树节点个数,所以我们以此为基础递归就可以了。
int BinaryTreeSize(BTnode* root)
{//节点个数=左子树节点个数+右子树节点个数//递归出口if (root == NULL){return 0;}return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}
1.4计算树的叶子节点个数
树的叶子结点就是没有左右子树的节点,所以咱们得设置两个递归出口。
int BinaryTreeLeafSize(BTnode* root)
{//递归出口if (root == NULL){return 0;}if (root->left == NULL && root->right == NULL){return 1;}return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}
1.5 计算树的第K层节点个数
当咱们K减成1的时候,就说明到达了第K层。
int BinaryTreeLevelKSize(BTnode* root, int k)
{//递归出口if(k==1){if (root == NULL){return 0;}else{return 1;}}return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}
1.6 树的深度
注意我是拿C++写的,用了自带的函数max,如果使用C语言写的话max函数得自己写,或者用一个问号表达式来实现类似效果。
int BinaryTreeDeep(BTnode* root)
{//计算树的深度if (root == NULL){return 0;}return 1 + max(BinaryTreeDeep(root->left), BinaryTreeDeep(root->right));
}
1.7查找节点
BTnode* BinaryTreeFind(BTnode* root, BTtype x)
{//递归出口if (root == NULL){return 0;}if (root->x == x){return root;}BTnode* left = BinaryTreeFind(root->left, x);if(left){return root;}BTnode* right = BinaryTreeFind(root->right, x);if (right){return root;}return NULL;
}
1.8 遍历
1.8.1前序遍历
前序遍历就是先遍历头节点,然后遍历左子树,最后遍历右子树。我们可以把它拆开来想,我们左子树依然用先遍历头,再遍历左子树,最后遍历右子树的方式来遍历,左子树的左子树依然如此......
void BinaryTreePrevOrder(BTnode* root)
{//头 左 右//递归出口if (root == NULL){cout << "NULL" << " ";return;}cout << root->x << " ";BinaryTreePrevOrder(root->left);BinaryTreePrevOrder(root->right);
}
1.8.2中序遍历
void BinaryTreeInOrder(BTnode* root)
{//左 头 右//递归出口if (root == NULL){cout << "NULL" << " ";return;}BinaryTreeInOrder(root->left); //注意别调用错了,调用中序的cout << root->x << " ";BinaryTreeInOrder(root->right);
}
1.8.3后序遍历
void BinaryTreePostOrder(BTnode* root)
{//递归出口if (root == NULL){cout << "NULL" << " ";return;}BinaryTreePostOrder(root->left);BinaryTreePostOrder(root->right);cout << root->x << " ";
}
我们可以发现,这三个遍历的不同就是打印根节点的位置发生了变化 。我们以上三个遍历都属于深度优先遍历。
1.8.4层序遍历(广度优先遍历)
void BinaryTreeLevelOrder(BTnode* root)
{queue<BTnode*> q;//创建队列q.push(root);while (!q.empty()){BTnode* tmp = q.front();q.pop();cout << tmp->x << " ";//左右子树入队列if (tmp->left){q.push(tmp->left);}if (tmp->right){q.push(tmp->right);}}
}
这个层序遍历我用了c++自带的队列,如果用C语言写的话我们可以把模拟实现的队列文件导入。我之前发过队列的模拟实现,给大家放在这里: 数据结构与算法之美:队列-CSDN博客
1.9判断二叉树是否为完全二叉树
bool BinaryTreeComplete(BTnode* root)
{queue<BTnode*> q;//创建队列q.push(root);while (!q.empty()){BTnode* tmp = q.front();q.pop();if (tmp==NULL){break;}//左右子树入队列q.push(tmp->left);q.push(tmp->right);}//现在队列中如果还有不为空的节点,就说明不是完全二叉树 while (!q.empty()){BTnode* tmp = q.front();q.pop();if (tmp){return false;}}return true;
}
1.10销毁二叉树
void BinaryTreeDestory(BTnode** root)
{//这里因为咱们要改变根节点,应该传入的是根节点的地址,所以得拿二级指针接收//递归出口if ((*root) == NULL){return;}//自叶向根方向的释放//如果先释放的话,就找不到叶子节点了BinaryTreeDestory(&((*root)->left));BinaryTreeDestory(&((*root)->right));free(*root);*root = NULL;
}
所有测试代码:
#include"BinaryTree.h"
void test()
{//建树BTnode* nodeA=BTbuybode('A');BTnode* nodeB = BTbuybode('B');BTnode* nodeC = BTbuybode('C');BTnode* nodeD = BTbuybode('D');BTnode* nodeE = BTbuybode('E');nodeA->left = nodeB;nodeA->right = nodeC;nodeB->left = nodeD;nodeC->left = nodeE;BTnode* root = nodeA;//计算节点个数int size = 0;BinaryTreeSize(root, &size);cout <<"节点个数:"<< size<< endl;//计算叶子节点个数cout << "叶子节点个数:" << BinaryTreeLeafSize(root) << endl;//计算第三层节点个数cout << "第三层节点个数:" << BinaryTreeLevelKSize(root, 3) << endl;//计算二叉树深度cout<<"二叉树深度:"<< BinaryTreeDeep(root) << endl;//查找值为E的节点BTnode* node = BinaryTreeFind(root, 'E');if (node)//已找到{cout << "已找到该节点" << endl;}else{cout << "未找到该节点" << endl;}//查找值为G的节点BTnode* node1 = BinaryTreeFind(root, 'G');if (node1)//已找到{cout << "已找到该节点" << endl;}else{cout << "未找到该节点" << endl;}//前序遍历BinaryTreePrevOrder(root);cout << endl;// 二叉树中序遍历BinaryTreeInOrder(root);cout << endl;//后序遍历BinaryTreePostOrder(root);cout << endl;//广度优先遍历BinaryTreeLevelOrder(root);cout << endl;//是否为完全二叉树if (BinaryTreeComplete(root)){cout << "是完全二叉树" << endl;}else{cout << "不是完全二叉树" << endl;}//二叉树的销毁BinaryTreeDestory(&root);
}
int main()
{test();return 0;
}
测试结果:
2、二叉树的静态模拟实现
之前我们介绍堆的时候建堆用的是vector数组,其实静态建树一共有两个方式。一个是vector数组,一个是链式前向星。二叉树当然也可以用这两个方法建,但是呢对于二叉树来说有一个特殊的方法来建树。
我们创建两个足够大数组,这两个数组分别记录着下标为k的左右节点的值。其中这个k是当前这个节点的值。咱们建树的时候都默认根节点的值为1.
#include<iostream>
#include<queue>
using namespace std;
const int N = 1e4 + 10;
int l[N], r[N];
queue<int> q;
void bfs()
{q.push(1);while (q.size()){int v = q.front();cout << v << " ";q.pop();//由于二叉树的存储找不到当前节点的父节点(也就是不能向上查找)//所以不需要bool数组标记if(l[v]){q.push(l[v]);}if(r[v]){q.push(r[v]);}}
}
void dfs1(int v)
{cout <<v<<" ";if (l[v]) dfs1(l[v]);if (r[v]) dfs1(r[v]);
}
void dfs2(int v)
{if (l[v]) dfs2(l[v]);cout << v << " ";if (r[v]) dfs2(r[v]);
}
void dfs3(int v)
{if (l[v]) dfs3(l[v]);if (r[v]) dfs3(r[v]);cout << v << " ";
}
int main()
{//建二叉树int n;cin >> n;//节点个数cin >> l[1] >> r[1];//l[],r[]分别存储当前节点的左右孩子,0代表没有该孩子 for(int i=2;i<=n;i++)//循环n-1次{cin >> l[i] >> r[i];}//二叉树新增的深度优先遍历方式//前序遍历dfs1(1);cout << endl;//中序遍历dfs2(1);cout << endl;//后序遍历dfs3(1);cout << endl;//宽度优先遍历bfs();
}
我们可以发现他的各种遍历的思路是和咱们动态实现一样的。
二叉树练习题:题海拾贝:二叉树的模拟题-CSDN博客
题海拾贝:[USACO3.4] 美国血统AmericanHeritage(求先序排列问题)-CSDN博客
题海拾贝:[JLOI2009] 二叉树问题-CSDN博客
好了,今天的内容就分享到这,我们下期再见!
相关文章:
【新春不断更】数据结构与算法之美:二叉树
Hello大家好,我是但凡!很高兴我们又见面啦! 眨眼间已经到了2024年的最后一天,在这里我要首先感谢过去一年陪我奋斗的每一位伙伴,是你们给予我不断前行的动力。银蛇携福至,万象启新程。蛇年新春之际…...
Linux环境基础开发工具的使用(apt, vim, gcc, g++, gbd, make/Makefile)
什么是软件包 在Linux下安装软件, 一个通常的办法是下载到程序的源代码, 并进行编译, 得到可执行程序. 但是这样太麻烦了, 于是有些人把一些常用的软件提前编译好, 做成软件包(可以理解成windows上的安 装程序)放在一个服务器上, 通过包管理器可以很方便的获取到这个编译好的…...
渗透测试之WAF规则触发绕过规则之规则库绕过方式
目录 Waf触发规则的绕过 特殊字符替换空格 实例 特殊字符拼接绕过waf Mysql 内置得方法 注释包含关键字 实例 Waf触发规则的绕过 特殊字符替换空格 用一些特殊字符代替空格,比如在mysql中%0a是换行,可以代替空格 这个方法也可以部分绕过最新版本的…...
新站如何快速获得搜索引擎收录?
本文来自:百万收录网 原文链接:https://www.baiwanshoulu.com/8.html 新站想要快速获得搜索引擎收录,需要采取一系列有针对性的策略。以下是一些具体的建议: 一、网站内容优化 高质量原创内容: 确保网站内容原创、…...
Harmony Next 跨平台开发入门
ArkUI-X 官方介绍 官方文档:https://gitee.com/arkui-x/docs/tree/master/zh-cn ArkUI跨平台框架(ArkUI-X)进一步将ArkUI开发框架扩展到了多个OS平台:目前支持OpenHarmony、Android、 iOS,后续会逐步增加更多平台支持。开发者基于一套主代码…...
小阿卡纳牌
小阿卡纳牌 风:热湿 火:热干 水:冷湿 土:冷干 火风:温度相同,但是湿度不同,二人可能会在短期内十分热情,但是等待热情消退之后,会趋于平淡。 湿度相同、温度不同&#x…...
【llm对话系统】LLM 大模型Prompt 怎么写?
如果说 LLM 是一个强大的工具,那么 Prompt 就是使用这个工具的“说明书”。一份好的 Prompt 可以引导 LLM 生成更准确、更相关、更符合你期望的输出。 今天,我们就来聊聊 LLM Prompt 的编写技巧,掌握这把解锁 LLM 潜能的钥匙! 一…...
【现代深度学习技术】深度学习计算 | 参数管理
【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上,结合当代大数据和大算力的发展而发展出来的。深度学习最重…...
c++ 定点 new
(1) 代码距离: #include <new> // 需要包含这个头文件 #include <iostream>int main() {char buffer[sizeof(int)]; // 分配一个足够大的字符数组作为内存池int* p new(&buffer) int(42); // 使用 placement new…...
Myeclipse最新版本 C1 2019.4.0
Myeclipse C1 2019.4.0下载地址:链接: https://pan.baidu.com/s/1MbOMLewvAdemoQ4FNfL9pQ 提取码: tmf6 1.1、什么是集成开发环境? ★集成开发环境讲究-站式开发,使用这个工具即可。有提示功能,有自动纠错功能。 ★集成开发环境可以让软件开…...
使用 lock4j-redis-template-spring-boot-starter 实现 Redis 分布式锁
在分布式系统中,多个服务实例可能同时访问和修改共享资源,从而导致数据不一致的问题。为了解决这个问题,分布式锁成为了关键技术之一。本文将介绍如何使用 lock4j-redis-template-spring-boot-starter 来实现 Redis 分布式锁,从而…...
thinkphp6+swoole使用rabbitMq队列
安装think-swoole安装 composer require php-amqplib/php-amqplib,以支持rabbitMq使用安装rabbitMq延迟队列插件 安装 rabbitmq_delayed_message_exchange 插件,按照以下步骤操作: 下载插件:https://github.com/rabbitmq/rabbitmq-delayed-…...
大一计算机的自学总结:异或运算
前言 异或运算这个操作看上去很匪夷所思,实际上作用非常大。 一、异或运算的性质 1.异或运算就是无进位相加。 2.满足交换律、结合律。 3.0^nn,n^n0。 4.若集合B为集合A子集,集合A异或和为x,集合B异或和为y,则集…...
宫本茂的游戏设计思想:有趣与风格化
作为独立游戏开发者之一,看到任天堂宫本茂20年前的言论后,深感认同。 游戏研发思想,与企业战略是互为表里的,游戏是企业战略的具体战术体现,虚空理念的有形载体。 任天堂长盛不衰的关键就是靠简单有趣的游戏…...
【AI论文】扩散对抗后训练用于一步视频生成总结
摘要:扩散模型被广泛应用于图像和视频生成,但其迭代生成过程缓慢且资源消耗大。尽管现有的蒸馏方法已显示出在图像领域实现一步生成的潜力,但它们仍存在显著的质量退化问题。在本研究中,我们提出了一种在扩散预训练后针对真实数据…...
使用Python Dotenv库管理环境变量
使用Python Dotenv库管理环境变量 在开发Python应用程序时,管理配置信息(如API密钥、数据库连接字符串等)是一个常见的需求。为了确保安全性和灵活性,通常不建议将这些敏感信息硬编码在代码中。这时,dotenv库就派上了…...
oracle 分区表介绍
oracle 分区表介绍 Oracle 分区表是一个非常强大的数据库功能,可以将一个大的表分割成多个更小、更易管理的块(分区)。这种分区结构在处理大规模数据时非常有用,因为它能改善性能、简化维护和管理,并支持高效的数据存取…...
在线可编辑Excel
1. Handsontable 特点: 提供了类似 Excel 的表格编辑体验,包括单元格样式、公式计算、数据验证等功能。 支持多种插件,如筛选、排序、合并单元格等。 轻量级且易于集成到现有项目中。 具备强大的自定义能力,可以调整外观和行为…...
基于 Node.js 的天气查询系统实现(附源码)
项目概述 这是一个基于 Node.js 的全栈应用,前端使用原生 JavaScript 和 CSS,后端使用 Express 框架,通过调用第三方天气 API 实现天气数据的获取和展示。 主要功能 默认显示多个主要城市的天气信息 支持城市天气搜索 响应式布局设计 深色主题界面 优雅的加载动画 技术栈 …...
【javaweb项目idea版】蛋糕商城(可复用成其他商城项目)
该项目虽然是蛋糕商城项目,但是可以复用成其他商城项目或者购物车项目 想要源码的uu可点赞后私聊 技术栈 主要为:javawebservletmvcc3p0idea运行 功能模块 主要分为用户模块和后台管理员模块 具有商城购物的完整功能 基础模块 登录注册个人信息编辑…...
langchain基础(三)
Chain: 关于三个invoke: 提示模板、聊天模型和输出解析器都实现了langchain的runnable接口, 都具有invoke方法(因为invoke方法是Runnable的通用调用方法) 所以可以一次性调用多次invoke直接得到最终结果:…...
在Ubuntu上用Llama Factory命令行微调Qwen2.5的简单过程
半年多之前写过一个教程:在Windows上用Llama Factory微调Llama 3的基本操作_llama-factory windows-CSDN博客 如果用命令行做的话,前面的步骤可以参考上面这个博客。安装好环境后, 用自我认知数据集微调Lora模块:data/identity.j…...
go 循环处理无限极数据
数据表结构: CREATE TABLE permission (id int(11) NOT NULL AUTO_INCREMENT COMMENT 权限ID,permission_name varchar(255) DEFAULT NULL COMMENT 权限名称,permission_url varchar(255) DEFAULT NULL COMMENT 权限路由,status tinyint(1) DEFAULT NULL COMMENT 权…...
Kafka 深入服务端 — 时间轮
Kafka中存在大量的延迟操作,比如延时生产、延时拉取和延时删除等。Kafka基于时间轮概念自定义实现了一个用于延时功能的定时器,来完成这些延迟操作。 1 时间轮 Kafka没有使用基于JDK自带的Timer或DelayQueue来实现延迟功能,因为它们的插入和…...
一文掌握ADB的安装及使用
文章目录 一、什么是ADB?二、 安装ADB2.1 下载ADB2.2 配置环境变量 三、连接Android设备四、 常用ADB命令五、ADB高级功能5.1 屏幕截图和录制5.2 模拟按键输入5.3 文件管理5.4 系统设置管理5.5 系统操作指令5.6 日志操作指令5.7 APK操作指令5.8 设备重启和恢复 六、…...
Linux系统下速通stm32的clion开发环境配置
陆陆续续搞这个已经很久了。 因为自己新电脑是linux系统无法使用keil,一开始想使用vscode里的eide但感觉不太好用;后面想直接使用cudeide但又不想妥协,想趁着这个机会把linux上的其他单片机开发配置也搞明白;而且非常想搞懂cmake…...
Java 9模块开发:IntelliJ IDEA实战指南
在Java 9中,模块化是一个重要的特性,它可以帮助我们更好地组织和管理代码。而IntelliJ IDEA作为一个强大的集成开发环境,为Java 9模块的开发提供了全面的支持。本文将通过一个实际的项目示例,详细讲解如何在IntelliJ IDEA中开发和…...
OpenCSG月度更新2025.1
1月的OpenCSG取得了一些亮眼的成绩 在2025年1月,OpenCSG在产品和社区方面继续取得了显著进展。产品方面,推出了AutoHub浏览器自动化助手,帮助用户提升浏览体验;CSGHub企业版功能全面升级,现已开放试用申请,…...
【算法与数据结构】动态规划
目录 基本概念 最长递增子序列(中等) 最大子数组和(中等) 基本概念 重叠子问题 一个问题可以被分解为多个子问题,并且这些子问题在求解过程中会被多次重复计算。例如,在计算斐波那契数列时,…...
AWTK 骨骼动画控件发布
Spine 是一款广泛使用的 2D 骨骼动画工具,专为游戏开发和动态图形设计设计。它通过基于骨骼的动画系统,帮助开发者创建流畅、高效的角色动画。本项目是基于 Spine 实现的 AWTK 骨骼动画控件。 代码:https://gitee.com/zlgopen/awtk-widget-s…...

