【算法与数据结构】二叉树(前中后)序遍历

请添加图片描述

文章目录

  • 📝前言
  • 🌠 创建简单二叉树
    • 🌉二叉树的三种遍历
      • 🌠前序
        • 🌉中序遍历
      • 🌠后序遍历
    • 🌠二叉树节点个数
    • 🌉二叉树节点个数注意点
  • 🚩总结


📝前言

一棵二叉树是结点的一个有限集合,该集合:

  1. 或者为空
  2. 由一个根节点加上两棵别称为左子树和右子树的二叉树组成
    在这里插入图片描述

二叉树可以没有节点(空树)否则,它包含一个根节点,这个根节点最多可以有两个分支:左子树和右子树,左右子树也符合二叉树的定义,可以是空树,或者由根节点和其左右子树组成。
因此二叉树的定义采用的是递归的思想:一个二叉树要么为空,要么由根节点和其左右两个子二叉树组成。左右子树本身也符合二叉树的定义,可以递归定义下去。

本小节我们将学习二叉树的前中后序遍历!

🌠 创建简单二叉树

在学习二叉树的基本操作之前,需要先创建一棵二叉树,然后才能学习相关的基本操作。由于现在大家对二叉树结构的理解还不够深入,为了降低学习成本,这里手动快速创建一棵简单的二叉树,以便快速进入二叉树操作学习。等大家对二叉树结构有了一定了解之后,再深入研究二叉树的真正创建方式。

手插简单二叉树代码:

// 二叉树节点结构体定义
typedef struct BinTreeNode
{// 左子节点指针struct BinTreeNode* left;// 右子节点指针struct BinTreeNode* right;// 节点值int val;
}BTNode;// 创建节点,分配内存并返回
BTNode* BuyBTNode(int val)
{BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));// 空间分配失败if (newnode == NULL){perror("malloc fail");return NULL;}// 初始化节点值newnode->val = val;// 初始化左右子节点为NULLnewnode->left = NULL;newnode->right = NULL;return newnode;
}// 创建示例树
BTNode* CreateTree()
{// 创建节点1-6BTNode* n1 = BuyBTNode(1);BTNode* n2 = BuyBTNode(2);BTNode* n3 = BuyBTNode(3);BTNode* n4 = BuyBTNode(4);BTNode* n5 = BuyBTNode(5);BTNode* n6 = BuyBTNode(6);// 构建树结构n1->left = n2;n1->right = n4;n2->left = n3;n4->left = n5;n4->right = n6;return n1; // 返回根节点
}

二叉树的图像:
在这里插入图片描述
注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后序详解重点讲解。

🌉二叉树的三种遍历

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。
在这里插入图片描述

🌠前序

您说得对,我来补充一下前序遍历的注释:

前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
算法:
访问根节点 -> 前序遍历左子树 -> 前序遍历右子树

  • 即先访问根节点,然后遍历其左子树,再遍历其右子树。

在这里插入图片描述

注意:
递归基准条件是当根节点为NULL时返回。访问根节点要放在递归左右子树之前,这保证了根节点一定先于其子节点被访问。递归左子树和右子树的顺序不能调换,否则就不是前序遍历了。

代码:

void PreOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}printf("%d ", root->val);PreOrder(root->left);PreOrder(root->right);
}
int main()
{BTNode* root = CreateTree();PreOrder(root);printf("\n");
}

前序递归图解:
在这里插入图片描述

运行:

🌉中序遍历

中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。中序遍历是在遍历一个结点的左子树后,然后访问这个结点,最后遍历它的右子树。

void InOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}InOrder(root->left);printf("%d ", root->val);InOrder(root->right);
}

在这里插入图片描述

🌠后序遍历

后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
后序遍历是先遍历一个结点的左右子树,最后再访问这个结点。

void PostOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}PostOrder(root->left);PostOrder(root->right);printf("%d ", root->val);
}

后序运行图:
在这里插入图片描述
由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。
在这里插入图片描述

🌠二叉树节点个数

这里分别实现前序、中序和后序遍历方式统计二叉树节点个数:

前序遍历:

int PreOrderCount(BTNode* root) 
{if(root == NULL) return 0;count++;  PreOrderCount(root->left);PreOrderCount(root->right);return count;
}int TreeSize(BTNode* root) 
{if(root == NULL) return 0;  count = 0;PreOrderCount(root);return count;
}

中序遍历:

int InOrderCount(BTNode* root) 
{if(root == NULL) return 0;InOrderCount(root->left);count++;InOrderCount(root->right);return count;
}int TreeSize(BTNode* root) 
{if(root == NULL) return 0;count = 0;  InOrderCount(root);return count;
}

后序遍历:

int PostOrderCount(BTNode* root) 
{if(root == NULL) return 0;PostOrderCount(root->left);PostOrderCount(root->right);count++;return count;
}int TreeSize(BTNode* root) 
{if(root == NULL) return 0;count = 0;PostOrderCount(root);return count;
}

三种遍历方式都是通过递归遍历每个节点,并在遍历每个节点时将统计变量count加1,最终count的值即为树的节点总数。

🌉二叉树节点个数注意点

注意当我们TreeSize函数使用了static变量size来统计节点个数,static变量的值会在函数调用之间保留,所以第二次调用TreeSize时,size的值会继续增加,导致统计结果叠加。

int TreeSize(BTNode* root)
{static int size = 0;if (root == NULL)return 0;else++size;TreeSize(root->left);TreeSize(root->right);return size;
}
int main()
{printf("TreeSize : %d\n", TreeSize(root));printf("TreeSize : %d\n", TreeSize(root));
}

代码运行:

在这里插入图片描述

改进

为了解决使用static变量导致的结果叠加问题,可以考虑使用以下方法:

  1. 每次调用TreeSize前重置size为0:
int TreeSize(BTNode* root) {static int size = 0;size = 0; // reset sizeif (root == NULL) return 0;else++size;TreeSize(root->left);TreeSize(root->right);return size;
}
  1. 不使用static变量,直接返回递归调用的结果:
int TreeSize(BTNode* root) 
{if (root == NULL)return 0;else return 1 + TreeSize(root->left) + TreeSize(root->right);
}

如果当前节点为NULL,直接返回0否则,返回:当前节点本身为1,加上左子树的节点数(TreeSize(root->left)返回值),加上右子树的节点数(TreeSize(root->right)返回值)

  1. 将size定义为函数参数,每次递归传递:
int TreeSize(BTNode* root, int* size) 
{if (root == NULL) return 0;*size += 1;TreeSize(root->left, size);TreeSize(root->right, size);return *size;
}
int main()
{// 调用int size = 0;TreeSize(root, &size);
}

🚩总结

请添加图片描述

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

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

相关文章

6-181 统计无向图的连通分量

要求实现函数,能够统计并返回无向图(顶点数不超过100)的连通分量(极大连通子图)数,可增加自定义函数或借助STL(标准模板库)之queue(队列)。无向图采用邻接矩阵存储,输入的顶点从1开始编号,但顶点下标从0开始使用。 函数接口定义: int CountSubGraph(int mat[][N…

C++提高笔记(六)---STL函数对象、STL常用算法(遍历、查找)

1、STL-函数对象 1.1函数对象 1.1.1函数对象概念 概念: 重载函数调用操作符的类,其对象常称为函数对象 函数对象使用重载的()时,行为类似函数调用,也叫仿函数 本质:函数对象(仿函数)是一个类,不是一个…

linux常用指令

前言 大家好我是jiantaoyab,这篇文章给大家介绍Linux下常用的命令。 指令的本质也是Linux上的一些程序。 cd cd - 回到最近从哪来的路径 cd ~ 当前用户对应的工作目录(普通用户:/home/xx root用户:/root) cd / 去…

C语言经典面试题目(十九)

1、什么是C语言?简要介绍一下其历史和特点。 C语言是一种通用的高级计算机编程语言,最初由贝尔实验室的Dennis Ritchie在1972年至1973年间设计和实现。C语言被广泛应用于系统编程、应用程序开发、嵌入式系统和操作系统等领域。它具有高效、灵活、可移植…

【vscode】vscode重命名变量后多了很多空白行

这种情况,一般出现在重新安装 vscode 后出现。 原因大概率是语言服务器没设置好或设置对。 以 Python 为例,到设置里搜索 "python.languageServer",将 Python 的语言服务器设置为 Pylance 即可。

React全家桶及原理解析-lesson4-Redux

lesson4-react全家桶及原理解析.mov React全家桶及原理解析 React全家桶及原理解析 课堂⽬标资源起步Reducer 什么是reducer什么是reduceRedux 上⼿ 安装reduxredux上⼿检查点react-redux 异步代码抽取Redux拓展 redux原理 核⼼实现中间件实现redux-thunk原理react-redux原理 实…

AI和信号处理推荐书单

推荐AI书单 1、《动手学深度学习-pytorch版》 https://item.m.jd.com/product/10068173196371.html?utm_sourceiosapp&utm_mediumappshare&utm_campaignt_335139774&utm_termCopyURL&ad_odshare&gxRnAomTM2bWfQmswcp40mDrUkxA7sLkk&gxdRnAoymFZOTXe…

【数据可视化】Echarts官方文档及常用组件

个人主页 : zxctscl 如有转载请先通知 文章目录 1. 前言2. Echarts官方文档介绍3. ECharts基础架构及常用术语3.1 ECharts的基础架构3.2 ECharts的常用术语3.2.1 ECharts的基本名词3.2.2 ECharts的图表名词 4. 直角坐标系下的网格及坐标轴4.1 直角坐标系下的网格4.2…

关于BFF

BFF(Backend For Frontend)是一种架构设计模式,用于解决多端(如Web、移动端等)共用一个后端服务时的问题。BFF的主要目标是将前端与后端的业务逻辑分离,使得前端可以根据自身的需求定制接口和数据&#xff…

C++的语法

可能需要用到存储各种数据类型(比如字符型、宽字符型、整型、浮点型、双浮点型、布尔型等) 下表显示了各种变量类型在内存中存储值时需要占用的内存,以及该类型的变量所能存储的最大值和最小值。 注意:不同系统会有所差异 #inc…

CentOS7 操作firewall防火墙

常用命令 开启关闭防火墙 systemctl start/status/stop/disable firewalld查看默认区域名称 $ firewall-cmd --get-default-zone public查看区域信息 firewall-cmd --get-active-zones查看指定接口所属区域 firewall-cmd --get-zone-of-interfaceeth0查看防火墙配置 # 查…

Qt Excel文件读写

QAxObject是Qt框架中用于与ActiveX控件和COM对象进行交互的类。它使得在Qt应用程序中嵌入和使用ActiveX控件,或者操作COM对象成为可能。通过QAxObject,你可以在Qt中方便地操作Excel、Word等Office应用程序,以及许多其他支持ActiveX或COM技术的…

AI人工智能小程序系统开发

开发AI人工智能小程序系统需要以下步骤: 1. 确定需求:了解客户对人工智能小程序的期望,并分析系统的实际应用场景。 2. 设计架构:选择合适的技术框架和人工智能算法,进行小程序系统架构的设计。 3. 数据采集和处理&…

诺视科技完成亿元Pre-A2轮融资,加速Micro-LED微显示芯片商业化落地

近日,Micro-LED微显示芯片研发商诺视科技(苏州)有限公司(以下简称“诺视科技”)宣布完成亿元Pre-A2轮融资,本轮融资由力合资本领投,老股东盛景嘉成、汕韩基金以及九合创投持续加码,这…

【漏洞复现】北京新网医讯技术有限公司云端客服管理系统存在SQL注入漏洞

免责声明:文章来源互联网收集整理,请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该…

【Linux】cat vim 命令存在着什么区别?

Linux 中的 cat 命令和 vim 命令之间存在一些显著的区别! cat 命令 首先,cat命令主要用于连接并显示文件的内容。它的原含义是“连接(concatenate)”,可以将多个文件的内容连接起来,并输出到标准输出流中&…

python模块

模块导入方式 模块需要在使用前进行导入 语法:[from 模块名] import [ 模块 | 类 | 函数 | *] [ as 别名 ] * 代表全部将该模块全部导入 from 模块名 import 功能名 #导入时间模块中的sleep方法 from time import sleep 注意:from可以省略 直接…

python使用appium在指定的坐标位置点击

在Appium中,要在指定的坐标位置执行点击操作,你可以使用TouchAction类配合press和release方法。下面是一个简单的示例代码,展示了如何在指定的(x, y)坐标位置执行点击操作: from appium import webdriver from appium.webdriver.…

掘根宝典之C++正向迭代器和反向迭代器详解

简介 迭代器是一种用于遍历容器元素的对象。它提供了一种统一的访问方式,使程序员可以对容器中的元素进行逐个访问和操作,而不需要了解容器的内部实现细节。 C标准库里每个容器都定义了迭代器,这迭代器的名字就叫容器迭代器 迭代器的作用类…

java Flink(四十二)Flink的序列化以及TypeInformation介绍(源码分析)

Flink的TypeInformation以及序列化 TypeInformation主要作用是为了在 Flink系统内有效地对数据结构类型进行管理,能够在分布式计算过程中对数据的类型进行管理和推断。同时基于对数据的类型信息管理,Flink内部对数据存储也进行了相应的性能优化。 Flin…