数据结构之二叉树详解[1]

在前面我们介绍了堆和二叉树的基本概念后,本篇文章将带领大家深入学习链式二叉树。

1.预备知识

2.二叉树结点的创建

3.二叉树的遍历 

3.1前序遍历

3.2中序遍历

3.3 后序遍历

4.统计二叉树的结点个数

5.二叉树叶子结点的个数

 6.二叉树第k层的结点个数

 7.总结


1.预备知识

由于二叉树的性质,我们在实现二叉树及其相关功能时会经常使用到递归的操作。

所以我们首先介绍一下递归算法。

在百度百科中,对于递归算法的定义是:某个函数直接或者间接地调用自身,这样原问题的求解就转换为了许多性质相同但是规模更小的子问题。

很多同学或许对这句话无法理解,没有关系。

在这里我们仅仅将其当作一个工具,在编写代码时,我们只需搞清楚繁杂的递归调用中的一次调用即可,不需要去深究他调用的过程。在文章后续的代码实现中,大家可以深刻体会到这句话的意思。

2.二叉树结点的创建

 既然是链式二叉树,对学习过链表的大家就很简单了。直接上代码

typedef char BTDataType;typedef struct BTreeNode//每一个结点的结构
{BTreeNode* left;//指向左树BTreeNode* right; //指向右树BTDataType data;//结点的值
}BTNode;BTNode* BuyNode(BTDataType x)//为新结点开辟空间
{BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));newnode->data = x;newnode->left = newnode->right = NULL;return newnode;
}

3.二叉树的遍历 

二叉树的遍历有四种:前序遍历、中序遍历、后序遍历、层序遍历。

今天我们在这里讲解前三种遍历,层序遍历请看下回分解。

3.1前序遍历

前序遍历就是先遍历根结点,在遍历左子树,最后遍历右子树。

理解了概念我们直接上代码。

void PreOrder(BTNode* root)//前序 根 左子树 右子树
{if (root == NULL){printf("NULL");return;}else {printf("%c", root->data);//打印根节点PreOrder(root->left);//递归遍历左子树PreOrder(root->right);//递归遍历右子树}
}

这里我们就使用到了递归。该如何理解呢。

我们只关注一次调用,既然遍历左树,

那么我们就让根去找,即 PreOrder(root->left)。

遍历右树,即PreOrder(root->right)。

中间是如何进行调用的细节我们不需要深究,我们只需要知道这个递归的写法。剩下的交给计算机。如果大家仍没理解,可以画个对照代码画一个草图。

 

3.2中序遍历

中序遍历:先遍历左子树,在找根,最后遍历右子树。

代码如下

void InOrder(BTNode* root)
{if (root == NULL){printf("NULL");return;}else {InOrder(root->left);//遍历左树printf("%c", root->data);//找根InOrder(root->right);//遍历右树}
}

这里的递归我们仍然按一样的方法进行理解,只看一层调用。大家可以看图理解。 

3.3 后序遍历

后序遍历:先遍历左子树,再遍历右子树,最后找根。

void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL");return;}else {PostOrder(root->left);//遍历左树PostOrder(root->right);//遍历右树printf("%c", root->data);//找根}
}

后序的图大家就自己画画吧。 

4.统计二叉树的结点个数

 如何统计二叉树的结点个数呢。

思路很简单,没遍历一个非空结点,个数加一。我们看下面代码。

void TreeSize(BTNode* root)
{if (root == NULL)return;int count = 0;count++;//既然不为空,那么一定就有一个结点的存在TreeSize(root->left);TreeSize(root->right);
}

如果大家觉得这个代码对的话,那就掉入陷阱了!

在递归调用中,count存在于栈帧之中,并且递归的每一次调用都会开辟新的一块栈帧,这样的话我们的count就无法进行值的保存而达到连续的效果。所以这里我们不能这样进行实现。

下面我们介绍正确的写法。

void TreeSize(BTNode* root,int*p)//p为指向节点个数的指针
{if (root == NULL)return;++(*p);//结点个数++,既然不为空,那么一定有一个根结点,所以先++TreeSize(root->left, p);//遍历左子树的结点TreeSize(root->right, p);//遍历右子树的结点
}

我们在这里运用定义了一个外部指针变量,用来记录结点的个数,传入的是记录结点个数变量的地址。这样就可以避免 原来出现的问题了。

 其实这里还有个更加简单的方法,直接用root来记录结点个数。

int TreeSize(BTNode* root)
{return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}

5.二叉树叶子结点的个数

 既然可以得到结点总数,那叶子结点是不是也可以得到呢。我们先上代码再解释。

int BTreeLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->left == NULL && root->right == NULL) //叶子结点的特点{return 1;}return BTreeLeafSize(root->left) + BTreeLeafSize(root->right);//找叶子结点
}

既然要找叶子结点首先我们要知道叶子结点的性质:左右子树均为空。

所以我们在递归的时候需要加入叶子结点的判断条件,如果是叶子结点就要返回。

当然在进行递归前要判断根节点是否存在或者根结点是否有值,我们将其划分为代码中的一中情况。

 6.二叉树第k层的结点个数

我们该如何求任意一层的结点个数呢。

我们将求第k层的结点个数转化为求第k-1层结点的左右子树个树是不是就和我们求叶子结点个数大同小异了呢!

int BTreeLayerSize(BTNode* root,int k)
{if (root == NULL)return 0;if (k == 1)return 1;return BTreeLayerSize(root->left, k - 1) + BTreeLayerSize(root->right, k - 1);
}

我们加入需要的层数k,我们从根结点往下找第k层,如果要使其找到k层,就要使k=1。我们画图理解 。

我们假设要求第三层的结点个数,k=3。根结点为第一层,与其匹配的k=3,所以到找到第三层,就要使k--,简而言之,我们要找到的那一层匹配的k=1。这里可能有点绕,大家可以试着自己画图理解 。

 7.总结

 二叉树的内容到这里并没有结束,后序的内容需要用到队列的知识,所以我会在穿插队列的实现后继续为大家讲解二叉树的相关知识!敬请期待!

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

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

相关文章

589.N叉树的前序遍历

刷算法题: 第一遍:1.看5分钟,没思路看题解 2.通过题解改进自己的解法,并且要写每行的注释以及自己的思路。 3.思考自己做到了题解的哪一步,下次怎么才能做对(总结方法) 4.整理到自己的自媒体平台。 5.再刷重复的类…

高级个人主页

高级个人主页 效果图部分代码领取源码下期更新预报 效果图 部分代码 <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8" name"viewport" content"widthdevice-width, initial-scale1, maximum-scale1, use…

数据驱动测试在接口测试和网站测试中的应用

什么是数据驱动测试 据驱动测试是一种测试方法&#xff0c;其中测试数据和测试逻辑是分开的&#xff0c;测试数据被存储在外部源中&#xff08;如Excel表格、JSON文件、数据库等&#xff09;&#xff0c;测试逻辑则独立于测试数据。在测试过程中&#xff0c;测试数据被读取并传…

代码随想录算法训练营第五十三天

今天同事说他要离职啦&#xff0c;还挣挺多的&#xff0c;我也慢慢努力吧&#xff01;&#xff01; 儿子似乎有点斜颈&#xff0c;还好不是很大的病&#xff0c;儿子也开始面对人生的苦难啦。都好好加油生活&#xff01; 1143.最长公共子序列 二维可以理解一点。 class Solut…

用面向对象的思想编写实时嵌入式C程序

实时嵌入式系统的软件一般由C语言编写&#xff0c;程序结构基本上都是这样的&#xff1a; // 主程序 int main(void) {init(); // 初始化while(1){tick(); // 业务逻辑}return 0; }// 计时器 static unsigned int g_timer_tick_cnt 0; // 时钟中断回调 void isr_time…

[c++]多态的分析

多态详细解读 多态的概念多态的构成条件 接口继承和实现继承: 多态的原理:动态绑定和静态绑定 多继承中的虚函数表 多态的概念 -通俗的来说&#xff1a;当不同的对象去完成某同一行为时&#xff0c;会产生不同的状态。 多态的构成条件 必须通过基类的指针或者引用调用虚函数1虚…

超级简单的地图操作工具开发可疑应急,地图画点,画线,画区域,获取地图经纬度等

使用echars的地图画点,画线,画区域,获取地图经纬度等 解压密码:10086007 地图也是用临时的bmap.js和china.js纯离线二选一 一共就这么多文件 画点,画线,画区域 点击地图获取经纬度-打印到控制台,这样就能渲染航迹,多变形,结合其他算法算圆等等操作 下载资源:https://download…

Databend 开源周报第 144 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 了解 Databend …

Redis-详解(基础)

文章目录 什么是Redis&#xff1f;用Redis的特点&#xff1f;用Redis可以实现哪些功能&#xff1f;Redis的常用数据类型有哪些?Redis的常用框架有哪些?本篇小结 更多相关内容可查看 什么是Redis&#xff1f; Redis&#xff08;Remote DictionaryServer&#xff09;是一个开源…

【AI学习】对指令微调(instruction tuning)的理解

前面对微调&#xff08;Fine-tuning&#xff09;的学习中&#xff0c;提到指令微调。当时&#xff0c;不清楚何为指令微调&#xff0c;也一直没来得及仔细学习。 什么是指令微调&#xff1f;LLM经过预训练后&#xff0c;通过指令微调提升模型的指令遵循能力。所谓指令&#xf…

uniapp怎么使用jsx

安装vitejs/plugin-vue-jsx npm install vitejs/plugin-vue-jsx -Dvite.config.js配置 import { defineConfig } from "vite"; import uni from "dcloudio/vite-plugin-uni"; import vueJsx from vitejs/plugin-vue-jsxexport default defineConfig({plu…

全方位入门git-慕课网 笔记

目录 【上传github忽略某些文件】【配置用户名和邮箱】【想要删除不需要的文件时如何进行操作】【想要给文件重命名如何操作】【想要移动文件到其他位置时如何操作】【文件有变化时&#xff0c;如何查看前后变化】【操作失误的情况下如何实现一键还原】【不再追踪时如何实现撤销…

区块链 | NFT 水印:Review on Watermarking Techniques(一)

&#x1f34d;原文&#xff1a;Review on Watermarking Techniques Aiming Authentication of Digital Image Artistic Works Minted as NFTs into Blockchains 1 应用于 NFT 的水印技术 常见的水印技术类型可以分为&#xff1a; 可见 v i s i b l e \mathsf{visible} visi…

项目9-网页聊天室1(注册+Bycrpt加密)

1.准备工作 1.1.前端页面展示 1.2 数据库的建立 我们通过注册页面&#xff0c;考虑如何设计用户表数据库。 用户id&#xff0c;userId用户名&#xff0c;唯一&#xff0c;username用户密码&#xff0c;password&#xff08;包括密码和确认密码ensurePssword【数据库没有该字段…

【全开源】酷柚易汛ERP 源码部署/售后更新/上线维护

一款基于FastAdminThinkPHPLayui开发的ERP管理系统&#xff0c;帮助中小企业实现ERP管理规范化&#xff0c;此系统能为你解决五大方面的经营问题&#xff1a;1.采购管理 2.销售管理 3.仓库管理 4.资金管理 5.生产管理&#xff0c;适用于&#xff1a;服装鞋帽、化妆品、机械机电…

openssl 生成证书步骤

本地测试RSA非对称加密功能时&#xff0c;需要用到签名证书。本文记录作者使用openssl本地生成证书的步骤&#xff0c;并没有深入研究openssl&#xff0c;难免会有错误&#xff0c;欢迎指出&#xff01;&#xff01;&#xff01; 生成证书标准流程&#xff1a; 1、生成私钥&am…

jar包安装成Windows服务

一、前言 很多年前写过一篇《使用java service wrapper把windows flume做成服务》的文章&#xff0c;也是把jar包安装成windows服务&#xff0c;今天介绍另外一种更简便的方案。 二、正片 这次使用的工具是 winsw&#xff0c;一个Windows服务包装器。下面看详细介绍 首先从g…

运输层(计算机网络谢希仁第八版)——学习笔记五

课件&#xff1a;课程包列表 (51zhy.cn) 目录 运输层协议概述 用户报协议UDP 传输控制协议TCP概述 可靠传输的工作原理 TCP可靠传输的实现 TCP的流量控制 TCP的拥塞控制 TCP的运输连接管理 运输层协议概述 进程之间的通信 运输层的位置——只有位于网络边缘部分的主机的协议栈才…

Vue3实战笔记(13)—pinia安装笔记

文章目录 前言安装和配置pinia总结 前言 Pinia 是 Vue 的专属状态管理库&#xff0c;它允许你跨组件或页面共享状态。 Pinia是一个轻量级的状态管理库&#xff0c;它专注于提供一个简单的API来管理应用程序的状态。相比之下&#xff0c;Vuex是一个更完整的状态管理库&#xf…

【Android Studio】使用UI工具绘制,ConstraintLayout 限制性布局,快速上手

文章目录 一、前言二、绘制效果三、ConstraintLayout 使用方法3.1 创建布局文件3.2 替换配置3.3 设置约束&#xff0c;步骤13.4 设置约束&#xff0c;步骤23.5 其他设置 四、结束 一、前言 在进行Android APP开发过程中&#xff0c;减少layout嵌套即可改善UI的绘制性能&#x…