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

二叉树的创建和遍历实现相关推荐

  1. 二叉树的创建和遍历-C语言实现

    二叉树的创建和遍历-C语言实现 链式存储结构 struct BinaryTreeNode {//数据char data;//左子树BinaryTreeNode *leftChild;//右子树Bina ...

  2. c语言二叉树的生成,C语言实现二叉树的创建以及遍历(递归)

    C语言实现二叉树的创建以及遍历 #include typedef char ElemType; typedef struct BiTNode { ElemType data; struct BiTNo ...

  3. python二叉树的创建与遍历

    二叉树的基本概念: 一个结点的层次直观上来说就是其所在的行,其中根结点层次为1(第一行),其子结点层次为2(第二行),以此类推 二叉树的深度(高度):指的是二叉树中的最大叶子结点所在的层.二叉树的深度 ...

  4. C语言二叉树的创建与遍历

    二叉树的创建与遍历 文章目录 二叉树的创建与遍历 前言 一.二叉树的结构 二.二叉树创建和三种遍历 1. 2.前序遍历 3.中序遍历 4.后序遍历 5.测试代码 总结 前言 二叉树(binary tr ...

  5. [******] 树问题:普通二叉树的创建与遍历

    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 ...

  6. 2.12_binary_tree_二叉树的创建和遍历

    创建二叉树 - 节点链接法 (很笨) class BiTreeNode(object):"""节点链接法"""def __init__(se ...

  7. 二叉树的创建、遍历(递归和非递归实现)、交换左右子数、求高度(c++实现)

    要求:以左右孩子表示法实现链式方式存储的二叉树(lson-rson),以菜单方式设计并完成功能任务:建立并存储树.输出前序遍历结果.输出中序遍历结果.输出后序遍历结果.交换左右子树.统计高度,其中对于 ...

  8. C语言——二叉树的创建与遍历

    二叉树 内容 实现二叉树的创建算法与中序列遍历算法.步骤如下: 将二叉树模拟成完全二叉树,从根结点开始对所有结点进行编号,编号从1开始,在运行过程中输入结点对应的编号和值,最后以编号i=0,结点值x= ...

  9. 【二叉树详解】二叉树的创建、遍历、查找以及删除等-数据结构05

    二叉树 1. 二叉树简介 定义: 每一个结点的子节点数量不超过 2 二叉树的结点分为:左节点.右节点 满二叉树: 每个结点都有两个子结点的二叉树(除了叶子结点外) 完全二叉树: 除去最后一层,是一个满 ...

最新文章

  1. Nginx学习笔记(一) Nginx架构
  2. ts文件编译后变量在vscode里报错
  3. mysql通用查询日志_MySQL通用查询日志(GeneralQueryLog)_MySQL
  4. 魏桥集团创始人张士平去世,他是如何成为山东首富的?
  5. body-content取值的意义
  6. 吴恩达深度学习4.3练习_Convolutional Neural Networks_Car detection
  7. BZOJ1485: [HNOI2009]有趣的数列(卡特兰数+快速幂)
  8. 机场VIP会员管理系统
  9. java如何实现联网象棋代码_java中国象棋联网对战源码
  10. LeetCode之K sum problem
  11. 几种典型信号的频谱 周期单位脉冲序列的频谱
  12. 如何升级到 Ubuntu 20.04
  13. 郑州大学编译原理实验三算符优先分析算法JAVA
  14. TopoJSON格式规范说明
  15. 使用阿里云云服务器一年多的感受
  16. php linux重新写路由器,通过php脚本重启路由器
  17. 基于FPGA的遥控数字时钟设计
  18. SSM(spring.struts2.mybatis)注解式开发步骤
  19. 二进制小数转换为十进制数和
  20. BootstrapDialog.show函数底层简化

热门文章

  1. 英语听力能力如何提升?方法总结
  2. Nginx网络负载均衡,负载均衡,网络负载,网络均衡
  3. excel和python的数值排名
  4. es dsl 提取不重复值_询问操作方法:诊断DSL挂断,从PowerPoint中提取媒体,将IE限制为单个网页...
  5. es基本语句详解 查询语句详解
  6. SEH转化为C++异常三
  7. mac 安装自己的web 开发软件
  8. 服务器修复oxc0000098,修复Win7系统开机时出现0xc0000098错误代码的方法
  9. C语言编程:扑克牌魔术
  10. PID算法详解(精华知识汇总)