数据结构——二叉树的操作 (层序遍历)(C++实现)

数据结构——二叉树的操作(2)(C++实现)

  • 统计叶子结点个数
  • 统计结点个数
  • 层序遍历
    • 非递归方式
    • 递归方式

我们今天接着来看二叉树的操作,如果还没有看过上一篇的可以点击这里:

https://blog.csdn.net/qq_67693066/article/details/138163494

统计叶子结点个数

叶子结点就是左右孩子都没有的结点:
在这里插入图片描述判断叶子结点就是判断是否有左右孩子,如果都没有就为叶子结点:

        if(root->_leftChild== nullptr && root->_rightChild == nullptr){return 1;}

除了判断本身是否为叶子结点,我们也要到它的左右子树去看是否有叶子结点:

       if(root->_leftChild== nullptr && root->_rightChild == nullptr){return 1;}return _TotalLeaves(root->_leftChild) +_TotalLeaves(root->_rightChild);

同时,我们要防止空树的情况:

    //叶子结点int _TotalLeaves( _Node* const& root){if(root == nullptr){return 0;}if(root->_leftChild== nullptr && root->_rightChild == nullptr){return 1;}return _TotalLeaves(root->_leftChild) +_TotalLeaves(root->_rightChild);}

我们再封装一次:

    //叶子结点个数int TotalLeaves(){return _TotalLeaves(_root);}

我们可以测试一下:

#include "BTree.h"int main()
{BTree<int> bt;bt.Insert(20);bt.Insert(12);bt.Insert(35);bt.Insert(22);bt.Insert(1);bt.PrveOrder();std::cout<<std::endl;bt.InOrder();std::cout<<std::endl;bt.PostOrder();std::cout<<std::endl;std::cout <<"叶子结点个数:" << bt.TotalLeaves() << std::endl;return 0;
}

在这里插入图片描述

统计结点个数

统计结点个数,相对来说更简单,只要是一个结点,我们就返回1,表示一个结点:

    //统计结点个数int _NodeNumbers( _Node* const& root){//防止空树的情况if(root == nullptr){return 0;}return 1;}

统计完当前结点,还要统计它的左右子树的结点个数:

    int _NodeNumbers( _Node* const& root){//防止空树的情况if(root == nullptr){return 0;}return 1 + _NodeNumbers(root->_leftChild)+ _NodeNumbers(root->_rightChild); //本身 + 它的左右子树}
#include "BTree.h"int main()
{BTree<int> bt;bt.Insert(20);bt.Insert(12);bt.Insert(35);bt.Insert(22);bt.Insert(1);bt.PrveOrder();std::cout<<std::endl;bt.InOrder();std::cout<<std::endl;bt.PostOrder();std::cout<<std::endl;std::cout <<"叶子结点个数:" << bt.TotalLeaves() << std::endl;std::cout <<"结点个数:" << bt.NodeNumber() << std::endl;return 0;
}

在这里插入图片描述

层序遍历

层序遍历是一种较为特殊的遍历方式,它不依靠根,左子树,右子树的顺序,它是依靠二叉树的层数来遍历:
在这里插入图片描述
层次遍历是依次遍历各层的元素的算法,这种算法遍历出来就是每层的元素顺序。

层次遍历算法中我们要借助我们之前学过的数据结构——队列,因为在层次遍历算法中,我们依次要打印左孩子,右孩子,所以我们借助队列的性质,先入左孩子,然后出队时,左孩子最先被处理:

首先,每一层我们都可以用一个vector来储存:
在这里插入图片描述
然后用一个大的vector来储存这些vector:
在这里插入图片描述
然后我们用queue先压入根节点:
在这里插入图片描述然后20是第一层的vector,我们就压入第一层的vector:
在这里插入图片描述然后弹出20,将20的左孩子右孩子压入queue中:
在这里插入图片描述
然后将12压入vector[1],然后将12的左右孩子压入queue:

在这里插入图片描述然后将35压入vector[1]中,然后同样将35的左右孩子压入queue:
在这里插入图片描述然后同样的方法,将剩下的元素压入下一层的vector。了解了思想之后,我们开始写代码:

非递归方式

    // 定义一个函数 LevelOrder,用于实现二叉树的层序遍历std::vector<std::vector<T>> _LevelOrder(_Node* const& root){// 初始化结果容器,用于存放层序遍历得到的数据,每一层数据作为一个子向量存入std::vector<std::vector<T>> result;// 使用队列辅助遍历,初始时仅包含根节点std::queue<_Node*> queue;// 如果根节点为空,则直接返回空结果集if (!root){return result;}// 将根节点压入队列queue.push(root);// 当队列非空时,继续进行遍历while (!queue.empty()){// 初始化一个临时向量,用于存储当前层的所有节点数据std::vector<T> curResult;// 记录当前层队列的大小(即节点数)int curSize = queue.size();// 遍历当前层的所有节点for (int i = 0; i < curSize; i++){// 弹出队首节点_Node* front = queue.front();queue.pop();// 将当前节点的数据添加到当前层结果向量中curResult.push_back(front->_data);// 若当前节点有左子节点,将其压入队列,等待下一层遍历if (front->_leftChild)queue.push(front->_leftChild);// 若当前节点有右子节点,将其压入队列,等待下一层遍历if (front->_rightChild)queue.push(front->_rightChild);}// 将当前层遍历结果添加到总结果集中result.push_back(curResult);}// 遍历完成后,返回层序遍历结果集return result;}

递归方式

除了非递归方式,我们发现上面的方式都是一个模子里面刻出来的,所以我们也可以用递归的方式:

如果用递归方式,我们就不用队列来辅助了,因为每一次递归都会开一个新的函数栈帧,我们可以利用这个栈帧来区分层次:

// 定义一个成员函数 LevelOrder,用于获取二叉树的层序遍历结果
std::vector<std::vector<T>> LevelOrder()
{// 初始化结果容器,用于存放层序遍历得到的数据,每一层数据作为一个子向量存入std::vector<std::vector<T>> result;// 调用辅助递归函数,从根节点开始遍历LevelOrderHelper(_root, 0, result);// 返回层序遍历结果集return result;
}// 定义一个私有辅助递归函数 LevelOrderHelper,用于实现二叉树的层序遍历
void LevelOrderHelper(_Node* root, int level, std::vector<std::vector<T>>& result)
{// 如果当前节点为空,直接返回if (!root){return;}// 如果当前层级超出了结果容器的范围,添加一个新的子向量if (result.size() <= level){result.push_back(std::vector<T>());}// 将当前节点的数据添加到对应层级的子向量中result[level].push_back(root->_data);// 递归遍历左子树,传入下一层级编号LevelOrderHelper(root->_leftChild, level + 1, result);// 递归遍历右子树,传入下一层级编号LevelOrderHelper(root->_rightChild, level + 1, result);
}

这段代码定义了一个成员函数 LevelOrder,用于获取当前二叉树的层序遍历结果。它首先初始化一个结果容器,然后调用私有辅助递归函数 LevelOrderHelper,从根节点开始遍历,并将遍历结果存储在结果容器中。最后返回这个结果容器。

辅助递归函数 LevelOrderHelper 接收当前节点、所在层级以及结果容器作为参数。递归过程中,首先检查当前节点是否为空,若为空则直接返回。接着检查当前层级是否已存在于结果容器中,若不存在则添加一个新子向量。然后将当前节点数据添加到对应层级的子向量中。最后分别递归遍历左、右子树,传入下一层级编号。

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

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

相关文章

来自异国的客人 - 华为OD统一考试(D卷)

OD统一考试(D卷) 分值: 100分 题解: Java / Python / C++ 题目描述 有位客人来自异国,在该国使用m进制计数。 该客人有个幸运数字n(n<m),每次购物时,其总是喜欢计算本次支付的花费(折算为异国的价格后)中存在多少幸运数字。 问: 当其购买一个在我国价值k的产品时,…

市政行业乙级资质改革对公共交通工程的影响

资质门槛调整&#xff1a;改革可能会调整乙级资质的申请条件&#xff0c;例如注册资本、技术人员配置、业绩要求等&#xff0c;这直接影响到新企业和中小企业进入市场的难易程度。门槛的调整旨在提高行业整体的专业水平和服务质量。 业务范围变化&#xff1a;改革可能会重新定…

HCIP-Datacom-ARST必选题库_IPsec【3道题】

1.在IPsec中&#xff0c;能够用哪一个协议来实现数据的完整性? AES 3DES MD5 DES 2.以下关于IPsec密钥交换的描述&#xff0c;正确的是哪一项? IKE主模式支持NAT转换 IKE协商使用Bellham-Ford算法分发密钥 使用IKE协议主模式需要经历三个阶段:SA交换、密钥交换和ID交…

【Leetcode每日一题】 穷举vs暴搜vs深搜vs回溯vs剪枝_全排列 - 子集(难度⭐⭐)(65)

1. 题目解析 题目链接&#xff1a;78. 子集 这个问题的理解其实相当简单&#xff0c;只需看一下示例&#xff0c;基本就能明白其含义了。 2.算法原理 算法思路详解&#xff1a; 为了生成数组 nums 的所有子集&#xff0c;我们需要对数组中的每个元素进行“选择”或“不选择…

JavaScript-Vue入门

本文主要测分享Vue的一些基础 Vue简介 Vue.js 是一个构建数据驱动的 web 界面的渐进式框架。它的主要目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。 下是一些 Vue 的主要特点和概念&#xff1a; 1. 响应式数据绑定&#xff1a;Vue 使用基于 HTML 的模板语法…

Visual Studio安装MFC开发组件

MFC由于比较古老了&#xff0c;Visual Studio默认没有这个开发组件。最近由于一些原因&#xff0c;需要使用这个库&#xff0c;这就需要另外安装。 参考了网上的一些资料&#xff0c;根据实际使用&#xff0c;其实很多步骤不是必须的。 https://zhuanlan.zhihu.com/p/68117276…

Neo-reGeorg(webshell代理)-php

https://github.com/L-codes/Neo-reGeorg python加载需要的库 pip install requests 生成webshell代理文件 python .\neoreg.py generate -k 123456 其中&#xff0c;123456为密码&#xff0c;可以修改 生成后的文件路径&#xff1a; > neoreg_servers/tunnel.ashx …

alert() 函数功能

基本的 alert() 函数用法&#xff1a; 最基本的用法是通过 alert() 函数显示一条简单的文本消息&#xff0c;如下所示&#xff1a; alert("Hello, World!");这将在浏览器中显示一个消息框&#xff0c;其中包含文本 “Hello, World!”。 显示变量的值&#xff1a; a…

Java后台开发的前置说明

1.知识点逻辑 一个部分 都是先挑重点知识点讲解 然后根据这些重点知识点去完成一个项目的开发 然后在到返回来解决这个部分其他细枝末节的知识点 2.软件开发的分工 我们大致可以将软件开发分成四块&#xff1a; 1.前端开发(比如开发电脑中的京东 htmlcssjavascript) 2.移动开…

若依框架学习-springboot-gateway笔记

版本&#xff1a;若依框架微服务版本 学习内容&#xff1a;springboot-gateway 熔断降级踩坑 文档上使用的hystrix在springcloud3.1.8不再支持&#xff0c;建议改为使用CircuitBreaker&#xff0c;使用方式和hystrix几乎无差别&#xff0c;只是需要修改pom引用&#xff1a; …

什么是主数据管理?合理管理主数据,奠定企业数据战略基石

主数据&#xff08;Master Data&#xff09;是指在企业多个业务系统中重复使用、共享的、具有高价值的核心业务实体数据&#xff0c;如客户、产品、供应商、员工等&#xff0c;它们是企业运营和决策的基础&#xff0c;需要保持高度的准确性、一致性和完整性&#xff0c;以确保业…

Scala 05 —— 函数式编程底层逻辑

Scala 05 —— 函数式编程底层逻辑 该文章来自2023/1/14的清华大学交叉信息学院助理教授——袁洋演讲。 文章目录 Scala 05 —— 函数式编程底层逻辑函数式编程假如...副作用是必须的&#xff1f;函数的定义函数是数据的函数&#xff0c;不是数字的函数如何把业务逻辑做成纯函…

什么是 DNS?DNS设定时常见的问题都有哪些?

DNS&#xff08;Domain Name System&#xff09;即域名系统&#xff0c;它是互联网的一项服务。 它主要的作用是将域名转换为对应的 IP 地址&#xff0c;以便计算机能够识别和访问互联网上的各种资源。当我们在浏览器中输入域名时&#xff0c;DNS 会帮助我们找到与之对应的服务…

分布式与微服务区别?

1、概念角度&#xff1a; 分布式&#xff1a;把多个应用部署到多台服务器&#xff08;云&#xff09;上&#xff0c;多个应用之间相互协作&#xff0c;提高系统的扩展性和稳定性。 微服务&#xff1a;是分布式的一种实现方式。 2、粒度划分&#xff1a; 分布式&#x…

贪吃蛇详解

Win32 API介绍&#xff1a; 在写贪吃蛇这款游戏时需要用到一些有关Win32 API的知识&#xff0c; 接下来我会将设计到的知识点列举并讲解&#xff1a; 首先我们先了解一下Win32 API是什么&#xff0c;Windows这个多作业系统除了协调应⽤程序的执⾏、分配内存、管理资源之外&am…

普乐蛙VR航天航空体验馆VR双人旋转座椅元宇宙VR飞船

多长假来袭&#xff01;&#xff01;想为门店寻找更多新鲜有趣的吸粉体验&#xff1f;想丰富景区体验&#xff1f;别着急&#xff0c;小编为你准备了一款爆款设备——时光穿梭机&#xff0c;720无死角旋转&#xff01;&#xff01;吸睛、刺激体验&#xff0c;将亲子、闺蜜、情侣…

Java对象不再使用时,为什么要赋值为 null

相信大家在面试Java开发的时候,会遇到比较多的问题是Java的内存管理,这里面涉及到Java垃圾回收机制,以及JVM调优等等,那么今天跟大家讨论一个问题:Java对象不再使用时,为什么要赋值为 null ? 1、Java内存管理 在Java中,对象是在堆内存中分配的。 这部分内存用于存储…

数组的排序算法

1.冒泡排序法 原理如下&#xff1a;每次比较数组中相邻的两个数组元素的值&#xff0c;将较小的数排在较大的数前面&#xff0c;可实现数组元素的从小到大排序&#xff1b;每次将较大的数排在较小的数前面&#xff0c;可实现数组元素从大到小排序。 /**每次比较数组相邻的两个…

【Flutter 面试题】 可以嵌套使用 Scaffold 吗?为什么或者为什么不?

【Flutter 面试题】 可以嵌套使用 Scaffold 吗?为什么或者为什么不? 文章目录 写在前面口述回答补充说明写在前面 🙋 关于我 ,小雨青年 👉 CSDN博客专家,GitChat专栏作者,阿里云社区专家博主,51CTO专家博主。2023博客之星TOP153。 👏🏻 正在学 Flutter 的同学,…

科技论文网站:中国科技论文在线

文章目录 1. Intro2. Main3. Cons Evaluation彩蛋&#xff1a;科学素质 这是作者最后一次发 这种类型的宣传 科普文章 1. Intro 中国科技论文在线是经教育部批准&#xff0c;由教育部科技发展中心主办&#xff0c; 利用现代信息技术手段&#xff0c;打破传统出版物的概念&…