[C语言实现]数据结构二叉树之《我种下的树会为我遮阳挡雨》


🥰作者: FlashRider

🌏专栏: 初阶数据结构

🍖知识概要:详解二叉树的概念、二叉树的遍历、以及代码实现。

目录

树的基本概念

树的存储结构与二叉树的实现

树的存储

什么是二叉树

二叉链存储二叉树

二叉树的代码实现


树的基本概念

我们说的树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成的具有层次关系的集合。
因为外形看起来像是一棵根朝上,叶朝下的倒挂的树,所以我们把它称作树。

注意:子树不能有交集,否则就不是树形结构(即每个节点只能有一个父节点)。

一些术语:

节点的度:一个节点含有的字数个数。
叶节点:度为0的节点。
非叶节点/分支节点:度不为0的节点。
父/双亲节点:有子树的节点。
子节点:有父节点的节点。
兄弟节点:具有相同父节点的节点。
树的度:整个树中节点的度取最大值。
节点的层次:从根节点为第一层开始,依次往下递增。
树的高度:节点的最大层次。
祖先:从根节点到该节点所经过的分支上所有的节点。
森林:由m(m>0)棵互不相交的树组成的集合。

树的图示:

注:树的节点存储什么样的数据根据情况而议,图中使用字母表示节点只是为了方便。


树的存储结构与二叉树的实现

树的存储

我们可以观察一下树的结构,首先对于每一个父节点来说,它都有至少一个子节点,如果有多个兄弟我们可以都看作兄弟节点。对于没有子节点或兄弟节点的节点,我们可以把它的子节点或兄弟节点看作空。
因此我们可以发现任意节点都可以这么表示:子节点,兄弟节点。
字节点存储它的第一个儿子,兄弟节点存储它的兄弟。这样根据一个儿子的兄弟节点就可以遍历所有的儿子,而根据子节点又可以继续往深处找。

typedef int DataType;//树的数据类型 
typedef struct TreeNode
{ struct Node* _child;  //第一个孩子  struct Node* _brother;  //自己的兄弟DataType _data;  //节点内存的数据
}TNode;

而对于我们目前而言,研究一些特殊的树就可以了,而树中比较典型的一种就是二叉树。

什么是二叉树

二叉树是一种树的结点的有限集合,该集合满足两个性质:

1. 可以为空。 2. 由一个根加上左右两颗子树组成。

对于途中的结点2来说,它的右子树为空,对于3、5、6来说,左右子树都为空,但也满足二叉树的性质。因此这棵树是一颗二叉树。

完全二叉树:

除了最后一层叶子结点可以不用铺满,其他每一层的结点都必须满足最大值。比如上图中的第一层和第二层都满足结点为最多,第三层叶子节点最多为4个,但只有3个,因为叶子节点不用满足铺满,所以上图是一个完全二叉树。

满二叉树

每一层的结点都为最大值,即每一层结点都铺满。满二叉树是一种特殊的完全二叉树。

二叉树的一些性质:

1. 若根节点层次为1,则一颗非空二叉树第i层最多有2的i-1次方。
2. 若根节点层次为1,则深度为h的二叉树,最大结点数为2的h次方-1。
3. 对于任何一颗二叉树,如果度为0的叶节点个数为n0, 则度为2的分支结点个数为n2,则满足
n0 = n2 + 1。
4. 若根节点层次为1,则有n个结点的满二叉树深度为h = log2(n+1)    以2为底 n+1为对数。
5. 如果从上到下从左到右,根结点为0开始编号,对于结点 i 则:
        5.1 若 i 不为0,则(i-1)/2为父节点.
        5.2 若2i+1<n, 则2i+1为左孩子。否则无左孩子。
        5.3 若2i+2<n, 则2I+2为右孩子。否则无右孩子。

二叉链存储二叉树

我们可以使用链式结构存储二叉树。

typedef int DataType;//数据类型
typedef struct BinaryTreeNode
{struct BinaryTreeNode* _left;//左孩子struct BinaryTreeNode* _right;//右孩子DataType _data;//数据
}BTNode;

三叉链
如果使用三叉链存储二叉树,那么就多一个指向父亲的指针,可以快速找到父节点,在某些需要频繁找父节点的情况下可以使用。

typedef int DataType;//数据类型
typedef struct BinaryTreeNode
{struct BinaryTreeNode* _parent;//父节点struct BinaryTreeNode* _left;//左孩子struct BinaryTreeNode* _right;//右孩子DataType _data;//数据
}BTNode;

二叉树的代码实现

有了前面的知识,我们已经明白二叉树的结构是怎样的了,那么我们可以考虑如何创建二叉树的节点,其实也很简单,和链表节点类似,malloc一个新节点出来,并存储数据就行了,而二叉树节点的创建与链接我们一般用递归实现,这里就不再赘述,我们就直接手动连接。

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
//二叉树结构
typedef int BTDataType;
typedef struct BinaryTreeNode
{BTDataType data;struct BinaryTreeNode* left;struct BinaryTreeNode* right;
}BTNode;
//创建一个节点
BTNode* BuyNode(BTDataType x)
{BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));if (newnode == NULL){perror("malloc fail");return NULL;}newnode->data = x;newnode->left = newnode->right = NULL;return newnode;
}
//手动链接一颗二叉树
BTNode* CreatBinaryTree()
{BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;return node1;
}

二叉树的遍历:

二叉树的遍历分为前序中序后序三种,而这三种的区别就是根节点在前还是在后。
遍历顺序为:左子树,根节点,右子树。 左根右,因此是中序遍历。以此类推。
而我们可以使用递归非常简单的去实现这三种遍历,因为树本身就是一种递归结构。

如果当前节点为空,代表不需要再递下去了,就return,如果不为空代表可以继续递左右子树,因此看遍历需求考虑先递再打印还是先打印再递。

//前序遍历: 根 左 右
void PrevOrder(BTNode* bt)
{if (bt == NULL){printf("N ");return;}printf("%d ", bt->data);PrevOrder(bt->left);PrevOrder(bt->right);
}
//中序遍历 左 根 右
void InOrder(BTNode* bt)
{if (bt == NULL){printf("N ");return;}PrevOrder(bt->left);printf("%d ", bt->data);PrevOrder(bt->right);
}
//后序遍历 左 右 根
void PostOrder(BTNode* bt)
{if (bt == NULL){printf("N ");return;}PrevOrder(bt->left);PrevOrder(bt->right);printf("%d ", bt->data);
}

一些简单常见的操作:

对于一颗二叉树,我们也许会求它的高度或结点个数,这些该怎么做呢?

对于求结点个数来说,我们也可以使用递归思想(分治思想),一棵树的结点个数等于左子树节点数+右子树节点数+根结点,因此sum = left + right + 1;对于这棵树的每一颗子树我们都可以这么想,因此代码就显而易见了。
求叶子结点个数也很简单,只要一个树左右子树都为空,则就是叶结点了,在求结点个数的基础上改一改就行。

对于求高度来说,我们也可以递归,一棵树的高度为左右子树高度的最大值,加上本身这一层。即h = max(左子树高度,右子树高度) + 1。

//求结点个数
int BTreeSize(BTNode* bt)
{if (bt == NULL)	return 0;return BTreeSize(bt->left) + BTreeSize(bt->right) + 1;
}
//求叶结点个数
int BLeafSize(BTNode* bt)
{if (bt == NULL)return 0;if (bt->left == NULL && bt->right == NULL)return 1;return BLeafSize(bt->left) + BLeafSize(bt->right);
}
//求树的高度
int BTreeHeight(BTNode* bt)
{if (bt == NULL)return 0;int left = BTreeHeight(bt->left);int right = BTreeHeight(bt->right);return left > right ? left + 1 : right + 1;
}

一些常见操作:

求树的第k层的结点个数:因为这个问题指定了求哪一层,所以我们拿一个变量k来存储剩余层数,当k减到1时代表就是第k层了。

查找值为x的结点:也是用一个变量存储x,如果当前结点的data(前提是结点不为空)为x,就可以返回当前结点,否则返回左子树右子树中递归查找结果不为空的那一个。

//求树第k层的结点个数
int BinaryTreeLevelKSize(BTNode* bt, int k)
{assert(k > 0);if (k == 1)return 1;if (bt == NULL)return 0;return BinaryTreeLevelKSize(bt->left, k - 1) + BinaryTreeLevelKSize(bt->right, k - 1);
}
//二叉树查找值为x的结点
BTNode* BTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)return NULL;if (root->data == x)return root;BTNode* r1 = BTreeFind(root->left, x);if (r1) return r1;BTNode* r2 = BTreeFind(root->right, x);if (r2) return r2;return NULL;//if (r1 == NULL && r2 == NULL)//	return NULL;//if (r1 == NULL) return r2;//else return r1;
}

二叉树的基本知识点就差不多到这了,接下来我们就可以调试代码了。

//求前中后序遍历,并打印二叉树的结点/叶结点个数以及高度
int main()
{BTNode* root = CreatBinaryTree();PrevOrder(root);printf("\n");InOrder(root);printf("\n");PostOrder(root);printf("\n");printf("%d\n", BTreeSize(root));printf("%d\n", BLeafSize(root));printf("%d\n", BTreeHeight(root));return 0;
}

当然,为了避免内存泄漏,记得要销毁二叉树噢。

//二叉树的结点空间释放
void BTreeDestroy(BTNode* bt)
{if(bt == NULL)	return;BTreeDestroy(bt->left);BTreeDestroy(bt->right);free(bt);
}

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

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

相关文章

车载电子电器架构 —— 局部网络管理汇总

车载电子电器架构 —— 局部网络管理汇总 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明…

黄金票据的复现

实验环境以及工具 服务器&#xff1a;Windows server 2003 用户&#xff1a;Windows 7旗舰版 工具&#xff1a;mimikatz 搭建服务器环境 参考&#xff1a;内网横向——域渗透之黄金票据复现-CSDN博客 创建用户 使用gpupdate刷新策略&#xff1b; 搭建win7环境 设置ip ‘…

二维相位解包理论算法和软件【全文翻译-将相位分解为 “非旋转 “和 “旋转 “(2.4)】

2.4 将相位分解为 "非旋转 "和 "旋转 "部分 借用电磁场理论,可以用发散和卷曲来指定矢量场[9][10]。当且仅当矢量函数 F(r)(以及由其描述的场)在整个域 D 中不旋转或无旋转时,我们称之为矢量函数 F(r)、 因此,如果等式 2.30(也是第 2.2 节关于路径…

揭秘糖尿病患者稳定控制血糖的关键!

患者在就诊之前一直使用的二甲双胍和达格列净这两种降糖药物&#xff0c;这两种药对于控制血糖是有一定效果的。北京崇文门医院朱学敏主任的建议是继续服用&#xff0c;然后患者空腹血糖在7-8mmol/L左右&#xff0c;餐后血糖稍高&#xff0c;达到9-10mmol/L&#xff0c;但总体上…

单链表求集合的交集

#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> typedef int ElemType; typedef struct LinkNode {ElemType data;LinkNode* next; }LinkNode, * LinkList; //尾插法建立单链表 void creatLinkList(LinkList& L) {L (LinkNode*)mallo…

外汇110:交易中,是否真的存在确定性?

我们看问题的角度不同&#xff0c;得到的结果必然也是不一样的。我们不能否认任何一种可能性&#xff0c;但一切需要从逻辑出发。交易中&#xff0c;最大的确定性就是市场是不确定的&#xff0c;什么样的行情都可能发生。当然&#xff0c;绝对的确定性是不存在的&#xff0c;但…

nginx配置多vue项目

1. 找到linux docker安装好的nginx目录文件 进入nginx内 把打包好的vue项目放在html文件下 如上 三个文件夹下对应着三个不同的vue项目 2. 配置default.conf的配置文件&#xff0c; 一个nginx配置文件可以多个项目进行代理 进入到conf 找到conf.d下面的default.conf 文件…

09. 【Android教程】表格布局 TableLayout

学完了 Android 两个经典布局&#xff0c;是不是觉得已经可以应对大多数场景了&#xff1f;我记得当我学完 LinearLayout 和 RelativeLayout 之后&#xff0c;我觉得 UI 布局已经可以出师了&#xff0c;在本人从事了多年的 Android 研究之后&#xff0c;可以很负责任的告诉你&a…

STM32 uC/OS-III

What is uC/OS-III? C/OS-III 的发音为“Micro C O S Three”&#xff0c;这意味着 C/OS-III 是基于 C 语言编写的第三代 小型操作系统&#xff0c;当然这里所说的第三代是相对于 C/OS 的前两个版本 C/OS 和 C/OS-II 而言 的&#xff0c;后面也会介绍这三个版本的差别。C/OS/…

第117讲:深入MySQL性能优化:从多个角度提升数据库性能

文章目录 1.从哪些角度去考虑MySQL的优化2.数据库服务器的选型3.从操作系统层面去优化MySQL数据库3.1.关于CPU方面的优化3.2.关于内存方面的优化3.3.关于磁盘IO方面 4.应用端的优化5.数据库系统优化工具6.数据库系统参数优化6.1.最大连接数的优化&#xff08;max_connections&a…

Qt使用opencv打开摄像头

1.效果图 2.代码 #include "widget.h"#include <QApplication>#include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp>#include <QImage> #include <QLabel> #incl…

hadoop 高可用(HA)、HDFS HA、Yarn HA

目录 hadoop 高可用(HA) HDFS高可用 HDFS高可用架构 QJM 主备切换&#xff1a; Yarn高可用 hadoop 高可用(HA) HDFS高可用 HDFS高可用架构 QJM 主备切换&#xff1a; Yarn高可用

【威胁情报综述阅读3】Cyber Threat Intelligence Mining for Proactive Cybersecurity Defense

【威胁情报综述阅读1】Cyber Threat Intelligence Mining for Proactive Cybersecurity Defense: A Survey and New Perspectives 写在最前面一、介绍二、网络威胁情报挖掘方法和分类A. 研究方法1&#xff09; 第 1 步 - 网络场景分析&#xff1a;2&#xff09; 第 2 步 - 数据…

python文件处理:解析docx/word文件文字、图片、复选框

前言 因为一些项目原因&#xff0c;我需要提供解析docx内容功能。本来以为这是一件比较简单的工作&#xff0c;没想到在解析复选框选项上吃了亏&#xff0c;并且较长一段时间内通过各种渠道都没有真正解决这一问题&#xff0c;反而绕了远路。 终于&#xff0c;我在github pytho…

2024最新telegram电报模块化机器人TG飞机混合开发的机器人框架

更新日记&#xff1a;24-03-10 优化服务框架回复地址 金额 等交互模式优化修复一些免费莫名被卸载模块问题修复收费模块续费后未到期被卸载问题框架增加一些方法,详细最近会出各种开发教程TRX兑换增加机器人上管理功能,自动开会员上线,能量即将上线 更新日志24-02-25 增加电…

golang和Java的简单介绍和对比

一、golang 1、Golang简介 Golang&#xff0c;也称为Go&#xff0c;是由Google公司在2009年推出的开源编程语言&#xff0c;由罗伯特格瑞史莫(Rob Pike)、肯汤普逊(Ken Thompson)、罗勃派克(Robert Griesemer)等人设计。Go语言的目标是在保持简单高效的编程模型的同时&#xf…

Golang Context是什么

一、这篇文章我们简要讨论Golang的Context有什么用 1、首先说一下Context的基本作用&#xff0c;然后在讨论他的实现 (1)数据传递&#xff0c;子Context只能看到自己的和父Context的数据&#xff0c;子Context是不能看到孙Context添加的数据。 (2)父子协程的协同&#xff0c;比…

华为OD机试 - 最大社交距离(Java 2024 C卷 100分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

基于单片机便携式测振仪的研制系统设计

**单片机设计介绍&#xff0c;基于单片机便携式测振仪的研制系统设计 文章目录 一 概要二、功能设计三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机便携式测振仪的研制系统设计概要主要涉及利用单片机作为核心控制器件&#xff0c;结合测振原理和技术&#x…

15.Python访问数据库

如果数据量较少&#xff0c;则我们可以将数据保存到文件中&#xff1b;如果数据量较 大&#xff0c;则我们可以将数据保存到数据库中。 1 SQLite数据库 SQLite是嵌入式系统使用的关系数据库&#xff0c;目前的主流版本是SQLite 3。SQLite是开源的&#xff0c;采用C语言编写而…