【数据结构】二叉树链式结构的实现(三)

目录

一,二叉树的链式结构

二,二叉链的接口实现

        1,二叉链的创建

        2,接口函数

        3,动态创立新结点

        4,创建二叉树

        5,前序遍历

        6,中序遍历

        7,后序遍历

三,结点个数以及高度等

        1,接口函数

        2,结点个数

        3,叶子结点个数

        4,二叉树高度

        5,二叉树第k层结点个数

        6,二叉树查找值为x的结点


一,二叉树的链式结构

二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系;

通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。

链式结构又分为二叉链和三叉链,这里我们学习二叉链;

 二叉树是:

1,空树

2,非空:根节点,根节点的左子树、根节点的右子树组成的。

从图示中可以看出,二叉树定义是递归式的也称递归树,因此后序基本操作中基本都是按照该概念实现的;

 二叉链结构图示;

二,二叉链的接口实现

        1,二叉链的创建

typedef int BTDataType;
//二叉链
typedef struct BinaryTreeNode
{BTDataType data; // 当前结点值域	struct BinaryTreeNode* left; // 指向当前结点左孩子struct BinaryTreeNode* right; // 指向当前结点右孩子
}BTNode;

首先创建一个结构体表示二叉链data是当前结点的值域,BTDataType是储存的值的数据类型;

left是指向当前结点左孩子right是指向当前结点右孩子

这里的BTDataTypeint的重命名,也可以说是数据类型的重命名,这样统一化方便后续更改;

        2,接口函数

//动态创立新结点
BTNode* BuyNode(BTDataType x);
//创建二叉树
BTNode* GreatBTree();
//前序遍历
void PrevOrder(BTNode* root);
//中序遍历
void InOrder(BTNode* root);
//后序遍历
void PostOrder(BTNode* root);

这是以上要实现的接口函数;

        3,动态创立新结点

//动态创立新结点
BTNode* BuyNode(BTDataType x)
{BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));assert(newnode);newnode->data = x;newnode->left = NULL;newnode->right = NULL;return newnode;
}

后面创立新结点时直接调用此函数,一定要向堆区申请空间,这样函数结束空间会保留不会被回收;

data赋新值,leftright都指向空,再返回结点指针即可;

        4,创建二叉树

//创建二叉树
BTNode* GreatBTree()
{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;
}

然后我们申请结点来构造二叉树,通过链接将新结点链接起来;

创建的二叉树结构图示如下:

        5,前序遍历

//前序遍历
void PrevOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}printf("%d ", root->data);PrevOrder(root->left);PrevOrder(root->right);
}

二叉树的前序,中序,后续遍历都是同一种思路:

1. 前序遍历(Preorder Traversal 亦称先序遍历)——根结点---->左子树--->右子树

2. 中序遍历(Inorder Traversal)——左子树--->根结点--->右子树

3. 后序遍历(Postorder Traversal)——左子树--->右子树--->根结点

这里要用到递归思想:这里NULLN表示,建议画图来理解,一层一层遍历下去;

前序遍历:

先访问根结点(1)然后访问其左子树(2)打印 1

此时根结点为(2)然后访问其左子树(3)打印1 2

此时根结点为(3)然后访问其左子树(NULL)打印1 2 3

此时根结点为(NULL)return NULL到(3),然后访问(3)的右子树(NULL)打印1 2 3 N

此时根结点为(NULL)return NULL到(3),此时对(3)也就是对(2)的左子树的访问结束了,然后访问(2)的右子树(NULL);打印1 2 3 N N

此时根结点为(NULL)return NULL到(2),此时对(2)也就是对(1)的左子树访问结束了,然后访问(1)的右子树(4)打印1 2 3 N N N

此时根结点为(4)然后访问其左子树(5)打印1 2 3 N N N 4

此时根结点为(5)然后访问其左子树(NULL)打印1 2 3 N N N 4 5

此时根结点为(NULL)return NULL到(5)然后访问(5)的右子树(NULL)打印1 2 3 N N N 4 5 N

此时根结点为(NULL)return NULL到(5)此时对(5)也就是对(4)的左子树的访问结束了,然后访问(4)的右子树(6)打印 1 2 3 N N N 4 5 N N

此时根结点为(6)然后访问其左子树(NULL)打印1 2 3 N N N 4 5 N N 6

此时根结点为(NULL)return NULL到(6)然后访问(6)的右子树(NULL)打印1 2 3 N N N 4 5 N N 6 N

此时根结点为(NULL)return NULL到(6),此时对(6)也就是对(4)的右子树的访问结束了,此时对(4)也就是对(1)的右子树的访问结束了,此时对(1)的访问也结束了,前序遍历也就结束了;打印1 2 3 N N N 4 5 N N 6 N N

图解思路示例:

    

BTNode* root = GreatBTree();
//前序遍历
PrevOrder(root);

这就是前序遍历;

        6,中序遍历

//中序遍历
void InOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}InOrder(root->left);printf("%d ", root->data);InOrder(root->right);
}

中序遍历:左子树--->根结点--->右子树

跟前序遍历思路一致,就是换了一下访问的顺序,按照前序遍历的思路来就完事了;

//中序遍历
InOrder(root);
printf("\n");

        7,后序遍历

//后序遍历
void PostOrder(BTNode* root)
{if (root == NULL){printf("N ");return;}PostOrder(root->left);PostOrder(root->right);printf("%d ", root->data);
}

后序遍历:左子树--->右子树--->根结点

思路还是一致的,就是换了一下访问顺序,前,中,后序遍历的思路都是一致的,只要搞清楚其中一个就全部拿捏了;

//后续遍历
PostOrder(root);
printf("\n");

 这里对二叉链的基础遍历就实现完全了,有人说还有一个层序遍历,这个遍历需要用到队列,目前C语言阶段实现太过于繁琐,后序博主会补上;

三,结点个数以及高度等

像此类问题也都是递归问题,更加看重我们对函数栈帧的理解;

        1,接口函数

//结点个数
int	SumNode(BTNode* root);
//叶子结点个数
int LeafNode(BTNode* root);
//二叉树高度
int HeightTree(BTNode* root);
//二叉树第k层结点个数
int BTreeLeveSize(BTNode* root, int k);
//二叉树查找值为x的结点
BTNode* BTreeFine(BTNode* root, int x);

以上是要实现的函数;

        2,结点个数

//结点个数
int SumNode(BTNode* root)
{return root == NULL ? 0 : SumNode(root->left) + SumNode(root->right) + 1;
}

递归其实说难也难,说不难也不难,是有技巧在里面的;

1,大事化小:根结点为(1)的二叉树的结点总和==>左子树(2)的结点总和加上右子树(4)的结点总和再加上本身的结点个数1,然后根结点为(2)的结点总和==>左子树(3)的总和加上NULL1,这就是规律;【(1)=(2)+(4)+1 】

2,结束条件,当结点为NULL时返回0

//结点个数
printf("%d\n", SumNode(root));

        3,叶子结点个数

//叶子结点个数
int LeafNode(BTNode* root)
{if (root == NULL){return 0;}if (root->left==NULL && root->right==NULL){return 1;}else{return LeafNode(root->left) + LeafNode(root->right);}
}

 

大事化小:求根结点为(1)的二叉树的叶子节点的个数==>其左子树(2)加上其右子树(4)的叶子节点的个数;【(1)=(2)+(4)

结束条件:当结点为NULL时返回0,当结点的左右子树都为NULL时返回1;

        4,二叉树高度

//二叉树高度
int HeightTree(BTNode* root)
{if (root == NULL){return 0;}int left = HeightTree(root->left);int right = HeightTree(root->right);return left > right ? left + 1 : right + 1;
}

大事化小:求根结点为(1)的二叉树的高度==>其左子树(2)与右子树(4)中高的一颗的高度加上本身的高度1;【(1)=(2)>(4)?(2)+1:(4)+1 】

结束条件:当结点为NULL时返回0;

//二叉树高度
printf("%d\n", HeightTree(root));

        5,二叉树第k层结点个数

//二叉树第k层结点个数
int BTreeLeveSize(BTNode* root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}return BTreeLeveSize(root->left, k - 1)  + BTreeLeveSize(root->right, k - 1);
}

大事化小:求根结点为(1)的二叉树第K层的结点个数==>其左子树(2)加上右子树(4)中第K-1层结点的个数;【(1)=(2)+(4)

结束条件:当结点为NULL时返回0,K等于1时返回1;

//二叉树第k层结点个数
printf("%d\n", BTreeLeveSize(root,3));

        6,二叉树查找值为x的结点

//二叉树查找值为x的结点
BTNode* BTreeFine(BTNode* root, int x)
{if (root == NULL){return NULL;}if (root->data == x){return root;}if (BTreeFine(root->left, x) == NULL){return BTreeFine(root->right, x);}else{return BTreeFine(root->left, x);}
}

大事化小:查找根结点为(1)的二叉树中值为x的结点==>查找其左子树(2)与右子树(4)中值为x的结点;

结束条件:当结点为NULL时返回NULL当结点的值为x时返回该结点;

思路:所以当其中一个子树不为NULL时就是所求的结点,如果左子树不为空则返回左子树的结点,否则返回右子树的结点,如果左右都为空那也返回右子树的结点;

//二叉树查找值为x的结点
BTNode* ret = BTreeFine(root, 6);
printf("%d\n", ret->data);ret = BTreeFine(root, 3);
printf("%d\n", ret->data);

到这里就结束了,通过这些题目也充分的认识了二叉树(递归树),这就是递归算法,还是要多画图来理解,递归基层的知识就是函数栈帧的创建与销毁

第三阶段就到这里了,这阶段带大家了解一下二叉树(递归树)的递归思想;

后面博主会陆续更新;

如有不足之处欢迎来补充交流!

完结。。


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

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

相关文章

【Python】自动化办公之路:word自动化实战宝典!

文章目录 前言一、环境安装二、使用步骤1.引入库2.读入数据 Python-docx 编辑已存在文档win32com 将 doc 转为 docxwin32com 操作 word总结 前言 使用Python操作word大部分情况都是写操作,也有少许情况会用到读操作,在本次教程中都会进行讲解&#xff0…

Git(6)——GitHub

目录 一、简介 二、概要 三、注册 ​四、创建仓库 五、推送本地代码 六、拉取远端代码 一、简介 在Git(5)中,我们已经对Git分支的概念和用法有了一定了解,对于在本地进行代码版本管理,其实当前所学的东西基本已经…

C语言生成随机数、C++11按分布生成随机数学习

C语言生成随机数 如果只要产生随机数而不需要设定范围的话,只要用rand()就可以;rand()会返回一随机数值, 范围在0至RAND_MAX 间;RAND_MAX定义在stdlib.h, 其值为2147483647; 如果想要获取在一定范围内的数的话,直接做…

华为云HECS安装docker并安装mysql

1、运行安装指令 yum install docker都选择y,直到安装成功 2、查看是否安装成功 运行版本查看指令,显示docker版本,证明安装成功 docker --version 3、启用并运行docker 3.1启用docker指令 systemctl enable docker 3.2 运行docker指令…

AI绘图提示词Stable Diffusion Prompt 笔记

基础 提示词分为正向提示词(positive prompt)和反向提示词(negative prompt),用来告诉AI哪些需要,哪些不需要词缀的权重默认值都是1,从左到右依次减弱,权重会影响画面生成结果。AI …

LLM预训练之RLHF(一):RLHF及其变种

在ChatGPT引领的大型语言模型时代,国内外的大模型呈现爆发式发展,尤其是以年初的LLaMA模型为首的开源大模型和最近百川智能的baichuan模型,但无一例外,都使用了「基于人类反馈的强化学习」(RLHF)来提升语言…

7.代理模式

1.UML 2.代码 #include <iostream> using namespace std;class Subject{ public:virtual void Request() 0; };class RealSubject:public Subject { public:virtual void Request(){cout << "RealSubject" << endl;} }; class Proxy:public Subj…

【结构型】代理模式(Proxy)

目录 代理模式(Proxy)适用场景代理模式实例代码&#xff08;Java&#xff09; 代理模式(Proxy) 为其他对象提供一种代理以控制对这个对象的访问。Proxy 模式适用于在需要比较通用和复杂的对象指针代替简单的指针的时候。 适用场景 远程代理 (Remote Proxy) 为一个对象在不同…

【Git】03-GitHub

文章目录 1. GitHub核心功能2. GitHub搜索项目3. GitHub搭建个人博客4. 团队项目创建5. git工作流选择5.1 需要考虑的因素5.2 主干开发5.2 Git Flow5.3 GitHub Flow5.4 GitLab Flow(带生产分支)5.4 GitLab Flow(带环境分支)5.4 GitLab Flow(带发布分支) 6. 分支集成策略7. 启用…

redis桌面连接工具Another Redis Desktop Manager使用介绍

Another Redis Desktop Manager是一种类似于navicat的数据库连接工具&#xff0c;专门用来连接redis&#xff0c;使用起来非常简单方便&#xff0c;在这里推荐给大家。 没有用过这个软件的&#xff0c;首先通过下面的网盘链接下载Another Redis Desktop Manager 百度网盘redi…

权限提升WIN篇(腾讯云,CS,MSF)

溢出漏洞 信息收集 操作系统版本ver&#xff0c;systeminfo漏洞补丁信息systeminfo操作系统位数systeminfo杀软防护tasklist /svc网络netstat -ano,ipconfig当前权限whoami 筛选EXP 根据前面的信息收集中的系统版本&#xff0c;位数和补丁情况筛选出合适的EXP 提权 根据EX…

flutter开发实战-长按TextField输入框cut、copy设置为中文复制、粘贴

flutter开发实战-长按TextField输入框cut、copy设置为中文复制、粘贴 在开发过程中&#xff0c;需要长按TextField输入框cut、copy设置为中文“复制、粘贴”&#xff0c;这里记录一下设置的代码。 一、pubspec.yaml设置flutter_localizations 在pubspec.yaml中设置flutter_l…

程序员必须掌握的算法

引言 作为一名程序员&#xff0c;掌握一些重要的算法是必不可少的。算法是解决问题的方法和步骤&#xff0c;对于程序员来说&#xff0c;熟悉和掌握一些常见的算法可以提高编程能力&#xff0c;解决复杂的计算问题。与此同时&#xff0c;算法是计算机科学中的核心概念&#xff…

Cilium 1.11:服务网格的未来已来

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

Linux Ubuntu命令行快速配置C++开发环境

本文介绍在Linux操作系统的Ubuntu版本中&#xff0c;基于命令行&#xff0c;快速配置C 编辑、编译、运行的代码开发环境的简便方法。 在之前的文章Linux操作系统Ubuntu 22.04配置Visual Studio Code与C代码开发环境的方法(https://blog.csdn.net/zhebushibiaoshifu/article/det…

星际争霸之小霸王之小蜜蜂(十三)--接着奏乐接着舞

系列文章目录 星际争霸之小霸王之小蜜蜂&#xff08;十二&#xff09;--猫有九条命 星际争霸之小霸王之小蜜蜂&#xff08;十一&#xff09;--杀杀杀 星际争霸之小霸王之小蜜蜂&#xff08;十&#xff09;--鼠道 星际争霸之小霸王之小蜜蜂&#xff08;九&#xff09;--狂鼠之…

什么是集成测试?集成测试方法有哪些?

1、基本概念&#xff1a; 将软件集成起来后进行测试。集成测试又叫子系统测试、组装测试、部件测试等。集成测试主要是针对软件高层设计进行测试&#xff0c;一般来说是以模块和子系统为单位进行测试。 2、集成测试包含的层次&#xff1a; 1. 模块内的集成&#xff0c;主要是…

OA 电子审批流程是什么?

公司中&#xff0c;最最最常见也是最最最多的就是——各种审批。 我当年第一次实习&#xff0c;在一家国企的行政部门&#xff0c;我们部门领导那个时候最主要的工作就是“打通流程”&#xff0c;咱也不知道他在打通什么流程&#xff0c;反正这个很重要就是了。 结果&#xf…

CSS动效合集之实现气泡发散动画

前言 &#x1f44f;CSS动效合集之实现气泡发散动画&#xff0c;速速来Get吧~ &#x1f947;文末分享源代码。记得点赞关注收藏&#xff01; 1.实现效果 2.实现步骤 定义一个数组bubbles&#xff0c;用来存储气泡列表的基本新&#xff0c;w表示宽高&#xff0c;x表示绝对定位…

130. 被围绕的区域

130. 被围绕的区域 题目-中等难度示例1. 新建boardbfs2. 哈希bfs 题目-中等难度 给你一个 m x n 的矩阵 board &#xff0c;由若干字符 ‘X’ 和 ‘O’ &#xff0c;找到所有被 ‘X’ 围绕的区域&#xff0c;并将这些区域里所有的 ‘O’ 用 ‘X’ 填充。 示例 示例 1&#x…