目录
一.前言
二. 二叉树链式结构的实现
2.1 前置说明
2.2 二叉树的遍历
2.2.1 前序、中序以及后序遍历
我们先来按照这个形状来构建节点并链接起来
//搭建二叉树结构
typedef struct BinaryTreeNode
{
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
int val;
}BTNode;
//创造节点
BTNode* BuyNode(int x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL)
{
perror("node faild");
exit(-1);
}
node->val = x;
node->left = NULL;
node->right = NULL;
return node;
}
int main()
{
//手动构建节点
BTNode* node1 = BuyNode(1);
BTNode* node2 = BuyNode(2);
BTNode* node3 = BuyNode(3);
BTNode* node4 = BuyNode(4);
BTNode* node5 = BuyNode(5);
BTNode* node6 = BuyNode(6);
//开始链接
node1->left = node2;
node1->right = node4;
node2->left = node3;
node4->left = node5;
node4->right = node6;
}
前序遍历:
涉及到子问题嵌套子问题,是深入了解二叉树遍历分治(递归)的开始~
//前序遍历
void PrevOrder(BTNode* root)
{
//当二叉树无根时
if (root == NULL)
{
printf("NULL ");
return;
}
//当二叉树有根时
printf("%d ", root->val);
PrevOrder(root->left);
PrevOrder(root->right);
}
通过递归我们可以很轻易地写出前序遍历,而接下来我们就来分析二叉树递归遍历的精妙之处~
递归时每次函数都会建立一个栈帧。
前序遍历递归展开图:
一开始根是1,打印出来后走1的左子树2,2作为根打印后继续走2的左子树3,3作为根打印后走3的左子树,但此时3的左子树为空所以递进结束开始回归,回到根为3的时候继续走3的右子树,然而3的右子树也是空所以不再递进选择回归。而当我们遍历完3的左右子树后就会回到2作为根的那一层栈帧,因为2的左子树3已经被我们遍历完了,所以我们开始遍历2的右子树,发现2的右子树也是空所以不再递进选择回归,把2的左右子树也遍历完后我们再回归至根为1的那一层,因为2作为1的左子树已经遍历完了,所以我们接着对1的右子树4选择递进........就这样以此类推,当所以子树都递进后最终都会向根回归,达到前序遍历的作用。
所以该递归最核心的部分就是先打印根结点,然后再去找根结点的左右子树,通过这个规律我们可以轻易的模仿出中序遍历与后序遍历。
中序遍历递归图:
//中序遍历
void InOrder(BTNode* root)
{
//当二叉树无根时
if (root == NULL)
{
printf("NULL ");
return;
}
//当二叉树有根时
PrevOrder(root->left);
printf("%d ", root->val);
PrevOrder(root->right);
}
后序遍历:
//后序遍历
void PostOrder(BTNode* root)
{
//当二叉树无根时
if (root == NULL)
{
printf("NULL ");
return;
}
//当二叉树有根时
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->val);
}
2.3节点个数
2.4叶子节点个数
我们可以采用遍历左子树+右子树的形式(从左子树开始不断向下延申,到底后放返回去找右子树),递归返回左右子树相加的节点个数。方向是对的,但是注意我们写的是叶子节点的个数,不是上面代码所求空节点的个数,所以我们需要注意以下三点:
- 当该节点为空时返回0
- 当该节点是叶子时返回1
- 既不是空节点也不是叶子时返回左右子树相加
核心就在于辨别节点,当不是特殊节点时就要向下继续寻找它的左右子树的节点是否为特殊节点,一直持续到找到特殊节点(空或叶子节点)。
2.5第K层的节点个数
所以最后对于节点2而言,它所属的左右子树节点共返回了1个节点。
//第K层的节点数
int TreeKLevel(BTNode* root, int k)
{
assert(k > 0);
//该节点为空时
if (root == NULL)
{
return 0;
}
//到达目标层时
if (k == 1)
{
return 1;
}
return TreeKLevel(root->left, k - 1) + TreeKLevel(root->right, k - 1);
}
不同于与之前的判断左右子树为空后才返回1,由于是要求层节点所以我们要添加层数的限制,当到达所求层数时需要返回1,而所求层数的标准就是前面例子所假设的(k=3,对位于第三层的3而言它就是第一层)。到不了所求层数那就判定为空节点,直接返回0即可。
故基本思路就是一开始K为3,即第一层,那么我们肯定得优先判断第一层本身是否存在,不存在直接返回0,如果存在但又不满足我们所求层数则继续遍历它的左右子树,就这样持续下去直到我们能够成功到达目标楼层(K为1),不能到达肯定是因为前面的判断生效即该子树为空节点。所以最为核心的地方就是我们要弄清楚k楼层之间的相对关系。
2.6 二叉树查找值为x的节点
很多人会犯上述的代码错误,我们用递归展开图来解释问题。