数据结构:二叉树的实现

news2024/11/27 1:42:24

目录

二叉树的遍历方式

  前序遍历:

  中序遍历:

  后序遍历:

二叉树的基本结构和功能

  基本结构:

  基本功能:

二叉树功能的实现思路

二叉树功能的实现

   1、构建一个二叉树

   2、二叉树的销毁

   3、计算二叉树里的节点个数

   4、得到二叉树的子叶节点个数

    5、得到二叉树第K层节点个数

     6、查找二叉树值为X的节点

   7、二叉树的前/中/后序遍历

        前序遍历

        中序遍历

        后序遍历

   8、层序遍历

   9、判断二叉树是否为完全二叉树


二叉树的遍历方式

        二叉树有三种遍历方式,不同的遍历方式有不同的效果和作用。(实现二叉树的代码)

  前序遍历:

        根->左子树->右子树。

        (如上图所示)用前序遍历这个二叉树的结果是: 1 2 4 8 5 3 6 9 7

        如果加上空的子树,结果是:1 2 4 N 8 N N 5 N N 3 6 9 N N N 7 N N(为了方便理解,同一种颜色的是根的子树)


  中序遍历:

        左子树->根->右子树。

        (如上图所示)用前序遍历这个二叉树的结果是: 4 8 2 5 1 9 6 3 7

        如果加上空的子树,结果是:N 4 N 8 N 2 N 5 N 1 N 9 N 6 N 3 N 7 N(为了方便理解,同一种颜色的是根的子树) 


  后序遍历:

        左子树->右子树->根。

        (如上图所示)用前序遍历这个二叉树的结果是: 8 4 5 2 9 6 7 3 1

        如果加上空的子树,结果是:N N N 8 4 N N 5 2 N N 9 6 N N 7 3 1(为了方便理解,同一种颜色的是根的子树) 


二叉树的基本结构和功能

  基本结构:

        我们知道二叉树里有一个值和指向两个子树的指针构成,所以我们可以用下面的结构体来作为一个节点。

typedef char BTDataType;//为了便于以后因不同需求而更改。
//节点
typedef struct BinaryTreeNode
{
	BTDataType _data;//存放值
	struct BinaryTreeNode* _left;//一个指向左子树的指针
	struct BinaryTreeNode* _right;//一个指向右子树的指针
}BTNode;

  基本功能:

  1. 通过一个关于二叉树遍历的字符串来构建出一个二叉树
  2. 二叉树的销毁
  3. 得到二叉树的节点个数
  4. 得到二叉树的子叶节点个数
  5. 得到二叉树第K层节点个数
  6. 查找二叉树值为X的节点
  7. 二叉树的前序遍历
  8. 二叉树的中序遍历
  9. 二叉树的后序遍历
  10. 层序遍历
  11. 判断二叉树是否为完全二叉树

二叉树功能的实现思路

        (二叉树的大部分功能的实现都需要用到递归,所以我们要对递归一定熟练度)当我们要构建一个二叉树的时候,可以用递归的方式来连接一个一个的节点。其他的功能最主要的也是用递归

实现的。


二叉树功能的实现

   1、构建一个二叉树

        当我们要构建一个二叉树,我们首先要确定它的构建方式——前序、中序还是后序。(这里以前序遍历为例)

        前序遍历的方式是根->左子树->右子树

        若我们要通过前序遍历的数组" A B D # # E # H # # C F # # G # # "构建二叉树,我们首先可以先画出构建好的二叉树的图形(如下图所示),这回让你对你要创建的二叉树有更深的理解。

         利用递归的性质,我们只要遇到 ' # '就返回,反之,就创建节点,然后使其链接左子树和右子树。

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
    //当遇到' # '就返回
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}
    //创建一个节点
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	if (root == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	root->_data = a[*pi];
	(*pi)++;
	root->_left = BinaryTreeCreate(a, pi);//链接左子树
	root->_right = BinaryTreeCreate(a, pi);//链接右子树
    //最后返回根
	return root;
}

        如果我们想要用其他的遍历方式来构建二叉树可以调整一下链接左子树的时机。(记得字符串里的内容要是对应的遍历方式)


   2、二叉树的销毁

        要销毁一个二叉树,我们首先要确定销毁方式,因二叉树的结构只能用后序遍历(左子树->右子树->根)的方式来销毁二叉树。

        假如我们用前序(左子树->根->右子)或中序(左子树->根->右子树)的方式销毁二叉树,我们都会先销毁根,导致我们找不到后续的子树,所以我们只能用后序的方式来销毁二叉树。

// 二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
	if (*root == NULL)
	{
		return;
	}
    //先遍历到子叶再开始销毁
	BinaryTreeDestory(&(*root)->_left);
	BinaryTreeDestory(&(*root)->_right);
	free(*root);
	*root = NULL;
}

   3、计算二叉树里的节点个数

        计算节点个数,我们只需要遇到空指针就返回,反之就加左子树和右子树之和再加1(本身)即可。

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	return BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right) + 1;
}

   4、得到二叉树的子叶节点个数

        遇到空就返回0,如果本身非空且有左子树则加1,如果本身非空且有右子树则加1,这样我们就可以得到二叉树的子叶节点个数了。

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	
	if (root && root->_left == NULL)
		return 1;

	if (root && root->_right == NULL)
		return 1;

	return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}


    5、得到二叉树第K层节点个数

        我们先设根节点为第一层,则第二层就为k-1层,然后推导,当k == 1的时候就是我们需要计算节点个数的层数。若该二叉树没有第k层呢?我们只需当k == 0时就返回0即可。

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL)
		return 0;
    //当k == 1时就返回1
	if (k == 1)
		return 1;
	
	return BinaryTreeLevelKSize(root->_left, k - 1) 
           + BinaryTreeLevelKSize(root->_right, k - 1);
}

     6、查找二叉树值为X的节点

        在二叉树里查找一个值的时候,我们首先要确定查找方式,(这里以前序为例),我们用前序查找,如果左子树有这个值就返回该节点,没有就返回NULL,然后再查找右子树,若也没有就返回NULL。

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	if (root == NULL)
		return NULL;

	if (root->_data == x)
		return root;

	BTNode* left = BinaryTreeFind(root->_left, x);

	return left != NULL ? left : BinaryTreeFind(root->_right, x);
}

        如果不在左子树,那就只有两种可能,一个是在右子树,一个是没有这个值。这里的left都为空的话,那只能去右子树里去找了。


   7、二叉树的前/中/后序遍历

        这三种遍历方式在二叉树的实现都差不多,只是要区分先后顺序。

        前序遍历

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{	
		printf("# ");
		return;
	}

	printf("%c ", root->_data);
	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);
}

        中序遍历

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}

	BinaryTreePrevOrder(root->_left);
	printf("%c ", root->_data);
	BinaryTreePrevOrder(root->_right);
}

        后序遍历

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("# ");
		return;
	}

	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);
	printf("%c ", root->_data);
}

   8、层序遍历

        层序遍历就与上面的遍历有所不同,这里我们要利用队列来存储节点的指针来达到层序遍历。

        这里的层序遍历的结构是:A B C D E F G H 

        首先,我们要清楚队列的性质,队列是由链表构成,先进先出的方式来进出队列。开始先入一个根节点,每当出一个二叉树的节点就得要入它的左子树节点和右子树节点。遇到不是空节点就入队列。这样我们就可以实现二叉树的层序遍历。(这里避免过于臃肿就不写队列的过程了,如果想了解一下队列可以看看这篇文章详解栈和队列的实现以及它们的相互实现)

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if(root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		printf("%c", front->_data);

		if(front->_left)
			QueuePush(&q, front->_left);
		if (front->_right)
			QueuePush(&q, front->_right);
		QueuePop(&q);
	}
	printf("\n");
}

   9、判断二叉树是否为完全二叉树

        判断二叉树是否为完全二叉树的实现,跟上面基本同理,当与到空的时候就直接break去判断后面还有没有其他的数,无,则是完全二叉树,反之,不是完全二叉树。

        原理:(将空指针视为 N )如果一个二叉树是完全二叉树,将像上面入队列的时候,队列中存储的数据到N的时候后面就只有N了。但如果还有其他的数,那就不可能是完全二叉树。

// 判断二叉树是否是完全二叉树
bool BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if(root)
		QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
        
        if( front == NULL)
            break;

		if(front->_left)
			QueuePush(&q, front->_left);
		if (front->_right)
			QueuePush(&q, front->_right);

	}
	
    while (!QueueEmpty(&q))
    {
        BTNode* front = QueueFront(&q);
		QueuePop(&q);
        
        if( front)
        {
            QueueDistory(&q);
            return false;
        }
    }   
    return true;
}

(完)
        

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

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

相关文章

音频数据上的会话情感分析

情感分析,也被称为观点挖掘,是自然语言处理(NLP)中一个流行的任务,因为它有着广泛的工业应用。在专门将自然语言处理技术应用于文本数据的背景下,主要目标是训练出一个能够将给定文本分类到不同情感类别的模型。下图给出了情感分类器的高级概述。 例如,三…

从零开始理解AdaBoost算法:设计思路与算法流程(二)【权值更新与加权表决、数学公式】

设计思路 AdaBoost算法属于Boosting算法家族中的一种,其基本思路是将多个弱分类器组合成一个强分类器。 “强分类器”是指一个分类准确率较高的模型“弱分类器”则是指分类准确率略高于随机猜测的简单模型。 AdaBoost的核心思想是通过 加权 的方式逐步提高分类器…

Tomcat源码解析(八):一个请求的执行流程(附Tomcat整体总结)

Tomcat源码系列文章 Tomcat源码解析(一):Tomcat整体架构 Tomcat源码解析(二):Bootstrap和Catalina Tomcat源码解析(三):LifeCycle生命周期管理 Tomcat源码解析(四):StandardServer和StandardService Tomcat源码解析(五)&…

keda-P0460. 潜水员

可达信奥 - 登录 - 可达信奥https://kedaoi.cn/p/P0460 代码思路: 01背包DP。 思路也是比较经典的,就是看用这个水缸的最小值小,还是不用这个水缸的最小值小。但是这里涉及到一个初始化的问题,因为要求最小所以初始化理应…

使用NetAssist网络调试助手在单台计算机上配置TCP服务器和客户端

要使用NetAssist网络调试助手在同一台计算机上配置一个实例作为服务器(server)和另一个实例作为客户端(client),可以按照以下步骤进行操作: 前提条件 确保已经安装NetAssist网络调试助手,并了…

streamlit:如何快速构建一个应用,不会前端也能写出好看的界面

通过本文你可以了解到: 如何安装streamlit,运行起来第一个demo熟悉streamlit的基本语法,常用的一些组件使用streamlit库构建应用 大模型学习参考: 大模型学习资料整理:如何从0到1学习大模型,搭建个人或企业…

鞠小云张霖浩闪耀北京广播电视台春晚发布会,豪门姐弟感爆棚

昨日,2025年北京广播电视台“追梦春晚”全国海选发布会在杭州举行,中国内地青年女演员鞠小云同人气幕后张霖浩,受主办方盛情邀请出席本次活动。从现场流露出的照片中可以看出,鞠小云一袭白色长裙灵动温婉素雅,而张霖浩…

springboot与flowable(1):介绍、Flowable-ui使用

一、工作流引擎使用场景 工作流在企业管理系统中是高频使用的功能,一个最常见的例子是请假加班申请与审批的过程。事实上,工作流引擎能支持的业务场景远远不止单据审批,几乎所有涉及到业务流转、多人按流程完成工作的场景背后都可以通过工作流…

Vue3【二】 VSCode需要安装的Vue语法插件

VSCode需要安装的 适配Vue3的插件 Vue-Official插件安装

A股所有公司ZL申请与创新绩效分析(2000-2022年)

数据简介:专利是创新成果的主要载体,专利所包含的技术、经济、法律等信息主要通过结构化专利专利文献著录项的形式加以呈现。专利申请与创新绩效已成为衡量上市公司竞争力的重要指标。目前,各国ZSCQ管理部门开发管理的开放式平台是获取ZL数据…

PyQt5 多进程 多任务 多线程实现进度条功能 无边框 含源码

概述: 在项目 中我们常遇到,大量计算或者加载数据时,需要用到多线程,此时只能等待,我们这个时间需要添加一下进度条,告诉用户当前需要等待,这时间就需要用到多线程和等待进度条; 效…

【STM32】GPIO输出(江科大)

一、GPIO简介 1.GPIO:通用输入输出口 2.可配置为8种输入输出模式 3.引脚电平:0-3.3V(输出最大3.3V),部分引脚可容忍5V(输入,有FT) 4.输出模式下,可控制端口输出高低电平…

94、二叉树的迭代遍历

实现对二叉树的前后序非递归遍历 题解: 递归的实现就是:递去,归来。每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是…

MySQL系列-安装配置使用说明(MAC版本)

1、前言 本文将介绍MySQL的安装配置以及基本语法操作说明 环境:mac 版本:MySQL 8.0.28 之前电脑安装卸载过,后面在装的时候遇到一些问题,用了四五天才解决,主要是参考 https://blog.csdn.net/zz00008888/article/deta…

IPv6 自动配置流程图

IPv6 自动配置流程图 IPv6 自动配置生命周期 Mark

人事信息管理系统(Java+MySQL)

一、项目背景 在现代企业中,管理大量员工的工作信息、薪资、请假、离职等事务是一项非常繁琐和复杂的任务。传统的手工管理方式不仅效率低下,而且容易出错。为了提高人事管理的效率,减少人工操作带来的错误,企业迫切需要一个高效…

乒乓球烧拍日记之一结构分析

先上图 这是这两年烧过的球拍,最贵的是palio C2 80元左右,大部分球拍儿都是十多元,其中横拍儿是小朋友用的,直拍是我自己用的。照片中球拍儿的分类是按结构特点和材料来分的,最下面的两个剑轮球拍是成品拍儿。胶皮也都…

基于运动控制卡的圆柱坐标机械臂设计

1 方案简介 介绍一种基于运动控制卡制作一款scara圆柱坐标的机械臂设计方案,该方案控制器用运动控制卡制作一台三轴机械臂,用于自动抓取和放料操作。 2 组成部分 该机械臂的组成部分有研华运动控制卡,触摸屏,三轴圆柱坐标的平面运…

多目标融合参数搜索

多目标融合 权重分类目人群。 trick normlize 不同Score之间含义、量级和分布差异较大:评分计算的不同部分的意义、范围和分布存在显著差异,这使得直接比较或融合它们的结果变得困难。显式反馈(如点赞率)存在用户间差异&#…

2、数据操作

索引从0开始 一行 [1,:] 一列[:,1] 子区域:[1:3,1:] 第一行和第二行,从第一列开始 [::3,::2] 每3行一跳,每2列一跳 torch.tensor([[1,2,3,4]] 按位置算 xy ,x-y x*y x**y(幂) 1、广播机制形状不一样,…