【C语言】手撕二叉树

news2025/1/14 18:33:52

标题:【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;
}


完~

未经作者同意禁止转载 

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

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

相关文章

软件项目经理需要具备这 11 个能力

当前软件开发技术更新换代越来越快&#xff0c;各种项目实施管理思想也日新月异&#xff0c;作为一个软件项目经理&#xff0c;需要具备这 11 种能力&#xff1a; 1. 项目管理能力 了解项目管理的基本原则和方法&#xff0c;包括制定项目计划、资源分配、风险管理、问题解决和…

婚恋相亲红娘牵线系统源码支持微信小程序+微信公众号+H5+APP社交交友系统搭建,扩展你的社交影响力与人脉资源

当代年轻人晚婚晚育甚至不愿意结婚不愿意相亲等问题愈来愈严重了&#xff0c;但是如果抛开表现去追求本质的话&#xff0c;其实不难发现年轻人们还是会喜欢去找一些志同道合或者聊得来的异性朋友交流&#xff0c;从线上发展到线下的一个流程。 核心系统核心体系是恋爱小贴士、…

概率图模型在机器学习中的应用:贝叶斯网络与马尔可夫随机场

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

嵌入式s5p5818核心板介绍

底板寻址空间介绍 s5p6818 寻址空间采用统一编址方式进行管理 寻址空间映射图&#xff1a; 独立寻址&#xff1a;片内片外存储器只能选择其中一个 统一寻址&#xff1a;片内片外存储器都能使用&#xff0c;且使用的是同一片连续的寻址空间 reserved保留&#xff0c;Normaol …

【代码】Python3|用Python PIL压缩图片至指定大小,并且不自动旋转

代码主体是GPT帮我写的&#xff0c;我觉得这个功能非常实用。 解决自动旋转问题参考&#xff1a;一行代码解决PIL/OpenCV读取图片出现自动旋转的问题&#xff0c;增加一行代码image ImageOps.exif_transpose(image) 即可恢复正常角度。 from PIL import Image, ImageOpsdef …

vue 表格获取当前行索引,加颜色

vue 表格获取当前行索引&#xff0c;加颜色 <span styledisplay:inline-block;width:10px;height:10px;border-radius:50% :style"{background:color[scope.$index]}" />//定义颜色color: [#5387F7, #A794E0, #F3543C, #999999, #77D3F8, #FFA1B4, #26CEBA, #…

【Cookie和Session的区别(面试重点)】

Cookie和Session的区别 1. Cookie1.1 认识Cookie1.2 Cookie的引出1.3 Cookie工作原理1.4 Cookie重要结论 2. Session2.1 认识Session2.2 Session的引出2.3 Session的工作原理 3. Cookie和Session的区别 1. Cookie 1.1 认识Cookie Cookie&#xff1a;是小型文本文件&#xff0…

python自动生成SQL语句自动化

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Python自动生成SQL语句自动化 在数据处理和管理中&#xff0c;SQL&#xff08;Structured …

鸿蒙OpenHarmony【LED外设控制】 (基于Hi3861开发板)

概述 OpenHarmony WLAN模组基于Hi3861平台提供了丰富的外设操作能力&#xff0c;包含I2C、I2S、ADC、UART、SPI、SDIO、GPIO、PWM、FLASH等。本文介绍如何通过调用OpenHarmony的NDK接口&#xff0c;实现对GPIO控制&#xff0c;达到LED闪烁的效果。其他的IOT外设控制&#xff0…

【python】python新闻文本数据统计和聚类 (源码+文本)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

【UE5.1 C++】VS2022下载安装

目录 步骤 一、Visual Studio下载安装 二、Visual Studio Integration Tool插件安装 先看一下UE和VS的兼容性 &#xff08;虚幻5&#xff1a;为虚幻引擎C项目设置Visual Studio开发环境&#xff09; &#xff08;虚幻4&#xff1a;设置虚幻引擎的Visual Studio&#xff0…

生产数据采集系统

在数字化浪潮的推动下&#xff0c;生产数据采集系统已经成为企业提升生产效率、优化运营管理的关键工具。那么&#xff0c;什么是生产数据采集系统呢&#xff1f;简单来说&#xff0c;生产数据采集系统是指通过一系列技术手段&#xff0c;实时收集、处理和分析生产线上的各类数…

[负债学习]支线Python4.21

三的东西&#xff0c;一个是环境&#xff0c;一个是基础语法&#xff0c;第3个是代码的案例。 我们先从头开始讲一下计算机&#xff0c;它主要由4个部分组成cpu的中央处理器和一个储存和一个输出和出。而储存的话主要是由内存和外存而cpu&#xff0c;中央处理器全称叫做通用计…

vuex和pinia转态管理工具介绍

文章目录 一、介绍二、使用1、pinia使用2、Vuex使用 一、介绍 相同点&#xff1a; 都是Vue.js的状态管理工具 不同点&#xff1a; 区别PiniaVuex支持Vue2和Vue3都支持Vue3写法需要额外配置Mutation只有 state, getter 和 action&#xff0c;无Mutationaction异步、Mutation …

【学习】服务器解决:重新分配同样端口号后,连不上VScode

原来服务器分配的环境有问题&#xff0c;重新分配了一下。还是同样的端口号&#xff0c;Xshell和xftp能够连接上&#xff0c;但是VScode连接不上。 问题解决: 清除本地 SSH 缓存中与远程主机相关的条目可以通过编辑 known_hosts 文件来实现。这个文件包含了您曾经连接过的远程主…

1分钟带你学会Series显式索引和隐式索引

1.Series的索引 Series是Pandas库中的一种一维数据结构对象&#xff0c;与常规的一维数组相比&#xff0c;其最显著的特点在于拥有显式的索引结构。这种显式索引使得Series在数据处理和分析中更具灵活性和便利性。 显式索引指的是Series中每个元素除了值之外&#xff0c;还关…

sketchup{su}安装错误1402

错误如图 解决方法如下 打开autoremove&#xff0c;点击扩展&#xff0c;输入1402&#xff0c;点击搜索 等待修复成功既可尝试重新安装su 软件每周六选择其他方式登录免费使用

Python 高质量类编写指南

原文&#xff1a;https://www.youtube.com/watch?vlX9UQp2NwTk 代码&#xff1a;https://github.com/ArjanCodes/examples/tree/main/2023/classguide Python 高质量类编写指南 我们将通过一些方法增加类的可读性和易用性。 通过&#xff08;按照属性或行为&#xff09;拆分类…

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

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

Linux thermal框架介绍

RK3568温控 cat /sys/class/thermal/thermal_zone0/temp cat /sys/class/thermal/thermal_zone1/temp cat /sys/class/thermal/cooling_device0/cur_state cat /sys/class/thermal/cooling_device1/cur_state cat /sys/class/thermal/cooling_device2/cur_state thermal_zone…