目录
***我们后面都以这个二叉树为条件来用递归来模拟实现它的前,中,后序排列
二叉树的前,中,后序遍历简介
1.前序遍历:(Preorder Traversal 亦称先序遍历)---访问根节点的操作发生在遍历其左右子树之前。
2.中序遍历:(Inorder Traversal)----访问根节点的操作发生在遍历其左右子树之间。
3.后序遍历:(Postorder Traversal)---访问根节点的操作发生在遍历其左右子树之后。
由于被访问的节点必然是某子树的根,所以N(Node),L(Left subtree) 和 R(Right subtree)又可以解释为根,根的左子树和根的右子树。 NLR,LNR,LRN分别又称为先根遍历,中根遍历,后根遍历。
*****简单点来说,就是要把每一个节点当作是另一个子树的根,同时也可能是另一个子树的左节点或者右节点。(这个比较重要,先要理解这个,才能方便理解后面的递归实现,主要就是分治思想)
前,中,后序排列的训练
首先,我们用 & 来表示空树。
前序遍历:1 2 3 & & & 4 5 & & 6 & &
中序遍历:& 3 & 2 & 1 & 5 & 4 & 6 &
后序遍历:& & 3 & 2 & & 5 & & 6 4 1
***我们后面都以这个二叉树为条件来用递归来模拟实现它的前,中,后序排列
模拟创建一个二叉树(具体代码)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <time.h>
typedef int BTDataType;//二叉树存放数据的类型
typedef struct BinaryTreeNode {//二叉树结构的定义
struct BinaryTreeNode* left;//二叉树的左节点
struct BinaryTreeNode* right;//二叉树的右节点
BTDataType data;//二叉树的数据域
}BTNode;//将定义的二叉树结构名字重新简化定义
BTNode* BuyNode(BTDataType x) {//创建并且同时初始化二叉树的一个节点
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL) {
printf("malloc fail!");
exit(-1);
}
node->left = NULL;
node->right = NULL;
node->data = x;
return node;
}
BTNode* CreatBinaryTree() {//模拟创建一个二叉树
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;
return node1;
}
void PreOrder(BTNode* root) {//前序遍历
/*
如果想实现中序遍历和后续遍历的话,只需要将 printf("%d ", root->data);
这句放在
PreOrder(root->left);//递归遍历打印左子树的数据
PreOrder(root->right);//递归遍历打印右子树的数据
这两句话的中间和后面位置就可以了。
*/
if (root == NULL) {
printf("& ");
return;//这里必须要加上这个,不然无法返回并实现递归了。
//意思就相当于在其递归到空节点时,需要将其地址返回到上一级调用时的函数中去
}
printf("%d ", root->data);//打印根节点的数据
PreOrder(root->left);//递归遍历打印左子树的数据
PreOrder(root->right);//递归遍历打印右子树的数据
}
int main() {
BTNode* root = CreatBinaryTree();//模拟创建一个二叉树
PreOrder(root);//前序遍历
printf("\n");
return 0;
}
前序遍历的代码实现图解(后序和中序都差不多)
相信跟着图一起走一遍递归会对其理解更加方便了吧。由此我们可以知道其前序遍历如果使用递归来实现的话,大概递归的次数是24次。所以这简单的几句话,其实在计算机中可能需要占用的空间还是比较多的。其中序和后序递归和以上差别不大。
额外补充:
1.利用递归求二叉树的节点数量
2.求二叉树的叶子节点数量
3.求二叉树的第K层上有几个节点
4.求查找一个数据是否存在二叉树中
其数据来源的二叉树都统一用上面已经创建好的那个二叉树。也就是下面这个:
利用递归求二叉树的节点数量
int count = 0;//必须使用全局变量来统计其节点的数量,如果使用局部变量的话
//那么count的值容易不够准确,因为你每次递归调用的时候都会对其进行加1
//那么数据可能一般会变得比较大
void TreeSize(BTNode* root) {//求二叉树的节点数量方法一
if (root == NULL) {
return;
}
count++;
TreeSize(root->left);
TreeSize(root->right);
}
int TreeSize2(BTNode* root) {//求二叉树的节点数量方法二
return root == NULL ? 0 : TreeSize2(root->left) + TreeSize2(root->right) +1;
}
int main() {
BTNode* root = CreatBinaryTree();//模拟创建一个二叉树
//CreatBinaryTree的实现在上面已经实现好了,这里为了简化代码数量就不复制过来了
//下面另外几个补充的算法也是一样了,就不过多赘述了。
count = 0;//先提前将其count的值初始化一下,防止其前面改变了其count的值导致最终统计不准确
TreeSize(root);//求二叉树的节点数量方法一
printf("这个二叉树的节点数量为:%d \n", count);
int nums = TreeSize2(root);//求二叉树的节点数量方法二
printf("这个二叉树的节点数量为:%d \n", count);
return 0;
}
解析:总体思想就是将左右子树上不为空的节点数量全部都统计一下求和就ok。
求二叉树的叶子节点数量
int count;//用来统计叶子节点的数量
int TreeLeafSize(BTNode* root) {
/*
* //方法一
* // 需要利用全局变量来实现统计
*
if (root == NULL) {
return;
}
if (root->right == NULL && root->left == NULL) {
count++;
return;
}
TreeLeafSize(root->left);
TreeLeafSize(root->right);
*/
//方法二,不用使用全局变量
if (root == NULL) {//当根节点为空的时候直接返回0
return 0;
}
if (root->right == NULL && root->left == NULL)//当根节点为叶子节点的时候,返回 1
return 1;
return TreeLeafSize(root->left) + TreeLeafSize(root->right);
//递归遍历左右子树来分别统计左右子树的叶子节点数量再将其相加得出最后的结果。
}
解析:总体思想就是想求某一颗二叉树的叶子节点的数量就是得要求出它的左子树的左右节点都为空的节点数量加上它的右子树的左右节点都为空的节点数量之和。
求二叉树的第K层上有几个节点
int TreeKLevel(BTNode* root,int k){//统计二叉树的第K层上的节点数量
assert(k >= 1);//默认层数的计算是从第1层开始的。
if (root == NULL) {
return 0;
}
if (k == 1) {//第一层只有一个节点,只返回1就行。
return 1;
}
return TreeKLevel(root->left, k-1) + TreeKLevel(root->right, k-1);
}
int main(){
BTNode* root = CreatBinaryTree();//模拟创建一个二叉树
int k = 3;//表示层数,第k层。
int Level = TreeKLevel(root,k);
printf("这个二叉树第%d层的节点数量为:% d \n",k,Level);
return 0;
}
解析:总体思想就是如果想求一颗二叉树的第K层上的节点的数量,那么你需要求出
这颗二叉树的左子树的第K-1层上的节点数量加上右子树的第K-1层上的节点数量之和。
求查找一个数据是否存在二叉树中
BTNode* TreeFind(BTNode* root, BTDataType x){
if (root == NULL) {
return NULL;
}
if (root->data == x){
return root;
}
/*
这里不能这样写:
return TreeFind(root->left,x);
return TreeFind(root->right,x);
因为如果这样写的话,那么最终只会查找左子树里的数据,而没有查找右子树的数据
这时,如果数据刚好在右子树中,其需要查找的数据虽然在二叉树中,但任然会返回空节点。
*/
BTNode* Node1 = TreeFind(root->left, x);//因为其返回值为地址,所以我们可以先
//一个地址指针来接收其左子树的返回值并判断,如果找到了就直接返回。
//没找到就继续查找右子树
if (Node1!= NULL){
return Node1;
}
BTNode* Node2 = TreeFind(root->right, x);
if (Node2!= NULL){
return Node2;
}
return NULL;
}
int main(){
BTNode* root = CreatBinaryTree();//模拟创建一个二叉树
BTDataType x = 4;//需要在二叉树中查找的数据
BTNode* XAdress = TreeFind(root, x);
printf("这个节点的地址为:%p \n",XAdress);
return 0;
}
解析:总体思想就是遍历左右子树,看是否有节点中保存的数据与x相同。
但这里有个需要注意的点,就是需要注意不要多次反复查找数据,可以先用个地址变量保存下子树的地址判断,这样减少了不必要的递归.