【二叉树】【数据结构】【buu】[GUET-CTF2019]number_game
【二叉树】【数据结构】【buu】[GUET-CTF2019]number_game
【二叉树】
参考链接:
[数据结构——顺序队列与链式队列的实现]https://blog.csdn.net/m0_74712453/article/details/135351817?spm=1001.2014.3001.5502
[东哥带你刷二叉树(纲领篇)]https://labuladong.online/algo/essential-technique/binary-tree-summary/
正文
一、树的概念
1、树的定义
1)树
树是 n(n≥0) 个结点的有限集合。当 n>0 时,它是一棵非空树,满足如下条件:
1)有且仅有一个特定的结点,称为根结点Root;
2)除根结点外,其余结点分为 m 个互不相交的有限集合 T1、T2、…………、Tm,其中每一个 Ti(1≤i≤m) 又是一棵树,并且为根结点 Root 的子树。如图所示,代表的是一棵以 a 为根结点的树。
2)空树
当 n=0,也就是 0 个结点的情况也是树,它被称为空树。
3)子树
树的定义用到了递归的思想。即树的定义中还是用到了树的概念,如图所示,T1 和 T2 就是结点 a 的子树。结点 d、g、h、i 组成的树又是结点 b 的子树等等。
子树的个数没有限制,但是它们一定是互不相交的,如下图所示的就不是树。因为在这两个图中,a 的子树都有相交的边。
2、结点的定义
树的结点包含一个 数据域 和 m 个 指针域 用来指向它的子树。结点的种类分为:根结点、叶子结点、内部结点。结点拥有子树的个数被称为 结点的度。树中各个结点度的最大值被称为 树的度。
1)根结点
一棵树的根结点只有一个。
2)叶子结点
度为 0 的结点被称为 叶子结点 或者 终端结点。叶子结点的不指向任何子树。
3)内部结点
除了根结点和叶子结点以外的结点,被称为内部结点。
如上图所示,红色结点 为根结点,蓝色结点 为内部结点,黄色结点 为叶子结点。
3、结点间关系
1)孩子结点
对于某个结点,它的子树的根结点,被称为该结点的 孩子结点。
如上图所示,黄色结点 d 是 红色结点 b 的孩子结点。
2)父结点
而该结点被称为孩子结点的 父结点。
如上图所示,蓝色结点 a 是 红色结点 b 的父结点。
3)兄弟结点
同一父结点下的孩子结点,互相称为 兄弟结点。
如上图所示,绿色结点 c 和 红色结点 b 互为兄弟结点。
4、树的深度
结点的层次从根结点开始记为第 1 层,如果某结点在第 i 层,则它的子树的根结点就在第i+1 层,树中结点的最大层次称为 树的深度。
如下图所示,代表的是一棵深度为 4 的树。
5、森林的定义
森林是 m 棵 互不相交的树的集合,对于树的每个结点而言,其子树集合就是森林。
如图所示,b 和 c 两棵子树组成的集合就是一个森林。
二、二叉树的概念
1、二叉树的性质
二叉树是一种树,它有如下几个特征:
1)每个结点最多 2 棵子树,即每个结点的孩子结点个数为 0、1、2;
2)这两棵子树是有顺序的,分别叫:左子树 和 右子树;
3)如果只有一棵子树的情况,也需要区分顺序,如图所示:
b 为 a 的左子树;
c 为 a 的右子树;、
2、特殊二叉树
1)斜树
所有结点都只有左子树的二叉树被称为左斜树。
所有结点都只有右子树的二叉树被称为右斜树。
斜树有点类似线性表,所以线性表可以理解为一种特殊形式的树。
2)满二叉树
对于一棵二叉树,如果它的所有根结点和内部结点都存在左右子树,且所有叶子结点都在同一层,这样的树就是满二叉树。
满二叉树有如下几个特点:
1)叶子结点一定在最后一层;
2)非叶子结点的度为 2;
3)深度相同的二叉树,满二叉树的结点个数最多,为 (其中 h 代表深度)。
2)完全二叉树
对一棵具有 n 个结点的二叉树按照层序进行编号,如果编号 i 的结点和同样深度的满二叉树中的编号 i 的结点在二叉树中位置完全相同,则被称为 完全二叉树。
满二叉树一定是完全二叉树,而完全二叉树则不一定是满二叉树。
完全二叉树有如下几个特点:
1)叶子结点只能出现在最下面两层。
2)最下层的叶子结点一定是集中在左边的连续位置;倒数第二层如果有叶子结点,一定集中在右边的连续位置。
3)如果某个结点度为 1,则只有左子树,即 不存在只有右子树 的情况。
4)同样结点数的二叉树,完全二叉树的深度最小。
如下图所示,就不是一棵完全二叉树,因为 5 号结点没有右子树,但是 6 号结点是有左子树的,不满足上述第 2 点。
3、二叉树的性质
接下来我们来看下,二叉树有哪些重要的性质。
1)性质1
【性质1】二叉树的第 i(i≥1) 层上至多有 2i-1 个结点。
既然是至多,就只需要考虑满二叉树的情况,对于满二叉树而言,当前层的结点数是上一层的两倍,第一层的结点数为 1,所以第 i 的结点数可以通过等比数列公式计算出来,为2i-1 。
2)性质2
【性质2】深度为 h 的二叉树至多有 2h-1结点。
对于任意一个深度为 h 的二叉树,满二叉树的结点数一定是最多的,所以我们可以拿满二叉树进行计算,它的每一层的结点数为1、2、4、8 …、2h-1。
利用等比数列求和公式,得到总的结点数为:1+2+4+8+ …+2h-1=2h-1
3)性质3
【性质3】对于任意一棵二叉树 T,如果叶子结点数为 x0,度为 2 的结点数为 x2,则x0=x2+1
令 x1 代表度 为 1 的结点数,总的结点数为 n,则有:n=x0+x1+x2 ……①
任意一个结点到它孩子结点的连线我们称为这棵树的一条边,对于任意一个非空树而言,边数等于结点数减一,令边数为 e,则有:e=n−1 ……②
对于度为 1 的结点,可以提供 1 条边,如图中的黄色结点;对于度为 2 的结点,可以提供 2 条边,如图中的红色结点。所以边数又可以通过度为 1 和 2 的结点数计算得出:e=x1+2x2 ……③
联立上述①②③三个等式,得到:e=n−1=x0+x1+x2−1=x1+2x2
化简后,得证:x0=x2+1
4)性质4
**【性质4】具有 n 个结点的完全二叉树的深度为 **[log2]n+1
由【性质2】可得,深度为 ℎh 的二叉树至多有2h-1个结点。所以,假设一棵树的深度为 h,它的结点数为 n,则必然满足:n≤2h-1
由于是完全二叉树,它一定比深度为 ℎ−1h−1 的结点数要多,即:2h-1 - 1 < n
将上述两个不等式,稍加整理,得到:2h-1 ≤ n < 2h
然后,对不等式两边取以2为底的对数,得到:h-1≤[log2]n+1 <h
这里,由于 h 一定是整数,所以有:h = [log2]n+1
二、二叉树的储存和创建
1、创建二叉链结构
1 | // 二叉树结构体 |
2、手动构建一颗树
其实构建一棵树的思想还是挺简单的,按照图示创建6个节点,并根据图中的样子将节点顺次链接起来
1 | // 创建一个节点 (有返回值,返回值类型为---结构体指针) |
三、二叉树的遍历 (重点)
1、前序遍历
前序遍历的代码非常简洁,短短几行即可操作,先看代码:
1 | // 前序遍历 根 左 右 |
2、中序遍历
中序遍历的代码和前序遍历一样,看起来都非常简洁:
1 | // 中序遍历 左 根 右 |
3、后序遍历
代码演示:
1 | // 后序 左 右 根 |
4、层次遍历
代码演示:
1 | // 初始化队列 |
[GUET-CTF2019]number_game
下载完拖入010是elf文件,直接拖入64位ida
由sub_4006D6可知flag长度为10
sub_400758是用二叉树把flag传给了v4,
先给本身赋值赋值,
然后把左结点来把奇数位进行赋值
右结点把偶数位进行赋值
sub_400807这是个二叉树的中序遍历
sub_400881是吧v7的值输入到特定的地址中
sub_400917是个检查函数,跟进查看
这是个二维数组的检查函数要求在一个5*5的二维数组中 每行 or 每列 不能有相同的数字
跟进unk_601060并且将它按照5*5的格式展开如下
1 | 14#23 |
这是个数独游戏,把每个数字写出来之后
按照从上到下,从左到右的顺序排列好后
按照二叉树的中序排列逆回去就是flag