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

news2024/10/5 23:28:41

数据结构——二叉树的操作(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.coloradmin.cn/o/1626455.html

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

相关文章

ezplot--Matlab学习

目录 一、代码 二、效果 ​编辑 三、ezplot讲解 四、如何自定义一个函数 一、代码 clc; clear; t0:32; x4(t) cos(2*pi*t/4).*sin(2*pi*t/4); x8(t) cos(2*pi*t/8).*sin(2*pi*t/8); x16(t) cos(2*pi*t/16).*sin(2*pi*t/16); subplot(3,1,1) ezplot(x4,[0,32]); subplot…

怎样选购内衣洗衣机?2024年5款最新推荐机型种草

随着科技的不断发展&#xff0c;内衣洗衣机成为了家家户户必备的小家电之一&#xff0c;为我们的生活带来了极大的便利。但面对市场上众多的内衣洗衣机品牌&#xff0c;如何选择一款质量好的内衣洗衣机呢&#xff1f;本文将为您推荐5款最新的内衣洗衣机品牌&#xff0c;从而帮助…

冯唐成事心法笔记 —— 知人

系列文章目录 冯唐成事心法笔记 —— 知己 冯唐成事心法笔记 —— 知人 冯唐成事心法笔记 —— 知世 冯唐成事心法笔记 —— 知智慧 文章目录 系列文章目录PART 2 知人 人人都该懂战略人人都该懂战略第一&#xff0c;什么是战略第二&#xff0c;为什么要做战略第三&#xff0…

【GitHub】如何在github上提交PR(Pull Request) + 多个pr同时提交、互不干扰

【GitHub】如何在github上提交PR(Pull Request 写在最前面1. 准备工作1.1 注册 GitHub 账号1.2 了解 Git 基础1.3 找到一个项目 2. 创建你的 PR2.1 Fork 和克隆仓库2.2 创建一个新的分支2.3 进行更改2.4 推送更改到 GitHub2.5 创建 Pull Request 3. 优化你的 PR3.1 保持提交清晰…

投资标的参考

1、中央汇金投资有限责任公司 1.1、香港中央结算有限公司 2、中央汇金投资有限责任公司持股列表 _ 东方财富网_ 数据频道东方财富网提供十大流通股东数据、十大股东数据、股东持股明细、股东持股变动统计、股东持股分析、股东持股统计、股东协同等数据&#xff0c;充分展示股东…

Java中的ArrayList

ArrayList<E>的特点 可调整大小的数组实现 <E>:是一种数据类型 ArrayList的构造方法 ArrayList list new ArrayList();创建一个空的集合对象 package dayhou40.day45; ​ import java.util.ArrayList; ​ public class Arraylisttest {public static void ma…

大数据第五天(操作hive的方式)

文章目录 操作hive的方式hive 存储位置hive 操作语法创建数据表的方式 操作hive的方式 hive 存储位置 hive 操作语法 创建数据表的方式 – 创建数据库 create database if not exists test我们创建数据库表的时候&#xff0c;hive是将我们的数据自动添加到数据表中&#xf…

uniapp——授权报错,选择合适的基础库

说明 我的小程序开发版本点击选择头像报错 更换基础库就好了

[华为OD] 给航天器一侧加装长方形或正方形的太阳能板 100

给航天器一侧加装长方形或正方形的太阳能板&#xff08;图中的红色斜线区域&#xff09;&#xff0c;需要先安装两个支 柱&#xff08;图中的黑色竖条&#xff09;&#xff0c;再在支柱的中间部分固定太阳能板。但航天器不同位置的支柱长度 不同&#xff0c;太阳能板的安装面…

JavaScript-3(内置对象+数组对象+字符串对象)

目录 1.预解析 2.对象 什么是对象 创建对象的三种方法 利用字面量创建方法 利用new Object创建对象 构造函数创建对象 new关键字 遍历对象 3.内置对象 Math对象 Math概述 Math随机数 Date日期对象 格式化日期 Date总的时间毫秒 4.数组对象 创建数组的两种方式…

tcp inflight 守恒算法背后的哲学

tcp inflight 守恒拥塞控制的正确性 很久以前我开始纠结 tcp 锯齿&#xff0c;很多年后我知道这叫 capacity-seeking&#xff0c;甚至说 tcp 属于 capacity-seeking protocol 的原因就是它早已深入人心的 aimd 行为&#xff0c;而该行为生成了 tcp 锯齿。 在消除锯齿&#xf…

01.Scala概述及环境配置

文章目录 [toc] 1.**Scala概述**2.**Scala环境搭建**2.1下载2.2环境变量配置 1.Scala概述 特点&#xff1a; 同样运行在JVM上&#xff0c;可以与现存程序同时运行。可直接使用Java类库。同Java一样静态类型。语法和Java类似&#xff0c;比Java更加简洁&#xff08;简洁而并不…

深度学习的瓶颈是什么!

深度学习主要的瓶颈&#xff1a; 数据依赖与标注问题&#xff1a;深度学习模型通常需要大量的标注数据来进行训练。然而&#xff0c;获取大量的标注数据不仅成本高昂&#xff0c;而且在某些领域&#xff08;如医疗、金融等&#xff09;中可能难以获取足够的标注数据。此外&…

换脸插件升级导致SDWebUI无法启动cannot import name ‘Undefined‘ from ‘pydantic.fields‘

今天在一台新的机器环境装了SDWEBUI&#xff0c;都使用最新的版本&#xff0c;升级了下换脸的插件&#xff0c;于是乎启动崩溃了。错误如下 Launching Web UI with arguments: --listen --skip-torch-cuda-test --disable-nan-check --skip-version-check --skip-python-versi…

简记在arduino安装esp32开发板包

主要参考来源&#xff1a;http://t.csdnimg.cn/Fuqg9&#xff0c;感谢原博主的文章&#xff0c;原文我有不够详细的地方本文会给出说明。 废话&#xff0c;不用看&#x1f447; 本人安装时常大概2小时&#xff0c;按理说有离线文件了应该很快&#xff0c;我这2小时属实有点慢了…

Docker-概念及配置(超详细)

docker 第一章 1、什么是docker 答&#xff1a;docker是一种容器引擎&#xff0c;通过docker可以将软件安装并且配置好以后&#xff0c;做成一个镜像文件。通过这个镜像文件可以快速的安装、配置软件环境 2、3个概念 【docker镜像】&#xff1a;将软件环境安装配置好以后产生…

wstunnel (websocket模式ssh)

接上一篇 修改客户端运行参数 ssh -o ProxyCommand"./wstunnel client -L stdio://%h:%p ws://192.168.254.131:8080" 127.0.0.1 其中127.0.0.1为服务端的本地ssh访问&#xff0c;可以修改为通过服务端访问其他设备的ssh服务。例如&#xff1a; ssh -o ProxyComma…

吉布提国家概况

吉布提国家概况 &#xff08;最近更新时间&#xff1a;2022年10月&#xff09; 【国 名】 吉布提共和国&#xff08;The Republic of Djibouti&#xff0c; La Rpublique de Djibouti&#xff09;。 【面 积】 2.32万平方公里。 【人 口】约100万。主要有伊萨族和阿法尔族。…

42-巩固练习(二)

42-1 函数的递归 1、问&#xff1a;关于递归的描述错误的是&#xff08;&#xff09; A.存在限制条件&#xff0c;当满足这个限制条件的时候。递归便不再维续 B.每次递归调用之后越来越接近这个限制条件 C.递归可以无限递归下去 D.递归层次太深&#xff0c;会出现栈溢出现…

C#基础|对象初始化器与构造方法对比总结

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 01 对象初始化器的作用 为了更加灵活的初始化对象的“属性”&#xff0c;是对构造化方法的补充。 02 构造方法总结 2.1、存在的必要性&#xff1a;一个类中&#xff0c;至少要有一个构造方法&#xff08;有无参数均…