二叉树的创建和遍历实现
1 前言
提到**树(Tree)**结构,很容易联想到”大树“,想到这是“一对多关系“特性的数据结构,其相关的名词、概念很多:
- 子树(SubTree)、结点(Node)、根结点(Root)、叶子(Leaf)/ 终端结点、分支结点 / 非终端结点、内部结点、孩子(Child)、双亲(Parent)、兄弟(Sibling)、堂兄弟、祖先、子孙
- 层次(Level)、度(Degree)、深度(Depth)/ 高度
- 空树、有序树、只有根结点的树、普通树、二叉树、斜树、满二叉树、完全二叉树等等
这里对于这些个概念不再一一展开介绍,对以上名词陌生的小伙伴可以自行学习。可参考:二叉树的相关概念
2 二叉树
首先得是颗树,然后是他的节点是两个,限制了分叉数量2。
2.1 顺序储存(数组实现)
图示为一颗满二叉树,我们按照 层次(Level)从0-n(数组下标)进行编号,这样就可以储存二叉树了。
char* arr;
arr[0] = 'A';
arr[1] = 'B';
arr[2] = 'C';
arr[3] = 'D';
arr[4] = 'E';
arr[5] = 'F';
arr[6] = 'G';
这样我们便可以使用数组存储二叉树了。
如果对于一个普通二叉树呢?可以构造出一个满/完全二叉树,对于虚构得节点用’#‘代替。如下一颗普通二叉树,用#补成满二叉树。
char* arr;
arr[0] = 'A';
arr[1] = 'B';
arr[2] = 'C';
arr[3] = '#';
arr[4] = '#';
arr[5] = '#';
arr[6] = 'G';
这里的问题是,将浪费内存,极端点,如果变成了右斜树,顺序储存将浪费大量空间。
2.2 链式储存(链表实现)
链表对我们来说并不陌生,通过结点加箭头的方式来存储数据,节点表示数据元素,箭头表示节点间的关系。
那二叉树的链式结构应该什么样呢?在开始就说二叉树是只有两个分叉的树,那么很简单,二叉树由一个的节点和两个分支组成,即:
- 数据元素
- 左子树分支(结点的左孩子)
- 右子树分支(结点的右孩子)
那现在这个结构就很显然了
/*二叉树的结点的结构体*/
typedef struct BiTNode {char data; //数据域struct BiTNode *lchild; //左孩子指针struct BiTNode *rchild; //右孩子指针
} BiTNode, *BiTree;;
实际上二叉链表是比较常用的存储方式,当然也可以在结构中加入struct BiTNode *parent;
可以很轻松地找到各节点的父节点,这时的链表可以称为三叉链表。
2.3二叉树的创建
二叉树百科中递归定义为:二叉树是一棵空树,或者是一棵由一个根节点和两棵互不相交的,分别称作根的左子树和右子树组成的非空树;左子树和右子树又同样都是二叉树。
既然二叉树是通过递归定义的,所以想要创建一颗二叉树,这里可以借助递归去创造。
前面在构建顺序链表时,将普通二叉树补全成满/完全二叉树。这里当拿到一颗普通二叉树时,能迅速将其个节点内容补齐,如下:
这里使用“#
”代替“NULL”
,补全子节点、叶节点的结构,而NULL(#)节点是不指向任何节点的。
我们通过先序遍历的方法输出下补充后的二叉树,即为:ABD##EG###C#F##
,同样也可以中序遍历,或者后序遍历像这样输出节点,只需对创建函数对应修改即可。
按照前序遍历的顺序创建二叉树,代码如下:
void CreateBiTree(BiTree *T)
{char ch;scanf("%c",&ch);if (ch == '#')*T = NULL; //保证是叶结点else{*T = (BiTree)malloc(sizeof(BiTNode));if (!*T) return;(*T)->data = ch;//生成结点CreateBiTree(&(*T)->lchild);//构造左子树CreateBiTree(&(*T)->rchild);//构造右子树 }
}
值得注意的是,CreateBiTree(BiTree *T)
,的入参是二级指针。一级指针不行嘛,遍历时用的都是一级指针,学生时代并没太注意,只是记下了,稍后代码说明。
这里使用的递归创建和前序遍历是一致的。
3 二叉树遍历
3.1递归实现
前面进行二叉树创建就是根据二叉树的定义,通过递归实现了二叉树的创建,当然遍历也是如此,无非就三件事:访问根结点、找左子树、找右子树。
- 先序遍历
/*
*先序遍历
*root:指向根根节点的指针
*/
void preOrderTranversal(BiTNode *root){if(root == NULL) return;//节点(根或者子根节点)printf("%c\t",root->data);preOrderTranversal(root->lchild);//遍历左子树preOrderTranversal(root->rchild);//遍历右子树
}
- 中序遍历
/*中序遍历*/
void inorder_traversal(BiTNode *root)
{if (root == NULL) { //若二叉树为空,做空操作return;}inorder_traversal(root->lchild); //递归遍历左子树printf("%c\t", root->data); //访问根结点inorder_traversal(root->rchild); //递归遍历右子树
}
- 后续遍历
/*后序遍历*/
void postorder_traversal(BiTNode *root)
{if (root == NULL) { //若二叉树为空,做空操作return;}postorder_traversal(root->lchild); //递归遍历左子树postorder_traversal(root->rchild); //递归遍历右子树printf("%c\t", root->data); //访问根结点
}
main函数
void main(void){BiTree T = NULL;CreateBiTree(&T);printf("先序遍历:");preOrderTranversal(T);printf("\n");printf("中序遍历:");inorder_traversal(T);printf("\n");printf("后序遍历:");postorder_traversal(T);printf("\n");
}
执行下看看:
4 栈实现二叉树遍历
代码不再这里粘贴了,另外不仅栈,队列也可以实现二叉树的遍历。
想学习的小伙伴可参考,栈实现二叉树的遍历
*为什么创建二叉树时使用二级指针做入参
请看下面代码:
#include <stdio.h>//目标:在函数func中将b赋值给*q;
int a= 10;
int b = 100;
int *q;void func(int *p){printf("fun:&p = %p p = %p\n",&p,p);p = &b;printf("fun:&p = %p p = %p\n",&p,p);
}int main(){printf("&a = %p &b = %p &q = %p\n",&a,&b,&q);q = &a;printf("*q = %d q = %p &q = %p\n",*q,q,&q);func(q);printf("*q = %d q = %p &q = %p\n",*q,q,&q);return 0;
}
乍一看去,好像没有问题,我们将指针q传入函数func,那好先运行一下看看结果如下:
我们并没有成功将q指向b,这里为什么呢?
因为func的入参是q,即进入的参数是&a,然后在func起来后,在堆栈申请内存,将b的值放入该地址,但返回时也将销毁。
我们可以通过二级指针才操作,具体如下:
func()入参改为二级指针,入参为&q的地址
void func(int **p){printf("fun:&p = %p p = %p\n",&p,p);*p = &b;//改变q指向的内容printf("fun:&p = %p p = %p\n",&p,p);
}int main(){printf("&a = %p &b = %p &q = %p\n",&a,&b,&q);q = &a;printf("*q = %d q = %p &q = %p\n",*q,q,&q);func(&q);printf("*q = %d q = %p &q = %p\n",*q,q,&q);return 0;
}
此时,将可以修改q指向的内容。我写的有点乱,可以拿笔画一画简单体会下。哈哈哈~
参考
https://www.jianshu.com/p/12848eef3452
https://cloud.tencent.com/developer/article/1819521
https://blog.csdn.net/lingling_nice/article/details/80960439
二叉树的创建和遍历实现相关推荐
- 二叉树的创建和遍历-C语言实现
二叉树的创建和遍历-C语言实现 链式存储结构 struct BinaryTreeNode {//数据char data;//左子树BinaryTreeNode *leftChild;//右子树Bina ...
- c语言二叉树的生成,C语言实现二叉树的创建以及遍历(递归)
C语言实现二叉树的创建以及遍历 #include typedef char ElemType; typedef struct BiTNode { ElemType data; struct BiTNo ...
- python二叉树的创建与遍历
二叉树的基本概念: 一个结点的层次直观上来说就是其所在的行,其中根结点层次为1(第一行),其子结点层次为2(第二行),以此类推 二叉树的深度(高度):指的是二叉树中的最大叶子结点所在的层.二叉树的深度 ...
- C语言二叉树的创建与遍历
二叉树的创建与遍历 文章目录 二叉树的创建与遍历 前言 一.二叉树的结构 二.二叉树创建和三种遍历 1. 2.前序遍历 3.中序遍历 4.后序遍历 5.测试代码 总结 前言 二叉树(binary tr ...
- [******] 树问题:普通二叉树的创建与遍历
1. 二叉树的创建 String pre_str = "1,2,4,8,-1,-1,9,-1,-1,5,-1,-1,3,6,-1,10,-1,-1,7,-1,-1";//先序输入S ...
- 2.12_binary_tree_二叉树的创建和遍历
创建二叉树 - 节点链接法 (很笨) class BiTreeNode(object):"""节点链接法"""def __init__(se ...
- 二叉树的创建、遍历(递归和非递归实现)、交换左右子数、求高度(c++实现)
要求:以左右孩子表示法实现链式方式存储的二叉树(lson-rson),以菜单方式设计并完成功能任务:建立并存储树.输出前序遍历结果.输出中序遍历结果.输出后序遍历结果.交换左右子树.统计高度,其中对于 ...
- C语言——二叉树的创建与遍历
二叉树 内容 实现二叉树的创建算法与中序列遍历算法.步骤如下: 将二叉树模拟成完全二叉树,从根结点开始对所有结点进行编号,编号从1开始,在运行过程中输入结点对应的编号和值,最后以编号i=0,结点值x= ...
- 【二叉树详解】二叉树的创建、遍历、查找以及删除等-数据结构05
二叉树 1. 二叉树简介 定义: 每一个结点的子节点数量不超过 2 二叉树的结点分为:左节点.右节点 满二叉树: 每个结点都有两个子结点的二叉树(除了叶子结点外) 完全二叉树: 除去最后一层,是一个满 ...
最新文章
- Nginx学习笔记(一) Nginx架构
- ts文件编译后变量在vscode里报错
- mysql通用查询日志_MySQL通用查询日志(GeneralQueryLog)_MySQL
- 魏桥集团创始人张士平去世,他是如何成为山东首富的?
- body-content取值的意义
- 吴恩达深度学习4.3练习_Convolutional Neural Networks_Car detection
- BZOJ1485: [HNOI2009]有趣的数列(卡特兰数+快速幂)
- 机场VIP会员管理系统
- java如何实现联网象棋代码_java中国象棋联网对战源码
- LeetCode之K sum problem
- 几种典型信号的频谱 周期单位脉冲序列的频谱
- 如何升级到 Ubuntu 20.04
- 郑州大学编译原理实验三算符优先分析算法JAVA
- TopoJSON格式规范说明
- 使用阿里云云服务器一年多的感受
- php linux重新写路由器,通过php脚本重启路由器
- 基于FPGA的遥控数字时钟设计
- SSM(spring.struts2.mybatis)注解式开发步骤
- 二进制小数转换为十进制数和
- BootstrapDialog.show函数底层简化
热门文章
- 英语听力能力如何提升?方法总结
- Nginx网络负载均衡,负载均衡,网络负载,网络均衡
- excel和python的数值排名
- es dsl 提取不重复值_询问操作方法:诊断DSL挂断,从PowerPoint中提取媒体,将IE限制为单个网页...
- es基本语句详解 查询语句详解
- SEH转化为C++异常三
- mac 安装自己的web 开发软件
- 服务器修复oxc0000098,修复Win7系统开机时出现0xc0000098错误代码的方法
- C语言编程:扑克牌魔术
- PID算法详解(精华知识汇总)