链式二叉树的结构
⽤链表来表⽰⼀棵⼆叉树,即⽤链来指⽰元素的逻辑关系。 通常的⽅法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别⽤来给出该结点左孩⼦和右孩⼦所在的链结点的存储地址。
由上图可见,链式二叉树的每个节点存有三个变量,第一个为节点存的数据,第二个与第三个分别为指向左孩子指针与指向右孩子指针。
创建链式二叉树
由于普通的二叉树并没有插入删除的意义,所以在创建二叉树时只需要手动创建二叉树就行,因为普通二叉树没有条件限制比如:![](https://i-blog.csdnimg.cn/direct/97bfd0d318f14a048108d51b1124f68a.png)
所以在普通二叉树里并没有增删的意义,创建时只需要手动创建即可。
![](https://i-blog.csdnimg.cn/direct/748deb13becd4cf0bdd1b7c1b7a8c0c5.png)
创建二叉树的第一步:先写一个创建单个节点的函数,并将节点里左右指针都初始化为NULL,这样也方便后续链接操作。 第二步:手动申请7个节点,并将将节点之间进行链接。最终形成以下的逻辑图片。![](https://i-blog.csdnimg.cn/direct/439f08bcb68749c5b61ac78b8fd83a49.png)
因为根结点的左⼦树和右⼦树分别⼜是由⼦树结点、⼦树结点的左⼦树、⼦树结点的右⼦树组成的,因此⼆叉树定义是递归式的,
链式二叉树的遍历
前序遍历:
前序遍历(Preorder Traversal 亦称先序遍历):访问根结点的操作发⽣在遍历其左右⼦树之前,及访问顺序为:根,左子树,右子树。就以上图为例子
第一步:先判断此时的根是否为NULL,如果是就输出NULL后返回,第二步:先输出根节点的值,接着在递归根的左子树,第三步:将根的左子树递归完后接着递归右子树。
左子树展开图
以上是根节点的左子树的展开图:一开始会先节点是否为空,接着输出根节点数值,接着递根的左子树,当左子树为空的时候就开始递根的右子树,右子树又开始递 根,左子树,右子树,当左右子树都为NULL时候开始归。
当左子树归完后根节点开始递根节点的右子树。
右子树展开图
最终递归的输出顺序为:
中序遍历:
中序遍历(Inorder Traversal):访问根结点的操作发⽣在遍历其左右⼦树之中(间)及访问顺序为:左子树,根,右子树。
左子树展开图
根据顺序,从根开始并没有先输出值而是先开始递归根的左子树,从上图的左子树的递归展开图中,当节点递到3的左子树(NULL)时,先输出NULL接着开始归,重新归到3节点时输出3,接着递归3的右子树(7),并重复步骤,当1的左子树递归完后就输出1节点的值并执行递归1的右子树。
右子树展开图
当递归1的右子树(4)时,4又作为根开始递归4的左子树与右子树,但遇到NULL节点时就开始进行返回。
最终中序遍历完成会输出:
后序遍历:
后序遍历(Postorder Traversal):访问根结点的操作发⽣在遍历其左右⼦树之后 ,及访问顺序为:左子树,右子树,根。
左子树展开图
右子树展开图
从后续展开图中可以看出,二叉树的后序遍历,是优先访问根节点的左子树与右子树,将根节点的左右子树访问完最后再访问根节点的值,最终输出的顺序为:
二叉树的前中后序其实访问的顺序是一样的,只是在于访问根节点数据的时机不同,产生的结果也不同。
层序遍历:
除了二叉树的先序遍历、中序遍历、后序遍历外,还可以对⼆叉树进⾏层序遍历。设⼆叉树的根结点所在层数为1,层序遍历就是从所在⼆叉树的根结点出发,⾸先访问第⼀层的树根结点,然后从左到右访问第2 上的结点,接着是第三层的结点,以此类推,⾃上⽽下,⾃左⾄右逐层访问树的结点的过程就是层序遍历。
、
从上图可以看出,通过队列的特性,先进先出,先将根节点入队,入队后出队,并在出队的时候同时将根的左右子树入队,当节点为NULL则不入队,当队列重新为空时,则结束循环,并打印出二叉树的层序遍历形式。
链式二叉树的简单算法题
求二叉树所有节点个数:
![](https://i-blog.csdnimg.cn/direct/6a957bb5901749cf948974163c23ec45.png)
左子树展开图
![](https://i-blog.csdnimg.cn/direct/d04e29e307d24393804b2295f92e85d1.png)
右子树展开图
![](https://i-blog.csdnimg.cn/direct/7eae5d9e926a4cdc87a2c62f27a43a75.png)
从上图的左右子树展开图图可以看出,求节点根数需要分别递归计算根的左子树与右子树,当递归到NULL时返回0,当左右子树递归完后需要再加上1(自身),再进行返回。最终计算出该子树节点数量为7。
求二叉树叶子节点的个数:
![](https://i-blog.csdnimg.cn/direct/b8e548e9c5fa49f88ddab1b8e3486da8.png)
递归展开图
![](https://i-blog.csdnimg.cn/direct/2a4affe513e0419a902b0cb2583686e5.png)
![](https://i-blog.csdnimg.cn/direct/acb745749d5d46a59e7f322f4cc4480f.png)
因为叶子节点的特点是左右子树都为NULL,利用这一特性,当递到根的左右子树都为NULL,就返回1表示这个节点是叶子节点。如果左节点或又节点其中一个是NULL并不会返回1,而是继续递归。当递归到叶子节点时直接返回一。最终得得出根的左子树节点数与根的右子树节点数。
求二叉树第k层的节点个数:
![](https://i-blog.csdnimg.cn/direct/f2d9dcf65ce6415eafa119e3f66edb6e.png)
递归展开图![](https://i-blog.csdnimg.cn/direct/50e402cd72124ccab4cef2c2e06f4684.png)
由上图的展开图可以看出,假设k==3,分别递归根的左子树与右子树的第k层,每一次递归的时候都让k-1,当k==1的时候就说明已经到了第k层直接返回1,当递归到第k-1层的时,只需要判断k-1层的左节点与右节点,如果为NULL就返回0,最后将k层返回的数字进行相加并return就得到第k层节点数。
求二叉树的深度:
![](https://i-blog.csdnimg.cn/direct/884a5ae01a884f40813d15144d52f3f0.png)
左子树深度展开图
![](https://i-blog.csdnimg.cn/direct/481858007591408aa7032d69a3bba296.png)
右子树深度展开图
![](https://i-blog.csdnimg.cn/direct/4813c527c18b4a54ad416382b713b366.png)
想要计算二叉树的深度,就要计算根的左子树与右子树的深度,将左子树的深度与右子树的深度进行比较,并返回较大值,如上图,通过递归展开,会得知左子树的深度为3,右子树的深度为2,最终返回左子树的深度+1,所以上图二叉树的深度为4。
二叉树查找值为x的节点:
![](https://i-blog.csdnimg.cn/direct/44cf1b6753354ebc81dc7525fd128675.png)
递归展开图
![](https://i-blog.csdnimg.cn/direct/8f0e624f6538449a8a0d31a077ddc00e.png)
![](https://i-blog.csdnimg.cn/direct/2cefa0132d664b4391cb49e75bd6080b.png)
由上图可见,当需要找x节点时,一开始会先判断根节点是否符合条件,如果符合条件直接返回,如果不符合就先递归根的左子树,当左子树递归完后发现并没有找到值会返回NULL,此时指针为NULL,会递归根的右子树,当发现有节点符合值的时候会直接返回并给指针赋值,防止继续递归。