二叉树(4)——链式二叉树

1 二叉树的概念 

二叉树是

  1. 空树
  2. 非空:根节点,根节点的左子树、根节点的右子树组成的。
  • 二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。

2 二叉树的遍历

2.1 前序、中序以及后序遍历

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

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历

  1. 前序遍历(Preorder Traversal 亦称先序遍历):访问根结点的操作发生在遍历其左右子树之前。根 左子树 右子树
  2. 中序遍历(Inorder Traversal):访问根结点的操作发生在遍历其左右子树之中(间)。左子树 根 右子树
  3. 后序遍历(Postorder Traversal):访问根结点的操作发生在遍历其左右子树之后。        左子树 右子树 根
  • 由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树
  • NLR、LNR和LRN分别又称为先根遍历、中根遍历和后根遍历。

要注意:空NULL才是结束条件,而不是叶子。 只有看到NULL了才不往下遍历。

2.2 二叉树链式结构的实现 

在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。

2.2.1 手动构建二叉树 

//二叉树节点结构体
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}TreeNode;

//手动构建一个二叉树
TreeNode* BuyTreeNode(int x)
{
	TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
	assert(node);
    //给出根节点,左右子树记为空
	node->data = x;
	node->left = NULL;
	node->right = NULL;

	return node;
}

TreeNode* CreateTree()
{
	TreeNode* node1 = BuyTreeNode(1);
	TreeNode* node2 = BuyTreeNode(2);
	TreeNode* node3 = BuyTreeNode(3);
	TreeNode* node4 = BuyTreeNode(4);
	TreeNode* node5 = BuyTreeNode(5);
	TreeNode* node6 = BuyTreeNode(6);
	TreeNode* node7 = BuyTreeNode(7);

    //连接树
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	node5->right = node7;

	return node1;
}
  • 注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后序详解重点讲解。

2.2.2 二叉树遍历代码实现

//前序
void PrevOrder(TreeNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

//中序
void InOrder(TreeNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}
 
 
//后序
void PostOrder(TreeNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
 
	PostOrder(root->left);//左
	PostOrder(root->right);//右
	printf("%d ", root->data);//根
}
 
int main()
{
	TreeNode* root = CreateTree();
	// 二叉树前序遍历
	PreOrder(node);
	printf("\n");
	// 二叉树中序遍历
    InOrder(node);
	printf("\n");
	// 二叉树后序遍历
    PostOrder(node);
 
}

前序遍历

递归理解:先进入函数,然后递归返回,直到全部返回完毕,函数结束。

中序遍历

 2.2.3 计算二叉树节点个数

方法一:遍历
问题解决
  • 下面这样可以吗?
int TreeSize(TreeNode* root)
{
	int size = 0;
	if (root == NULL)
	{
		return 0;
	}

	++size;

	TreeSize(root->left);
	TreeSize(root->right);

	return size;
}


不可以。我们要知道,这样会递归出来很多个栈帧,而每一个栈帧都会创建一个新的size,最后size并没有实现累加。

针对以上问题,我们用静态static int size定义size可以解决问题:不把size放进栈帧,而是放进静态区。

  • 但如果我们再次调用一下函数,会发现size的值变成了14。这是为什么呢?

原因:局部的静态变量只会被初始化一次。静态局部变量生命周期是程序结束,如果我们重复调用TreeSize函数,它会以上一次的结果为初始值进行累加,那么节点个数就会成倍增长。

  • 如果每次用完将size置0呢?

也不可以,因为虽然局部静态变量的生命周期是在全局,但是它的作用域只在当前函数。而在函数里我们并不能找到地方将其置0。

根据以上问题,我们可以用全局变量,每次调用完函数以后将其置0即可。

代码实现
int size = 0;

void TreeSize(TreeNode* root)
{
	if (root == NULL)
	{
		return;
	}

	++size;

	TreeSize(root->left);
	TreeSize(root->right);
}

int main()
{
	TreeNode* root = CreateTree();

	//输出节点个数size的值
	size = 0;
	TreeSize(root);
	printf("TreeSize:%d\n",size);

	size = 0;
	TreeSize(root);
	printf("TreeSize:%d\n", size);

	size = 0;
	TreeSize(root);
	printf("TreeSize:%d\n", size);

	return 0;
}

方法二:递归 (首选思路)
代码实现
int TreeSize(TreeNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
 
	return TreeSize(root->left) + TreeSize(root->right) + 1;
}

int main()
{
    TreeNode* root = CreateTree();

	printf("TreeSize:%d\n", TreeSize(root));
	printf("TreeSize:%d\n", TreeSize(root));
	printf("TreeSize:%d\n", TreeSize(root));
}

也可以用三目运算符简写如下 

int TreeSize(TreeNode* root)
{
	return root == NULL ? 0 : TreeSize(root->left) +TreeSize(root->right) + 1;
}
思路分析

可看成校长安排数 学校人数问题。 

要控制好子问题分治代码返回条件

  • 分治:根+左子树的节点个数+右子树的节点个数 
  • 返回条件:空树返回0


 2.2.4 计算二叉树叶子节点个数

  • 分治:左子树的叶子节点+右子树的叶子节点
  • 返回条件:若为空树返回0,若为叶子节点则返回1。
代码实现 
// 叶子节点的个数
int TreeLeafSize(TreeNode* root)
{
	// 空 返回0
	if (root == NULL)
		return 0;
	// 不是空,是叶子 返回1
	if (root->left == NULL
		&& root->right == NULL)
		return 1;

	// 不是空 也不是叶子  分治=左右子树叶子之和
	return TreeLeafSize(root->left) +
		TreeLeafSize(root->right);
}

2.2.5 计算二叉树的高度

  • 分治:左子树和右子树中高的子树+1(每一层都要进行筛选)
  • 返回条件:空树NULL返回0

代码实现
方法一
int TreeHeight(TreeNode* root)
{
	if (root == NULL)
		return 0;
	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);

	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
 方法二
#include<math.h>

int TreeHeight(TreeNode* root)
{
	if (root == NULL)
		return 0;

	return fmax(TreeHeight(root->left), TreeHeight(root->right)) + 1;
}

注意:以下这种写法,在OJ里可能会过不去,因为如果数很多很多就会调用次数太多,有大量的重复计算,超出时间限制。

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

 下一节我们继续讲解二叉树第K层的节点个数的代码实现。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

单片机学习笔记---AD模数转换DA数模转换

目录 AD模数转换 XPT2046.c XPT2046.h main.c DA数模转换 main.c 上一篇博客讲了AD/DA转换的工作原理&#xff0c;也介绍了运算放大器的工作原理&#xff0c;这节开始代码演示&#xff01; AD模数转换 新创建一个工程&#xff1a;AD模数转换 第一个工程将用到LCD1602和…

Day20 -- learning english

一、积累 1.gulp 2.clog 3.artery 4.bloat 5.kidnap 6.groom 7.prey 8.cargo 9. jerk 10.treadmill 11.shatter 12. acrobatic 13. aggravate 14.moldy 15.curl 16.manual 17.slay 18.sibling 19.hatch 20.dense 二、练习 1.牛津原译 Gulp /ɡʌlp 1.~ sth (down)to swallow …

[计算机网络]深度学习传输层TCP协议

&#x1f493; 博客主页&#xff1a;从零开始的-CodeNinja之路 ⏩ 收录专栏&#xff1a;深度学习传输层TCP协议 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 [计算机网络]深度学习传输层TCP协议 前提概括一: TCP协议段格式二:确认应答三:超时重传四:…

YOLO v5项目实战 P5 解决运行detect文件时设置了--view-img但是显示不出来的问题

up主讲的实时显示目标检测后的图片的两种方法&#xff1a; &#xff08;1&#xff09;在下面的Terminal中输入下列命令&#xff1a; python detect.py --view-img &#xff08;2&#xff09;点击进入右上方的detect的Edit Configurations 然后在这个参数这里输入 --view img…

企业建站用什么服务器,多线BGP线路服务器值得信赖

随着数字化时代的到来&#xff0c;很多企业会选择建立自己的网站&#xff0c;让企业网站成为对外展示、业务开展的平台。但是有的企业在建站时&#xff0c;选择了不适合的服务器&#xff0c;导致出现访问延迟、数据加载缓慢等问题&#xff0c;对企业的形象和业务造成很大影响。…

简析剩余电流动作继电器在油气田站场内监测路灯接地方式

安科瑞电气股份有限公司 上海嘉定 201801 【摘要】油气站站场内路灯接地方式多采用TT系统&#xff0c;部分采用TN-S系统&#xff0c;但无论TT系统还是TN-S系统均存在相应问题&#xff0c;为解决相应问题&#xff0c;本文建议油气田站场内路灯接地方式采用TN-S系统局部TT系统。…

SQL补充:窗口函数

SQL窗口函数 结合order by关键词和limit关键词是可以解决很多的topN问题&#xff0c;比如从二手房数据集中查询出某个地区的最贵的10套房&#xff0c;从电商交易数据集中查询出实付金额最高的5笔交易&#xff0c;从学员信息表中查询出年龄最小的3个学员等。 但是&#xff0c;…

C/C++数据结构——剖析排序算法

1. 排序的概念及其运用 1.1 排序的概念 https://en.wikipedia.org/wiki/Insertion_sorthttps://en.wikipedia.org/wiki/Insertion_sort 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的…

自然语言编程系列(一):自然语言和程序语言介绍

1.自然语言和程序语言 自然语言和程序语言是两种截然不同但又相互关联的语言体系&#xff0c;它们分别服务于人类日常交流和计算机指令执行。 自然语言&#xff1a; 定义&#xff1a;自然语言是指人类在日常生活中使用的语言&#xff0c;如英语、汉语、法语等。它是非正式且灵…

FPFH特征描述符、对应关系可视化以及ICP配准

一、FPFH特征描述符可视化 C #include <pcl/point_types.h> #include <pcl/point_cloud.h> #include <pcl/search/kdtree.h> #include <pcl/io/pcd_io.h> #include <pcl/features/normal_3d_omp.h>//使用OMP需要添加的头文件 #include <boo…

Python一级考试笔记

Python一级考试笔记【源源老师】 前置知识&#xff1a;&#xff08;了解即可&#xff09; Python常见的几种编程环境&#xff1a;IDLE&#xff08;自带&#xff09;、Visual Studio Code、Jupyter、pyCharm&#xff1b; python版本&#xff1a;python3 和 python2&#xff08;…

开源模型应用落地-工具使用篇-向量数据库(三)

一、前言 通过学习"开源模型应用落地"系列文章&#xff0c;我们成功地建立了一个完整可实施的AI交付流程。现在&#xff0c;我们要引入向量数据库&#xff0c;作为我们AI服务的二级缓存。本文将详细介绍如何使用Milvus Lite来为我们的AI服务部署一个前置缓存。 二、术…

论文阅读_用模型模拟记忆过程

英文名称: A generative model of memory construction and consolidation 中文名称: 记忆构建和巩固的生成模型 文章: https://www.nature.com/articles/s41562-023-01799-z 代码: https://github.com/ellie-as/generative-memory 作者: Eleanor Spens, Neil Burgess&#xff…

python+pytest自动化测试函数测试类测试方法的封装

前言 今天呢&#xff0c;笔者想和大家聊聊pythonpytest接口自动化中将代码进行封装&#xff0c;只有将测试代码进行封装&#xff0c;才能被测试框架识别执行。 例如单个接口的请求代码如下&#xff1a; 1 2 3 4 5 6 import requests headers { "user-agent":…

证明之三条看似显然实则需要证明的陈述

三条看似显然实则需要证明的陈述 “表面显然的数学定理&#xff1a;隐藏的证明之谜” 较高等的数学中&#xff0c;有一点让很多人感到费解&#xff1a;其中有一些定理看上去非常显然&#xff0c;简直无须证明。遇到这样的定理时&#xff0c;人们常常会问&#xff1a;“如果这…

海外媒体发稿:掌握这8个东南亚媒体发稿的技巧-华媒舍

在如今的数字化时代&#xff0c;媒体的地位越来越重要&#xff0c;尤其在东南亚地区。了解如何在关键时刻掌握东南亚媒体发稿的技巧是非常重要的。本文将介绍8个在东南亚地区重要的媒体发稿技巧&#xff0c;帮助您更好地传达信息。 1. 熟悉目标媒体 要掌握东南亚媒体发稿的技巧…

如何基于YAML设计接口自动化测试框架?看完秒会!

在设计自动化测试框架的时候&#xff0c;我们会经常将测试数据保存在外部的文件&#xff08;如Excel、YAML、CSV&#xff09;或者数据库中&#xff0c;实现脚本与数据解耦&#xff0c;方便后期维护。目前非常多的自动化测试框架采用通过Excel或者YAML文件直接编写测试用例&…

.NET Core WebAPI中使用Log4net 日志级别分类并记录到数据库

一、效果 记录日志为文档 记录日志到数据库 二、添加NuGet包 三、log4net.config代码配置 <?xml version"1.0" encoding"utf-8" ?> <log4net><!-- Debug日志 --><appender name"RollingFileDebug" type"log4net…

抓包分析 TCP 协议

TCP 协议是在传输层中&#xff0c;一种面向连接的、可靠的、基于字节流的传输层通信协议。 环境准备 对接口测试工具进行分类&#xff0c;可以如下几类&#xff1a; 网络嗅探工具&#xff1a;tcpdump&#xff0c;wireshark 代理工具&#xff1a;fiddler&#xff0c;charles&…

论文阅读-EMS: History-Driven Mutation for Coverage-based Fuzzing(2022)模糊测试

一、背景 本文研究了基于覆盖率的模糊测试中的历史驱动变异技术。之前的研究主要采用自适应变异策略或集成约束求解技术来探索触发独特路径和崩溃的测试用例&#xff0c;但它们缺乏对模糊测试历史的细粒度重用&#xff0c;即它们在不同的模糊测试试验之间很大程度上未能正确利用…