数据结构与算法——二叉树+带你实现表达式树(附源码)

简介: 📖作者介绍:22级树莓人(计算机专业),热爱编程<目前在c++阶段,因为最近参加新星计划算法赛道(白佬),所以加快了脚步,果然急迫感会增加动力>——目标Windows,MySQL,Qt,数据结构与算法,Linux,多线程,会持续分享学习成果和小项目的📖作者主页:king&南星📖专栏链接:数据结构🎉欢迎各位→点赞👏 + 收藏💞 + 留言🔔​💬总结:希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 🐾————————————————版权声明:本文为CSDN博主「热爱编程的小K」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

9d47d307221344818bc1c6d99a6260b2.png

文章目录

👨‍🎓二叉树

🧑‍🎓一、概念及定义

⛵1、概念

⛵2、性质

👩‍🎓 二、结点的定义、链表应用、空节点的说明

⛵1、结点声明

⛵2、链表的应用

⛵ 3、空结点的说明及画图

🧑‍🏫三、表达式树——遍历

⛵1、表达式树引入与介绍

⛵2、中序遍历

⛵3、后序遍历

⛵4、先序遍历

⛵5、总结

⛵ 6、构建一颗表达式树

⛵A、第一步

⛵B、第二步

⛵C、第三步

⛵D、第四步

⛵E、第五步

⛵F、第六步

🧑‍⚖️四、查找节点

🧑‍🌾五、插入节点

👩‍🔧六、综合代码

👨‍🎓二叉树

🧑‍🎓一、概念及定义

⛵1、概念

二叉树是一棵树,其中每个结点的儿子都不能多于两个

如下图是由一个跟和两个子树组成的二叉树,且子树可能为空

b3021c80edba4bc79b67fc16e01f64d4.png

⛵2、性质

二叉树的一个性质是平均二叉树的深度要比N小得多。分析表明,这个平均深度为O(根号N),而对于特殊的二叉树,即二叉查找树,其深度的平均值为O(logN),不幸的是这个深度是可以大到N-1的,如下图所示

8e81d5ae65e149de87b7f67be61b12f4.png

👩‍🎓 二、结点的定义、链表应用、空节点的说明

⛵1、结点声明

因为一颗二叉树最多有两个儿子,所以我们可以用指针直接指向他们。树节点的声明在结构体是类似于双链表的声明,在声明中,一个节点就是由关键字信息加上两个指向其他节点的指针(Lift和Right)组成的结构

//叶子节点数据

#define EMPTY 6666

//是否要显示EMPTY  为1 不显示EMPTY  为0 显示EMPTY

#define NOTSHOWEMPTY  1

typedef struct Node

{

int data;

struct Node* pLift;

struct Node* pright;

}Node;

⛵2、链表的应用

在进行一次插入时需要调用malloc创建一个节点,节点可以在调用free删除后释放

Node* createNode(int newNodedata)  

{

Node* newNode = malloc(sizeof(Node));

if (NULL == newNode) return newNode;

newNode->data = newNodedata;

newNode->pLift = newNode->pright = NULL;

return newNode;

}

⛵ 3、空结点的说明及画图

我们可以用链表的知识用矩形框画出二叉树,但是,树一般画成圆圈并用一些直线连接起来,因为二叉树实际上就是图,当涉及树时,我们也不显示地画出NULL指针,因为具有N个节点的每一颗二叉树都需要N+1个NULL指针,我们在这里描述的二叉树是无序二叉树,叶子节点用EMPTY节点表示

🧑‍🏫三、表达式树——遍历

⛵1、表达式树引入与介绍

下图是一个表达式树,表达式树的树叶是操作数,比如常量或变量,而其他的节点为操作符。由于这里所有的操作都是二元的,因此这颗特定的树正好是二叉树,虽然这是最简单的情况,但是节点含有的儿子还是有可能多于两个的。一个节点也有可能只有一个儿子,如具有一目操作符的情况。可以将通过递归计算左子树和右子树所得到的值应用在根处的算符操作中而算出表达式树T的值。在本例中,左子树的值为a+(b*c),右子树的值为(((d*e)+f)*g),因此整颗表达式的值为a+(b*c)+(((d*e)+f)*g)

cd213d115ebf4cbe829e4aae24732866.png

这里我们先看看我们这里用的数据

7bcc4f1fdfb0412dbfde7382330f60f8.png

⛵2、中序遍历

我们可以通过递归产生一个带括号的左表达式,然后打印出在根处的运算符,然后再递归地产生一个带括号的右表达式而得到一个(对两个括号整体进行运算的)中缀表达式。这种一般的方法(左,根,右)被称为中序遍历

代码如下

void _midTravel(Node* root)  

{

if (NULL == root) return;

_midTravel(root->pLift);

#if NOTSHOWEMPTY

if (root->data != EMPTY)

#endif

 printf("%d ", root->data);

_midTravel(root->pright);

}

⛵3、后序遍历

就是递归打印出左子树,右子树,然后打印根节点,也就是后缀表达式,这种遍历一般称为后序遍历

代码如下

void _lstTravel(Node* root)  

{

if (NULL == root) return;

_lstTravel(root->pLift);

_lstTravel(root->pright);

#if NOTSHOWEMPTY

if (root->data != EMPTY)

#endif

 printf("%d ", root->data);

}

⛵4、先序遍历

先序遍历就是先打印出根节点,然后递归的打印出右子树和左子树,是一种前序记法

代码如下

void _preTravel(Node* root)  

{

if (NULL == root) return;

#if NOTSHOWEMPTY

if (root->data != EMPTY)

#endif

 printf("%d ", root->data);

_preTravel(root->pLift);

_preTravel(root->pright);

}

⛵5、总结

已知中序 和 先序 可以推导出树 知道后序

已知中序 和 后序 可以推导出树 知道先序

已知先序和后序,不能推导出树

这是因为只要知道先序和后序一个就可以知道根节点了,知道了中序就可以推导出左右子树了

⛵ 6、构建一颗表达式树

我们现在给出一种算法来把后缀表达式转换为表达式树。这种方法酷似后缀求值算法。一次一个符号地读入表达式,如果符号是操作数,那么我们就建立一个单节点树并将一个指向它的指针推入栈中。如果符号是操作符,那么我们就从栈中弹出指向两棵树T1和T2的两个指针(T1的先弹出)并形成一颗新的树,该树的根就是操作符,它的左右儿子分别指向T2和T1。然后将指向这棵树的指针压入栈中


来看一个例子,设输入为:ab+cde+**


⛵A、第一步

前两个符号是操作数,因此我们创建两颗单节点树并将指向它们的指针压入栈中


b92b665e7ac34eaa871726f987dba83b.png

⛵B、第二步

接着,读入“+”,因此弹出指向这两颗树的指针,一棵新的树新成,而将指向该树的指针压入栈中

330767d760fb421c81fa55abaca9f885.png

⛵C、第三步

然后,读入c、d、e,在每棵单节点树创建后,将指向对应的树的指针压入栈中

5891d0b7040f43c0a4222fbcf48fd224.png

⛵D、第四步

接下来读入“+”,因此两棵树合并

210940f1c07e4693a7a5c87b7ff4942b.png

⛵E、第五步

继续进行,读入“”,因此,弹出两个树指针并形成一颗新的树,“”是它的根

a0fda0a3cfe24efdb0109dde4fc35718.png

⛵F、第六步

最后,读入最后一个符号,两棵树合并,而指向最后的树的指针留在栈中

38027f61535942a78bc4eb7bf82051a7.png

🧑‍⚖️四、查找节点

Node* findNode(Node* root, int findData)  

{

if (NULL == root) return NULL;

if (findData = EMPTY) return NULL;

if (root->data == findData) return root;

Node* pTemp = findNode(root->pLift, findData);

if (pTemp) return pTemp;

return findNode(root->pright, findData);

}

🧑‍🌾五、插入节点

这里就体现了递推的大用处了,还有就是要传二级指针,因为要修改根节点

//插入(先序遍历)

bool inserData(Node** root, int insertdata)

{

if (NULL == root) return false;

//插入根节点

if( NULL == *root )

{

 *root = createNode(insertdata);

 return true;

}

if ((*root)->data == EMPTY) return false;

if (true == inserData(&((*root)->pLift), insertdata))

 return true;

else

 return inserData(&((*root)->pright), insertdata);

}

👩‍🔧六、综合代码

#include<stdio.h>

#include<string.h>

#include<stdlib.h>

#include<stdbool.h>

//叶子节点数据

#define EMPTY 6666

//是否要显示EMPTY  为1 不显示EMPTY  为0 显示EMPTY

#define NOTSHOWEMPTY  1

typedef struct Node

{

int data;

struct Node* pLift;

struct Node* pright;

}Node;

//创建节点函数

Node* createNode(int newNodedata);

//PRE:先序遍历,MID:中序遍历,LST:后序遍历

enum travelType { PRE, MID, LST };

//遍历

void Travel(Node* root, enum travelType type);

//先序遍历

void _preTravel(Node* root);

//中序遍历

void _midTravel(Node* root);

//后序遍历

void _lstTravel(Node* root);

//插入(先序遍历)

bool inserData(Node** root, int insertdata);

//找到数据为findData的第一个节点 找到返回节点地址 否则返回NULL

Node* findNode(Node* root, int findData);

int main()  

{

//一颗空树

Node* pRoot = NULL;

int arr[] = { 10, 99, 83, EMPTY, 22, EMPTY, EMPTY, EMPTY ,96,

 EMPTY, 56, 6, EMPTY, EMPTY, 11, 36, EMPTY, EMPTY, EMPTY,666 };

for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)

 inserData(&pRoot, arr[i]);

Travel(pRoot, PRE);

Travel(pRoot, MID);

Travel(pRoot, LST);

return 0;

}

//创建节点函数

Node* createNode(int newNodedata)  

{

Node* newNode = malloc(sizeof(Node));

if (NULL == newNode) return newNode;

newNode->data = newNodedata;

newNode->pLift = newNode->pright = NULL;

return newNode;

}

//遍历

void Travel(Node* root, enum travelType type)  

{

switch (type)

{

case PRE:

 printf("先序遍历:");

 _preTravel(root);

 printf("\n");

 break;

case MID:

 printf("中序遍历:");

 _midTravel(root);

 printf("\n");

 break;

case LST:

 printf("后序遍历: ");

 _lstTravel(root);

 printf("\n");

 break;

}

}

//先序遍历

void _preTravel(Node* root)  

{

if (NULL == root) return;

#if NOTSHOWEMPTY

if (root->data != EMPTY)

#endif

 printf("%d ", root->data);

_preTravel(root->pLift);

_preTravel(root->pright);

}

//中序遍历

void _midTravel(Node* root)  

{

if (NULL == root) return;

_midTravel(root->pLift);

#if NOTSHOWEMPTY

if (root->data != EMPTY)

#endif

 printf("%d ", root->data);

_midTravel(root->pright);

}

//后序遍历

void _lstTravel(Node* root)  

{

if (NULL == root) return;

_lstTravel(root->pLift);

_lstTravel(root->pright);

#if NOTSHOWEMPTY

if (root->data != EMPTY)

#endif

 printf("%d ", root->data);

}

//插入(先序遍历)

bool inserData(Node** root, int insertdata)

{

if (NULL == root) return false;

//插入根节点

if( NULL == *root )

{

 *root = createNode(insertdata);

 return true;

}

if ((*root)->data == EMPTY) return false;

if (true == inserData(&((*root)->pLift), insertdata))

 return true;

else

 return inserData(&((*root)->pright), insertdata);

}

//找到数据为findData的第一个节点 找到返回节点地址 否则返回NULL

Node* findNode(Node* root, int findData)  

{

if (NULL == root) return NULL;

if (findData = EMPTY) return NULL;

if (root->data == findData) return root;

Node* pTemp = findNode(root->pLift, findData);

if (pTemp) return pTemp;

return findNode(root->pright, findData);

}

版权声明:本文为CSDN博主「热爱编程的小K」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qq_72157449/article/details/129700648

相关文章
|
1月前
|
算法
数据结构之博弈树搜索(深度优先搜索)
本文介绍了使用深度优先搜索(DFS)算法在二叉树中执行遍历及构建链表的过程。首先定义了二叉树节点`TreeNode`和链表节点`ListNode`的结构体。通过递归函数`dfs`实现了二叉树的深度优先遍历,按预序(根、左、右)输出节点值。接着,通过`buildLinkedList`函数根据DFS遍历的顺序构建了一个单链表,展示了如何将树结构转换为线性结构。最后,讨论了此算法的优点,如实现简单和内存效率高,同时也指出了潜在的内存管理问题,并分析了算法的时间复杂度。
49 0
|
4天前
|
机器学习/深度学习 前端开发 算法
婚恋交友系统平台 相亲交友平台系统 婚恋交友系统APP 婚恋系统源码 婚恋交友平台开发流程 婚恋交友系统架构设计 婚恋交友系统前端/后端开发 婚恋交友系统匹配推荐算法优化
婚恋交友系统平台通过线上互动帮助单身男女找到合适伴侣,提供用户注册、个人资料填写、匹配推荐、实时聊天、社区互动等功能。开发流程包括需求分析、技术选型、系统架构设计、功能实现、测试优化和上线运维。匹配推荐算法优化是核心,通过用户行为数据分析和机器学习提高匹配准确性。
22 3
|
23天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
44 5
|
1月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
81 4
|
1月前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
75 16
|
1月前
|
搜索推荐 算法 C语言
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
94 8
|
1月前
|
搜索推荐 算法 C语言
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
105 7
|
1月前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
129 8
|
1月前
|
算法
数据结构之文件系统模拟(树数据结构)
本文介绍了文件系统模拟及其核心概念,包括树状数据结构、节点结构、文件系统类和相关操作。通过构建虚拟环境,模拟文件的创建、删除、移动、搜索等操作,展示了文件系统的基本功能和性能。代码示例演示了这些操作的具体实现,包括文件和目录的创建、移动和删除。文章还讨论了该算法的优势和局限性,如灵活性高但节点移除效率低等问题。
50 0
|
2月前
|
Java C++
【数据结构】探索红黑树的奥秘:自平衡原理图解及与二叉查找树的比较
本文深入解析红黑树的自平衡原理,介绍其五大原则,并通过图解和代码示例展示其内部机制。同时,对比红黑树与二叉查找树的性能差异,帮助读者更好地理解这两种数据结构的特点和应用场景。
38 0
下一篇
DataWorks