椋鸟数据结构笔记#8:二叉树的遍历、创建与销毁

萌新的学习笔记,写错了恳请斧正。

链式二叉树

这篇笔记我们讨论基于链式二叉树,其节点的数据结构如下:

typedef int BTDatatype;typedef struct BTNode
{BTDataType data;struct BTNode* left;struct BTNode* right;
} BTNode;
二叉树的遍历

先了解关于二叉树的遍历再去了解二叉树的创建会更轻松一点。

二叉树的遍历基本分两类,一类是深度优先的,另一类是广度优先的。

对于深度优先的遍历,又分为前序遍历、中序遍历、后序遍历;对于广度优先的遍历,只有层序遍历。

前序遍历(PreOrder Traversal)

从根节点开始,对于每一个子树,先访问其根节点,再访问其左子树和右子树。

比方说,对于下面这个二叉树:

在这里插入图片描述

我们先访问其根节点1,然后是其左子树
对于其左子树,我们还是先访问其根节点2,然后是其左子树
我们继续访问该节点的左子树3,然后是3的左子树
发现3的左子树为空,那么返回访问3的右子树
3的右子树也为空,这是2的左子树访问完了就轮到2的右子树了
2的右子树为空,那么1的左子树就访问完了轮到1的右子树
1的右子树为4,访问完根节点4后访问其左子树5
5的左子树右子树为空,那么4的左子树就访问完了
最后是4的右子树6

所以最终节点的访问顺序为:

1–>2–>3–>N–>N–>N–>4–>5–>N–>N–>6–>N–>N

下面这张图可能更直观些:

在这里插入图片描述

下面给出其实现(递归的思想):

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

中序遍历对每一个子树则是先访问其左子树,随后是根节点和右子树。

对于上方的示例,如果用中序遍历,顺序就会变为:

N–>3–>N–>2–>N–>1–>N–>5–>N–>4–>N–>6–>N

下面给出其实现:

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

同理,后序遍历就是最后访问根节点,对于上例的顺序为:

N–>N–>3–>N–>2–>N–>N–>5–>N–>N–>6–>4–>1

下面给出其实现:

void BinaryTreeInOrder(BTNode* root)
{if (root == NULL){return;}BinaryTreeInOrder(root->left);BinaryTreeInOrder(root->right);printf("%c ", root->data);
}
层序遍历(LevelOrder Traversal)

层序遍历就是逐层的一个一个元素遍历,与前几个一条路走到底再拐回来的遍历方式不同

想要实现二叉树的层序遍历,就不仅仅是递归这么简单了,我们需要借助之前学的数据结构:队列

层序遍历的步骤:

  1. 初始化队列: 首先,将根节点放入队列中。
  2. 遍历队列: 当队列不为空时,重复以下步骤:
    • 从队列中取出一个元素(节点)。
    • 访问这个节点。
    • 如果这个节点有左子节点,则将左子节点放入队列。
    • 如果这个节点有右子节点,则将右子节点放入队列。

通过这种方式,可以保证每个节点都按照层序被访问一次,同时每个节点只会被放入队列一次,从而实现二叉树的层序遍历。

对于这个方法的理解,ChatGPT给出了一个比较生动的例子:

想象一下,你在组织一场大型的派对游戏,游戏的目标是确保每个人都能按顺序获得一个气球。派对的参与者按照到达的顺序站成一列,每个人都可能带着一两个朋友。在这个比喻中,每个参与者就像是树中的一个节点,他们带来的朋友分别对应于这个节点的左右子节点。

  1. 开始游戏: 游戏开始时,你手里只有一个气球,而第一个参与者(对应于树的根节点)站在你面前。你将这个气球给了他,并询问是否有朋友一起来。他回答说带了两个朋友(左右子节点)。
  2. 组织队列: 为了管理游戏的顺序,你拿出一个大篮子(对应于队列),让第一个参与者的朋友们按照他们被介绍的顺序站到篮子后面。现在,篮子里有两个人在等待气球。
  3. 进行派对游戏: 接下来,你从篮子(队列)的前端取出一个人(节点),给他一个气球,并询问他是否也有带朋友来。如果有,他的朋友们也按顺序加入到篮子的末尾。
  4. 重复步骤: 按照这个流程,你一直重复,总是从篮子的前端取人,给他气球,然后将他的朋友们(如果有的话)加入到篮子的末尾。

通过这个过程,每个参与者都会按照他们到达派对的顺序获得气球。同样地,在二叉树的层序遍历中,我们利用队列这一数据结构,确保了每个节点都能按照它们在树中的层级顺序被访问。就像在游戏中一样,通过不断地将朋友(子节点)加入队列并按顺序访问,我们能够遍历树中的每一个节点,从而实现层序遍历。

下面给出其实现:

void BinaryTreeLevelOrder(BTNode* root)
{Queue q;QueueInit(&q);if (root != NULL){QueuePush(&q, root);}while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front->_left != NULL){printf("%c ", front->_data);QueuePush(&q, front->_left);QueuePush(&q, front->_right);}else{printf("NULL ");}}printf("\n");QueueDestory(&q);
}
二叉树的创建与销毁

我们以一道题为例:

编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以指针方式存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,空格字符代表空树。建立起此二叉树以后,再对二叉树进行中序遍历,输出遍历结果。

有了前面的内容这里就很好完成了,实现如下:

BTNode* BuyBTNode(BTDataType x)
{BTNode* new = (BTNode*)malloc(sizeof(BTNode));if (new == NULL){perror("malloc");exit(EXIT_FAILURE);}new->_data = x;new->_left = NULL;new->_right = NULL;return new;
}BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi)
{if (*pi >= n || a[*pi] == '#') {(*pi)++; // 跳过当前的'#'或越界的元素return NULL;}BTNode* root = BuyBTNode(a[(*pi)++]);root->_left = BinaryTreeCreate(a, n, pi);root->_right = BinaryTreeCreate(a, n, pi);return root;
}

而二叉树的销毁就更加简单了:

void BinaryTreeDestroy(BTNode** root)
{if (*root == NULL){return;}BinaryTreeDestroy(&(*root)->_left);BinaryTreeDestroy(&(*root)->_right);free(*root);*root = NULL;
}
其他二叉树相关
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{if (root == NULL){return 0;}return 1 + BinaryTreeSize(root->_left) + BinaryTreeSize(root->_right);
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL){return 0;}if (root->_left == NULL && root->_right == NULL){return 1;}return BinaryTreeLeafSize(root->_left) + BinaryTreeLeafSize(root->_right);
}
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL){return NULL;}if (root->_data == x){return root;}BTNode* ret = BinaryTreeFind(root->_left, x);if (ret != NULL){return ret;}return BinaryTreeFind(root->_right, x);
}
// 判断二叉树是否是完全二叉树
int BinaryTreeComplete(BTNode* root)
{Queue q;QueueInit(&q);if (root != NULL){QueuePush(&q, root);}while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front == NULL){break;}QueuePush(&q, front->_left);QueuePush(&q, front->_right);}while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);if (front != NULL){QueueDestroy(&q);return 0;}}QueueDestroy(&q);return 1;
}

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

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

相关文章

蓝桥杯 经验技巧篇

1. 注意事项 👨‍🏫 官方通知 👨‍🏫 资料文档 时间:4月13日 9:00~13:00 (时长 4小时)物品 准考证(赛前一周开放下载,自行打印)学生证身份证笔、水、外套&a…

【Claude 3】This organization has been disabled.此组织已被禁用。(Claude无法对话的原因和解决办法)

Claude对话提示 This organization has been disabled.此组织已被禁用。 This organization has been disabled.此组织已被禁用。 This organization has been disabled.此组织已被禁用。 问题截图 问题原因 出现该页面,表示您的账户已经无法使用,可能…

Qt+OpenGL-part3

1-4EBO画矩形_哔哩哔哩_bilibili 可以绘制两个三角形来组成一个矩形&#xff08;OpenGL主要处理三角形&#xff09; 直接画两个三角形&#xff1a; #include "openglwidget.h" #include <QDebug>unsigned int VBO,VAO; unsigned int shaderProgram;//顶点着…

CSS-属性

&#x1f4da;详见 W3scholl&#xff0c;本篇只做快速思维索引。 CSS 背景 用于定义元素的背景效果。 background-colorbackground-imagebackground-positionbackground-repeatbackground-attachment background-color background-color 属性指定元素的背景色。 h1 {back…

docker基础学习指令

文章目录 [toc] docker基础常用指令一、docker 基础命令二、docker 镜像命令1. docker images2. docker search3. docker pull4. docker system df5. docker rmi1. Commit 命令 三、 docker 容器命令1. docker run2. docker logs3. docker top4. docker inspect5. docker cp6. …

EChart简单入门

echart的安装就细不讲了&#xff0c;直接去官网下&#xff0c;实在不会的直接用cdn,省的一番口舌。 cdn.staticfile.net/echarts/4.3.0/echarts.min.js 正入话题哈 什么是EChart&#xff1f; EChart 是一个使用 JavaScript 实现的开源可视化库&#xff0c;Echart支持多种常…

显示学习1(基于树莓派Pico) -- 基础

先上图为敬。 驱动的是0.96寸的OLED&#xff0c;SSD1315。使用的I2C接口驱动。 有一说一树莓派Pico用来学习底层真的太好了&#xff0c;没有之一。首先是价格便宜&#xff0c;10块钱包邮还要什么自行车。然后底层封装很完备&#xff0c;接近闭源。最后是用的python&#xff0c…

Java笔试总结

. 操作系统中关于竞争和死锁的关系下面描述正确的是&#xff1f; A 竞争一定会导致死锁 B 死锁一定由竞争引起 C 竞争可能引起死锁 D 预防死锁可以防止竞争 答案: C 进程的控制信息和描述信息存放在()。 A JCB B PCB C AFT D SFT 答案: B 当系统发生抖动&#xff08;thrash…

python上传以及下载AWS S3上的文件

​ 免死金牌 由于本人平常是做NodeJS开发的&#xff0c;本次做的任务含有 Scheduled Job &#xff0c;所以选择了使用Python作为这次开发的语言&#xff0c;毕竟跑脚本还是这玩意适合。 其中有一个任务是要从S3上拉下一些文件来处理&#xff0c;处理完成后再push 上去的需求…

数据结构——堆的应用

堆的应用 1.堆排序2. topK问题 堆结构主要有两个应用&#xff1a;1、堆排序 2、topK问题 1.堆排序 现实中&#xff0c;排序是非常常见的&#xff0c;比如排序班级同学的各科分数&#xff0c;购物时&#xff0c;商品会按销量&#xff0c;价格&#xff0c;好评数等进行排序。相…

【游戏分析】非游戏领空追字符串来源

通过NPC名称找NPC数组 扫描 NPC名字 ASIC型 发现全部都有后缀 那么采用 字节集的方式去扫描 也是扫不到 说明:不是ASIC型字符串 扫描 NPC名字 Unicode型 没有结果 那么转换成字节集去扫描 终于发现结果了 把结果挨个修改字符串 发现 其中两个是可以用的 22和23 …

GIt 删除某个特定commit

目的 多次commit&#xff0c;想删掉中间的一个/一些commit 操作方法 一句话说明&#xff1a;利用rebase命令的d表示移除commit的功能&#xff0c;来移除特定的commit # 压缩这3次commit,head~3表示从最近1次commit开始&#xff0c;前3个commit git rebase -i head~3rebase…