【C语言】手撕二叉树

标题:【C语言】手撕二叉树

@水墨不写bug


正文开始:

        二叉树是一种基本的树形数据结构,对于初学者学习树形结构而言较容易接受。二叉树作为一种数据结构,在单纯存储数据方面没有  顺序表,链表,队列等线性结构  有优势,但是二叉树的搜索功能十分强大——高阶数据结构比如红黑树,B树等的基础就是搜索二叉树。

        二叉树的基本知识讲解:二叉树讲解

        对于初学者,首先学习二叉树的存储结构对于理解二叉树的本质结构非常有效。

(一)头文件

        本文不加解释的给出二叉树的头文件:

        Bitree.h

        并根据头文件来实现二叉树数据结构的相关功能。包括:二叉树的(前序)构建,二叉树的销毁,二叉树节点个数,二叉树叶子节点个数,二叉树第K层节点个数,二叉树查找值为x的节点,二叉树前序遍历,二叉树中序遍历,二叉树后序遍历,层序遍历,判断二叉树是否是完全二叉树等共11个功能。

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
#include<string.h>

//节点数据类型
typedef char BiTreeDataType;

//二叉树节点
typedef struct BiTreeNode
{
	BiTreeDataType _val;
	struct BiTreeNode* _left;
	struct BiTreeNode* _right;
}BTNode;

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BiTreeDataType* a, int* pi);

// 二叉树销毁
void BinaryTreeDestory(BTNode* root);

// 二叉树节点个数
int BinaryTreeSize(BTNode* root);

// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);

// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BiTreeDataType x);

// 二叉树前序遍历 
void BinaryTreePrevOrder(BTNode* root);

// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);

// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);

// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root);

(二)功能实现

        本文的二叉树是通过递归实现的,想要写好递归,需要注意一下几点:

        递归首先要有递归出口:

        确保递归函数的每一步都朝着基本情况靠近:在写递归函数时,确保每一步递归调用都是在缩小问题规模的基础上进行的。如果递归函数每一步都朝着基本情况靠近,那么递归一定会终止;

        写递归要有的思路:

        只考虑两层情况,即  本层递归  和  下一层递归  ,根据本层递归与下一层递归的关系,来写递归的逻辑;

        写递归要相信自己的函数:

        在写递归的时候,我们使用的下一层递归函数通常是一个黑盒,但是我们要相信递归函数一定完成任务。

(1)前序遍历

        首先判断此节点是否是空节点,若是,则退出递归。(这就是递归的出口)

        接下来访问此节点:printf()函数的调用代表访问节点。

        最后递归访问此节点的左子树和右子树。


// 二叉树前序遍历 root + left + right
void BinaryTreePrevOrder(BTNode* root)
{
	//递归出口
	if (root == NULL)
		return;
	
	printf("%d", root->_val);

	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);
}

(2)中序遍历

        首先判断此节点是否是空节点,若是,则退出递归。(这就是递归的出口)

        然后递归访问次节点的左子树。

        接下来访问此节点:printf()函数的调用代表访问节点。

        最后递归访问此节点的右子树。


// 二叉树中序遍历 left + root + right
void BinaryTreeInOrder(BTNode* root)
{
	//递归出口
	if (root == NULL)
		return;

	BinaryTreePrevOrder(root->_left);
	printf("%d", root->_val);
	BinaryTreePrevOrder(root->_right);
}

(3)后序遍历

        首先判断此节点是否是空节点,若是,则退出递归。(这就是递归的出口)

        然后递归访问次节点的右子树。

        接下来访问此节点:printf()函数的调用代表访问节点。

        最后递归访问此节点的左子树。


// 二叉树后序遍历 left + root + right
void BinaryTreePostOrder(BTNode* root)
{
	
	//递归出口
	if (root == NULL)
		return;

	BinaryTreePrevOrder(root->_left);
	BinaryTreePrevOrder(root->_right);

	printf("%d", root->_val);
}

(4)二叉树的(前序)构建

       前序构建的思路就是前序遍历的思路:先完成对根节点的访问,再递归构建左右子树。

访问根节点:

        根据字符串的当前的字符来决定:如果当前字符为‘#’,返回NULL表示空节点;如果不是‘#’,则创建新节点,并将此字符存入新节点。

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树  pi的作用计数,便于填充数组
// 接受一个字符串,‘#’ 表示 空节点。
BTNode* BinaryTreeCreate(BiTreeDataType* a, int* pi)
{
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}

	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	if (root == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	root->_val = a[(*pi)++];
	root->_left = BinaryTreeCreate(a,pi);
	root->_right = BinaryTreeCreate(a,pi);

	return root;
}

(5)二叉树的销毁

        递归销毁,如果此节点为空,此时不能访问此节点,直接返回即可,目的是返回到上一层来销毁;(也就是递归出口的定义)

        之后再递归销毁左子树和右子树,最后释放此节点。


// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
		return;

	BinaryTreeDestory(root->_left);
	BinaryTreeDestory(root->_right);
	free(root);
	//要在外部将root置空
}

(6)二叉树节点个数

        递归计数,如果此节点为空,返回0,不计数。

        否则返回左子树的计数结果和右子树的计数结果 + 1(此节点的计数)。


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

(7)二叉树叶子节点个数

        叶子节点有一个特征:左子树和右子树都为空。

        如果遇到这样的节点,计数+1;

        最后返回左子树与右子树的计数结果之和。


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

(8)二叉树第K层节点个数

       模拟递归:

        想象一下:在一个楼层中,假如你要下降K层,如何计数呢?

        在这里我们需要定义一个变量k作为标牌,标识还需要下降的层数。由于本层默认标定为1,所以当k==1时,计数+1;

        最后返回递归左子树和和右子树的结果。


// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	assert(k > 0);

	if (NULL == root)
		return 0;

	if (k == 1)
		return 1;
	return BinaryTreeLevelKSize(root->_left,k-1) 
		+ BinaryTreeLevelKSize(root->_right,k-1);
}

(9)二叉树查找值为x的节点

         查找思路:

        首先判断根节点是否为空,若为空,表示访问到叶子节点的左右节点,返回空表示没有找到。(另一种情况根节点为空表示整棵树都为空,也返回空表示没有找到)

        如果找到x返回这个节点的地址,表示找到了。

        接下来递归访问左右子树:

        需要注意的是:

        创建一个临时变量ret_l(ret_r)来接收在左右子树查找的结果,如果找到(ret_l(ret_r)不为空)则返回左(右)子树返回的结果;

        如果左右子树都没有找到,则返回空。


// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BiTreeDataType x)
{

	if (root == NULL)
		return NULL;
	if (root->_val == x)
		return root;

	BTNode* ret_l = BinaryTreeFind(root->_left, x);
	if (ret_l)
		return ret_l;

	BTNode* ret_r = BinaryTreeFind(root->_right, x);
	if (ret_r)
		return ret_r;

	return NULL;
}

(10)层序遍历

        思路:(不使用递归,通过一个循环和数据结构队列来实现。)

        初始时进一个根节点,接下来出一进二,从队头出一个数据同时压入两个数据——出根节点的同时进左子节点和右子节点,直到把队列出为空。

        printf()代表了对此节点的遍历。


// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue queue;
	Qinit(&queue);
	if (root != NULL)
		Qpush(&queue, root);

	while (!Qempty(&queue))
	{
		BTNode* front = Qfront(&queue);//队列先进先出
		Qpop(&queue);

		printf("%d", front->_val);

		if (front->_left)
			Qpush(&queue,front->_left);
		if (front->_right)
			Qpush(&queue,front->_right);
	}
	QDestroy(&queue);
}

(11)判断二叉树是否是完全二叉树

       

        思路:

        通过层序遍历来遍历二叉树。完全二叉树和非完全二叉树的区别就是完全二叉树的最后一个节点之前没有空节点。通过层序遍历来遍历二叉树,当找到空节点时,此时查找队列内是否有非空节点:

        如果都是空,此树是完全二叉树;

        如果有非空节点,此树不是完全二叉树。


// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{
	Queue queue;
	Qinit(&queue);
	if (root != NULL)
		Qpush(&queue, root);

	while (!Qempty(&queue))
	{
		BTNode* front = Qfront(&queue);//队列先进先出
		Qpop(&queue);

		if (front == NULL)
			break;
		Qpush(&queue, front->_left);
		Qpush(&queue, front->_right);
	}
	//如果队列里剩下的全是NULL,则是完全二叉树;否则不是
	while (!Qempty(&queue))
	{
		BTNode* front = Qfront(&queue);//队列先进先出
		Qpop(&queue);
		if (front)
		{
			QDestroy(&queue);
			return false;
		}
	}
	QDestroy(&queue);
	return true;
}


完~

未经作者同意禁止转载 

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

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

相关文章

sklearn 笔记 metrics

1 分类 1.1 accuracy_score 分类准确率得分 在多标签分类中&#xff0c;此函数计算子集准确率&#xff1a;y_pred的标签集必须与 y_true 中的相应标签集完全匹配。 1.1.1 参数 y_true真实&#xff08;正确&#xff09;标签y_pred由分类器返回的预测标签normalize 默认为 Tr…

Linux:Win10平台上,用VMware安装Centos7.x及系统初始化关键的相关配置(分步骤操作,详细,一篇足以)

VMware安装Centos7.x镜像的详细步骤&#xff1a;VMWare安装Centos系统&#xff08;无桌面模式&#xff09; 我这里是为了安装Hadoop集群&#xff0c;所以&#xff0c;以下这些步骤是必须进行的 如果你是学习Linux&#xff0c;可以跳过非必须的那些配置项 我安装的版本是&…

前端实现将二进制文件流,并下载为excel文件

目录 一、关于二进制流二、项目实践三、常见问题及解决 一、关于二进制流 含义&#xff1a;二进制流是一种计算机文件格式&#xff0c;它的数据以二进制形式存储&#xff0c;与文本文件不同。 二进制文件可以包含任意类型的数据&#xff0c;例如&#xff1a;图像、音频、视频…

智慧园区引领产业智慧化:深入探索智慧技术如何点亮园区创新发展之路,构建未来产业生态圈,驱动区域经济持续升级

目录 一、引言 二、智慧园区的内涵与特征 三、智慧技术点亮园区创新发展之路 1、智慧技术推动产业转型升级 2、智慧技术促进新兴产业发展 3、智慧技术提升园区创新能力 四、智慧园区在产业智慧化中的作用与价值 1、优化资源配置&#xff0c;提高经济效益 2、提升服务品…

Kibana安装部署(Linux)

Kibana是Elasticsearch的开源可视化工具&#xff0c;与存储在Elasticsearch中的数据进行交互。 1. 下载软件 这里使用的Elasticsearch的版本是7.12.0&#xff0c;所以kibana选择同样的7.12.0版本。 官网下载地址&#xff1a;https://www.elastic.co/cn/downloads/past-releas…

【全网首发】Mogdb 5.0.6新特性:CM双网卡生产落地方案

在写这篇文章的时候&#xff0c;刚刚加班结束&#xff0c;顺手写了这篇文章。 前言 某大型全国性行业核心系统数据库需要A、B两个物理隔离的双网卡架构方案&#xff0c;已成为行业标准。而最新发布的MogDB 5.0.6的CM新增支持流复制双网段部署&#xff0c;用于网卡级高可用容灾(…

Meta 向第三方开放 MR 操作系统;黄仁勋:人形机器人成本可能比人们预期要低得多丨 RTE 开发者日报 Vol.190

开发者朋友们大家好&#xff1a; 这里是「RTE 开发者日报」&#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「有…

大厂产品专家是做晋升述职的?

在大厂里,晋升都是需要述职的。与年终述职不同,晋升述职要求严格很多。这种情况下,如何完美表达自己才是适合晋升的人选?这篇文章,值得即将晋升和准备晋升的各位看看。 之前学姐写了一篇文章,讲怎么做年度述职,反响还不错~有兴趣的童鞋可以戳这里复习。今天学姐来讲一个…

25计算机考研院校数据分析 | 上海交通大学

上海交通大学电子信息与电气工程学院成立于2001年12月&#xff0c;其前身可湖源至百年前的电机专科&#xff0c;具有中国电气工程师“摇篮”之美称。50年代根据学科发展需要分为电工与计算机科学系(三系)和电子工程系(四系)。1985年&#xff0c;三系和四系合并&#xff0c;成立…

【LeetCode:216. 组合总和 III + 递归】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

类之间的关系

文章目录 一、横向关系复合&#xff08;组合&#xff09;委托&#xff08;聚合&#xff09;依赖关联 二、纵向关系&#xff08;继承&#xff09;继承下构造析构执行的顺序继承方法继承中的作用域多重继承 总结 一、横向关系 复合&#xff08;组合&#xff09; 包含与被包含黑色…

跟着野火从零开始手搓FreeRTOS(6)多优先级的配置

在 FreeRTOS 中&#xff0c;数字优先级越小&#xff0c;逻辑优先级也越小。 之前提过&#xff0c;就绪列表其实就是一个数组&#xff0c; 里面存的是就绪任务的TCB&#xff08;准确来说是 TCB 里面的 xStateListItem 节点&#xff09;&#xff0c;数组的下标对应任务的优先级&a…

mmclassification 训练自己的数据集

文章目录 从源码安装数据集准备config文件训练附录 从源码安装 git clone https://github.com/open-mmlab/mmpretrain.git cd mmpretrain pip install -U openmim && mim install -e .下面是我使用的版本 /media/xp/data/pydoc/mmlab/mmpretrain$ pip show mmcv mmpr…

npm install 卡在still idealTree buildDeps不动

前言 再使用npm install 安装包依赖时 发现一直卡住 停留在 观察node_cache下的_logs文件 发现一直在拉取包 37 silly idealTree buildDeps 38 silly fetch manifest riophae/vue-treeselect0.4.0尝试解决 尝试设置了taobao镜像源 依然如此 获取已经设置的镜像源 确实是ta…

6.3 实现Session 共享

1. Session 共享配置 2. Nginx 负载均衡 3. 测试请求分发 经过如上步骤 ,就完成了利用 Redis 实现 Session 共享的功能. 基本上不需要额外配置&#xff0c;开箱即用

【SpringBoot】-MyBatis详解+单表操作

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【Framework】 主要内容&#xff1a;什么是MyBatis框架&#xff1f;MyBatis框架有什么用&#xff1f;MyBatis实现查询步骤详解。MyBatis实现单表的增删查改。MyBatis模糊查询&…

LeetCode刷题实战4:寻找两个正序数组的中位数

题目内容 给定两个大小分别为 m 和 n 的正序&#xff08;从小到大&#xff09;数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。 算法的时间复杂度应该为 O(log (mn)) 。 示例 1&#xff1a; 输入&#xff1a;nums1 [1,3], nums2 [2] 输出&#xff1a;2.0…

微博评论爬取

import requests import csv# 打开CSV文件以写入数据 f open(data.csv, modea, encodingutf-8-sig, newline) csv_writer csv.DictWriter(f, fieldnames[昵称, 性别, 归属地, 内容]) csv_writer.writeheader()# 定义一个函数用于获取评论内容 def GetContent(max_id):# 设置请…

SRS服务接入华为云CDN

CDN简介: CDN的全称是Content Delivery Network&#xff0c;即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节&#xff0c;使内容传输得更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网…

为何3C电子精密件测量首选闪测仪?

在工业生产中&#xff0c;精密件的测量是至关重要的环节&#xff0c;它直接关系到产品的质量和性能。大部分3c电子工厂以及精密五金加工厂中&#xff0c;产品质检环节中大部分测量仪器都采用闪测仪。为什么呢&#xff1f; 测量精度与稳定性 闪测仪能够提供更高的测量精度和稳定…