【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;
}

完~

未经作者同意禁止转载 

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

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

相关文章

Pointnet++改进卷积系列:全网首发DynamicConv |即插即用,提升特征提取模块性能

简介:1.该教程提供大量的首发改进的方式,降低上手难度,多种结构改进,助力寻找创新点!2.本篇文章对Pointnet++特征提取模块进行改进,加入DynamicConv,提升性能。3.专栏持续更新,紧随最新的研究内容。 目录 1.理论介绍 2.修改步骤 2.1 步骤一 2.2 步骤二

菜鸟Java面向对象 2. Java 重写(Override)与重载(Overload)

Java 重写(Override)与重载(Overload) Java 重写与重载 Java 重写(Override)与重载(Overload)1. 重写(Override)1. 概念解释&#xff1a;2. 好处说明3. 异常规则处理 2. 方法的重写规则3. Super 关键字的使用4. 重载(Overload)**重载规则:**实例 5. 重写与重载之间的区别总结 1…

ReclaiMe Pro:丢失分区的恢复方法

天津鸿萌科贸发展有限公司是 ReclaiMe Pro 数据恢复软件的授权代理商。 ReclaiMe Pro 的分区恢复功能 ReclaiMe Pro 提供专业的分区恢复功能&#xff0c;支持从各种文件系统中搜索丢失和损坏的分区&#xff0c;例如 FAT、exFAT、NTFS、EXT、XFS 和 VMFS。 ReclaiMe Pro 启动…

Windows系统 清除本地maven仓库无用的 _remote.repositories、sha1、lastUpdated相关的文件

Windows系统 清除本地maven仓库无用的 _remote.repositories、sha1、lastUpdated相关的文件 脚本命令&#xff1a; set REPOSITORY_PATH 你自己本地maven仓库地址 rem starting... for /f "delims" %%i in (dir /b /s "%REPOSITORY_PATH%\*_remote.repositori…

什么是手机运营商三要素验证API接口

手机运营商三要素验证API接口又叫手机运营商三要素核验API接口&#xff0c;指的是输入姓名、身份证号码及手机号&#xff0c;通过运营商数据库实时校验此三项是否匹配。手机运营商三要素核验API接口广泛用于实名注册、风控审核等场景&#xff0c;如电商、直播、游戏、金融等。接…

复杂算子onnx导出(4):执行图的构建

文章目录 1. 执行图构建1.1 执行图构建1.2 执行图推理1.3 推理案例1.3.1 节点和engine的构建1.3.2 利用engine推理2. onnx解析并创建执行图2.1 onnx 转执行图并推理2.2 完整的代码前面几节已经介绍如何将带有复杂算子如 spconv导出 onnx,接下来如何对导出的onnx进行推理。带…

hashlib模块

【一】什么是摘要算法 Python的hashlib提供了常见的摘要算法 如MD5 SHA1等等。 摘要算法又称哈希算法、散列算法。 它通过一个函数&#xff0c;把任意长度的数据转换为一个长度固定的数据串&#xff08;通常用16进制的字符串表示&#xff09;。 摘要算法就是通过摘要函数f()对…

Leetcode刷题之链表小结(1)|92反转链表|206反转链表

TOC 小结 1. 如何反转某一个节点的指向? 206反转链表(简单)的递归解法——该方法的理念是: 若节点k1到节点m已经被反转&#xff0c;而我们当前处于k位置&#xff0c;那么我们希望k1指向k, 体现在以下代码的head->next->next head;这一句,可以记做一种常用的反转单个…

AI+招聘,激活企业的「新质生产力」

两会以来&#xff0c;「新质生产力」成为热词。而所谓的新质生产力&#xff0c;是创新起主导作用&#xff0c;摆脱传统经济增长方式、生产力发展路径&#xff0c;具有高科技、高效能、高质量特征&#xff0c;符合新发展理念的先进生产力质态。新质之「新」&#xff0c;很重要的…

4-19 算法思路总结

leetcode 98 验证二叉搜索树 利用递归算法&#xff0c;从上而下递归&#xff0c;遇到false直接返回无需递归到叶子节点&#xff0c;从上往下递归时&#xff0c;传递并改变两个参数&#xff0c;对于当前节点的最大值以及最小值&#xff0c;对于根节点来说&#xff0c;初始化的最…

wandb注册 wandb: ERROR api_key

wandb: ERROR api_key not configured (no-tty). call wandb.login(key[your_api_key]) Traceback (most recent call last): 背景 使用yolov8训练时 在pycharm中出现wandb账号未注册错误 Transferred 355/355 items from pretrained weights TensorBoard: Start with tensor…

平衡二叉树(AVLTree)

AVLTree 1、树的分类2、平衡二叉树2.1、构建一个平衡二叉树2.2、删除节点2.3、搜索方式2.3.1、广度优先搜索&#xff08;BFS&#xff09;2.3.2、深度优先搜索&#xff08;DFS&#xff09; 1、树的分类 树形结构是编程当中特别常见的一种数据结构。比如电脑中的文件管理系统就大…

(超级详细)JAVA之Stream流分析-------持续更新喔!!!

学习目标&#xff1a; 掌握 Java Stream流的相关api 掌握 Java Stream流的基本实现 掌握 java Stream流的使用场景 代码已经整理上传到了gitee中&#xff0c;有需要的小伙伴可以取查看一下源码点个小心心喔 大家也可以帮我提交一点案例喔&#xff01;&#xff01;&#xff01;&…

Springboot+vue的高校毕业与学位资格审核系统。Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的高校毕业与学位资格审核系统。Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&#x…

SpringMVC interceptor有时候配置的时候path=“/**“ 两个星号什么意思,与path=“/“以及path=“/*“什么区别

直接上案例&#xff1a; <mvc:interceptor> <mvc:mapping path"/**"/> <bean class"com.xuyang.interceptor.user.UserAuthInterceptor" /> </mvc:interceptor>/**的意思是所有文件夹及里面的子文件夹 /*是所有文件夹&#xff0c…

【QT进阶】Qt Web混合编程之使用ECharts显示各类折线图等

往期回顾 【QT进阶】Qt Web混合编程之QWebEngineView基本用法-CSDN博客 【QT进阶】Qt Web混合编程之CMake VS2019编译并使用QCefView&#xff08;图文并茂超详细版本&#xff09;-CSDN博客【QT进阶】Qt Web混合编程之html、 js的简单交互-CSDN博客 【QT进阶】Qt Web混合编程之使…

统一SQL 支持Oracle number/decimal/dec/numeric转换

统一SQL介绍 https://www.light-pg.com/docs/LTSQL/current/index.html 源和目标 源数据库&#xff1a;Oracle 目标数据库&#xff1a;Postgresql&#xff0c;TDSQL-MySQL&#xff0c;达梦8&#xff0c;LightDB-Oracle 操作目标 通过统一SQL&#xff0c;将Oracle中的numb…

【MATLAB源码-第196期】基于matlab的A*融合DWA算法栅格路径规划仿真,画出路径图、姿态角度以及线角速度。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 A算法与DWA算法的融合是一个高效的路径规划策略&#xff0c;这种策略将A算法的全局路径规划能力与DWA算法的局部避障能力结合起来&#xff0c;以期达到更快、更安全的导航效果。以下是对这种融合策略的详细描述。 一、基本概…

Java Future模式

前言 Future模式是并发编程的一个重要的设计模式。比如有个方法需要很长的时间才能得到结果&#xff0c;不会让调用的程序一直等待&#xff0c;而是先返回给它一张“提货卡”。其实相当于消息队列&#xff0c;当你下了订单之后&#xff0c;在并发情况下&#xff0c;实际不是即时…

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…