二叉树的遍历

2023-09-14 17:58:49

Ⅰ、二叉树基本介绍

         

1.1、二叉树的定义

二叉树(binary tree)是指树中节点的度不大于2的有序树,它是一种最简单且最重要的树。

二叉树的递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树 。 

1.2、特殊的二叉树

1、满二叉树:如果一棵二叉树只有度为0的节点和度为2的节点,并且度为0的节点在同一层上,则这棵二叉树为满二叉树 。

 

2、完全二叉树:深度为k,有n个节点的二叉树当且仅当其每一个节点都与深度为k的满二叉树中编

号从1到n的节点一一对应时,称为完全二叉树 。

完全二叉树的特点是叶子节点只可能出现在层序最大的两层上,并且某个节点的左分支下子孙的最

大层序与右分支下子孙的最大层序相等或大1 。

Ⅱ、二叉树的性质

性质1:二叉树的第i层上至多有2i-1(i≥1)个节点 。

性质2:深度为h的二叉树中至多含有2h-1个节点

性质3:若在任意一棵二叉树中,有n0个叶子节点,有n2个度为2的节点,则必有n0=n2+1

性质4:具有n个节点的满二叉树深为log2n+1。

性质5:若对一棵有n个节点的完全二叉树进行顺序编号(1≤i≤n),那么,对于编号为i(i≥1)的节点:

当i=1时,该节点为根,它无双亲节点 。

当i>1时,该节点的双亲节点的编号为i/2 。

若2i≤n,则有编号为2i的左节点,否则没有左节点 。

若2i+1≤n,则有编号为2i+1的右节点,否则没有右节点 。


Ⅲ、二叉树的四种遍历方式

如果二叉树中元素数目为n。这四种遍历算法的空间复杂性均为O (n),时间复杂性为O(n)。

3.1、二叉树的前序遍历

遍历顺序:跟 左 右 

前序遍历首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根

节点,然后遍历左子树,最后遍历右子树。

这个二叉树前序遍历的结果是:1 2 3 4 5 6

但是实际上是:1 2 3 null null null 4 5 null null 6 null

我们为了看上去方便就省去了null.

代码实现:

void PrevOrder(BTNode* root) {
	if (root == NULL) {
		printf("NULL ");

		return;
	}

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

3.2、中序遍历

遍历顺序:左 根 右

中序遍历(LDR)是二叉树遍历的一种,也叫做中根遍历、中序周游。在二叉树中,中序遍历首先

遍历左子树,然后访问根结点,最后遍历右子树。

中序遍历结果:3 2 1 5 4 6

实际上的结果应该是:null 3 null 2 null 1 null 5 null 4 null 6 null

代码实现:

void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}

	InOrder(root->left);
	printf("%d ", root->val);
	InOrder(root->right);
}

3.3、后序遍历

遍历顺序:左 右 跟

后序遍历首先遍历左子树,然后遍历右子树,最后访问根结点,在遍历左、右子树时,仍然先遍历

左子树,然后遍历右子树,最后遍历根结点。

后续遍历结果:3 2 5 6 4 1

实际上应该是:null null 3 null 2 null null 5 null null 6 4 1 

代码实现:

void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}

	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->val);
}

3.4、层次遍历

遍历顺序:一层一层的遍历

二叉树的层次遍历 ,顾名思义就是指从二叉树的第一层(根节点)开始,从上至下逐层遍历,

同一层中,则按照从左到右的顺序对节点逐个访问。在逐层遍历过程中,按从顶层到底层的次序访

问树中元素,在同一层中,从左到右进行访问。

层次遍历结果:1 2 4 3 5 6

实际遍历结果:1 2 4 3 null 5 6 null null null null null null 

代码实现:

void levelorder(BinaryTree *tree)
{
    Queue q = initQueue();
    BinaryTree *temp = tree;
    put(&q, temp);
    while (!IsEmptyQueue(q))
    {
        temp = get(&q);
        visited(temp);
        if (temp->left != NULL)
            put(&q, temp->left);
        if (temp->right != NULL)
            put(&q, temp->right);
    }
}

Ⅳ、二叉树求节点

4.1、求二叉树总节点个数

方法一

我们惯用的方式,就是创建一个变量Size,然后每遍历一个节点Size++。

这种方式有很大的局限性,就是我们在遍历二叉树时采用的是递归的方式,如果把Size放到函数里面就会出现每次调用时Size都是同一个值,没办法累加。

这里我们可以采用 静态函数 stati来修饰Size,这样Size只被调用一次,就可以达到累加的效果。

看代码:

int TreeSize(BTNode* root)
{
	static int size = 0;
	if (root == NULL)
		return 0;
	else
		++size;

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

	return size;
}

这样我们的得到的结果就是:6。

但是:当我们再次调用这个函数时我们会不会还得到结果6呢?  答案是不会,他会返回12。因为我们刚才提到静态变量修饰的Size时,Size在整个程序中只会被调用一次。所以这种方式有很大的局限性。我们不妨换个思路来解决

方法二

我们还是采用递归的方式,当遍历到叶子节点是我们就让他返回1,一层一层的往上返回,最终在加上1(根节点),我们是不是就可以得到我们整棵树的节点个数了呢?

看代码:

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

4.2、求二叉树叶子结点的个数

有了上面求二叉树总节点个数方法二的铺垫,这个问题也就很容易想到了。

这里我们要怎么确定一个节点时叶子节点呢?很简单没有左右孩子的节点就是叶子节点

我们可以遍历整个二叉树,遇到叶子节点就返回1,直到遍历到最后我们返回0,把他们全都累加起来,最后结果就是我们想要的叶子结点的个数。

看代码:

int TreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;

	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}

	return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}

4.3、求第K层节点的个数

这个问题相比于上面两个要复杂一点,首先我们要先解决如何遍历到第K层。

若要求第三层节点的个数:第一层相和第三层差2层,第二层和第三层差1层,第三层和第三层差0层......当k=1时证明就在第k层,我们就返回这个第K层的节点为1

我们可以得出一个结论:当前树的第K层   =   左子树的第K-1层    +   右子树的第K-1层

接下来我们来看代码:

int TreeKLevel(BTNode* root, int k)
{
	assert(k > 0);

	if (root == NULL)
		return 0;

	if (k == 1)
	{
		return 1;
	}

	return TreeKLevel(root->left, k - 1)
		+ TreeKLevel(root->right, k - 1);
}

Ⅴ、二叉树相关练习

1、已知二叉树的前序遍历为5 7 4 9 6 2 1,中序遍历是4 7 5 6 9 1 2则该二叉树的后序遍历是什么?

答案:4 7 6 1 2 9 5

解析:

通过前序遍历找到子树的根,在中序遍历中找到根的位置,然后确定根左右子树的区间,即根的左侧为左子树中所有节点,根的右侧为右子树中所有节点。

故:根为: 5

5的左子树:4 7   5的右子树: 6 9  1  2

5的左子树的根为: 7  5的右子树的根为:9

7的左子树: 4 7的右:空  9的左子树:6  9的右子树:2

这棵树的结构是:

2、如果一颗二叉树的前序遍历的结果是ABCD,则满足条件的不同的二叉树有( )种

答案:14

解析:

首先这棵二叉树的高度一定在3~4层之间:

三层:

A(B(C,D),()), A((),B(C,D)), A(B(C,()),D), A(B((),C),D),

A(B,C(D,())), A(B,C((),D))

四层:

如果为四层,就是单边树,每一层只有一个节点,除过根节点,其他节点都有两种选择,在上层节点的左边还是右边,所以2*2*2共8种

总共为14种。

也可以由公式的来:,这里的n指的是二叉树结点的个数(5*6*7*8)/(1*2*3*4)/(n+1)

答案也是14。

3、一棵非空的二叉树的先序遍历序列与后序遍历序列正好相反,则该二叉树一定满足( )

答案:只有一个叶子节点

解析:

前序遍历:根 左 右

后序遍历:左 右 根

从二叉树 前序 和 后序遍历结果规则中可以看出,如果树中每个节点只有一个孩子时,遍历结果肯定是反的

比如下面这前序和中序序列所构成的树的结构:

结语:这篇文章讲到这也就结束了, 如有疑问或者质疑,请在评论区发出来,我会尽全力帮大家解决,成长的路上少不了你们的支持,你们的三连是我进步最大的动力,还望大佬们多多支持,一起加油!共勉!

更多推荐

Learn Prompt-Prompt 高级技巧:MetaGPT

MetaGPT是一项引起广泛关注的研究成果,它引入了一个将人工工作流程与多智能体协作无缝集成的框架。通过将标准化操作(SOP)程序编码为提示,MetaGPT确保解决问题时采用结构化方法,从而减少出错的可能性。🎉开始阅读前,如果你对其他文章感兴趣,可以到欢迎页关注我们!「卡尔的AI沃茨」开源中文社区实时获得后续的更新和

网络安全进阶学习第二十课——CTF之文件操作与隐写

文章目录一、文件类型识别1、File命令2、Winhex3、文件头残缺/错误二、文件分离操作1、Binwalk工具2、Foremost3、dd4、Winhex三、文件合并操作1、Linux下的文件合并2、Windowsa下的文件合并四、文件内容隐写Winhex五、图片文件隐写1、图片混合2、LSB(最低有效位Least

PLC串口通讯和通讯接口知识汇总

在使用PLC的时候会接触到很多的通讯协议以及通讯接口,最基本的PLC串口通讯和基本的通讯接口你都了解吗?一、什么是串口通讯?串口是一种接口标准,是计算机上一种非常通用设备通信的协议。它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议。典型的串口通讯标准常见有如下三种。EIARS232(通常简称“RS232”):

【C++】命名空间 namespace 与 标准流 iostream ( 命名空间概念简介 | 命名空间定义 | 命名空间使用 | iostream 中的命名空间分析 )

文章目录一、命名空间namespace1、命名空间基本概念2、名称概念4、C语言的命名空间3、命名空间避免标识符冲突二、命名空间定义1、命名空间基本概念2、命名空间定义语法3、代码示例-命名空间定义使用三、命名空间使用1、命名空间默认访问方式2、使用命名空间3、使用默认的命名空间4、代码示例-使用命名空间四、标准流io

MySQL学习系列(11)-每天学习10个知识

目录1.数据库设计的关键因素2.使用存储过程和函数来提高性能和可重用性3.MySQL性能优化4.使用视图简化查询和提供数据安全性5.数据库备份和恢复策略的重要性和实践经验6.在分布式系统中保证数据一致性和可用性7.理解MySQL的复制和其在实际应用中的作用8.使用游标进行分页查询和遍历查询结果9.使用窗口函数10.数据

Redis 面试常见问答

本文出自:https://thinkinjava.cn作者:莫那鲁道1.什么是缓存雪崩?怎么解决?一般而言,我们会利用缓存来缓冲对数据库的冲击,假如缓存无法正常工作,所有的请求便会直接发送至数据库,进而导致数据库崩溃,从而导致整个系统崩溃。如何解决呢?2种策略(同时使用):对缓存做高可用,防止缓存宕机使用断路器,如果缓

深入学习 Redis - 分布式锁底层实现原理,以及实际应用

目录一、Redis分布式锁1.1、什么是分布式锁1.2、分布式锁的基础实现1.2.1、引入场景1.2.2、基础实现思想1.2.3、引入setnx1.3、引入过期时间1.4、引入校验id1.5、引入lua脚本1.5.1、引入lua脚本的原因1.5.2、lua脚本介绍1.6、过期时间续约问题(看门狗WatchDog)1.7

十四、流式编程(2)

本章概要中间操作跟踪和调试流元素排序移除元素应用函数到元素在map()中组合流中间操作中间操作用于从一个流中获取对象,并将对象作为另一个流从后端输出,以连接到其他操作。跟踪和调试peek()操作的目的是帮助调试。它允许你无修改地查看流中的元素。代码示例:Peeking.javaclassPeeking{publicst

Docker概念通讲

目录什么是Docker?Docker的应用场景有哪些?Docker的优点有哪些?Docker与虚拟机的区别是什么?Docker的三大核心是什么?如何快速安装Docker?如何修改Docker的存储位置?Docker镜像常用管理有哪些?如何创建Docker容器?Docker在后台的标准运行过程是什么?Docker网络模式

Apollo

Apollo🍓目前市面上比较多的配置中心🍓Apollo组件🍓Apollo特性🍓Apollo服务端安装🍓部署架构🍓核心概念🍓客户端连接Apollo🍓配置发布原理代码地址:https://gitee.com/xuhx615/apollo-demo.git🍓目前市面上比较多的配置中心⭐Disconf百度开源

Dubbo3基础使用

1、Dubbo概述现在SpringCloudAlibaba比较火,用的比较多是吧,那dubbo是不是过时的呢?并不是的,以前有人把Dubbo和SpringCloud进行对比,其实两者是不同维度的,不能对比,dubbo就是一个rpc框架,SpringCloud是一个生态,里面包括很多组件,并且dubbo3也可以和Spri

热文推荐