二叉树的遍历

二叉树的遍历有何意义?

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。
在这里插入图片描述

二叉树的递归结构遍历

递归就是在运行的过程中调用自己,以实现层次数据结构的查询和访问;结构指的是二叉树是树的一种特殊结构;遍历是指沿着某条搜索路线,依次对树中每个节点做一次且仅做一次访问。
在这里插入图片描述

写一个结构体

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
//给int起一个BTDateType的别称,及BTDataType就是int
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;//结构体的数据
	struct BinaryTreeNode* left;//结构体成员变量
	struct BinaryTreeNode* right;//结构体成员变量
}BTNode;//二叉树的别名,代表这个结构体
// 二叉树前序遍历

前序

前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。简单来记忆:根左右

二叉树前序遍历:
void PreOrder(BTNode* root)

void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);//访问根节点
	PreOrder(root->left);//d=递归左子树
	PreOrder(root->right);//递归右子树
}

在这里插入图片描述

前序我们把二叉树分成左子树、右子树3个部分,每一个节点都可以往下分成这3个部分,遇到空树就停下来,先访问根节点1,节点1访问它的左子树2,节点2再访问根->它自己,依次访问到左子树3,节点3的左子树访问到7,节点7访问到左右2个空,节点3再访问它的右子树节点8,节点8再访问到2个空,这样节点1的左子树访问完了,访问它的右子树节点节点4,节点4的左右子树是节点5,节点5访问到节点2个空停下来,节点4访问右子树节点6,节点6走到2个空,访问结束。
因此前序遍历打印的结果是:
1 2 3 7 NULL NULL 8 NULL NULL NULL 4 5 NULL NULL 6 NULL NULL

中序

中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。简单来记忆:左根右

二叉树中序遍历:
void InOrder(BTNode* root)

void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);//访问根节点
	InOrder(root->right);
}

在这里插入图片描述

节点1走到节点2,节点2走到节点3,节点3走到节点7,节点7走到空,然后回退访问根->自己7,再走到节点7的右子树是个空,然后回退到节点3,节点3的左子树访问完了就该访问它自己了,节点3能不能走到节点8,不能,节点3先走到它的左子树为空再走到节点8,节点8的左子树为空,节点3就走完了回退到节点2,节点2的左子树是空回退到它的根节点1,节点1走到节点4不能直接访问节点5,走到5的左子树访问空,再访问节点5,再访问它的右子树空,节点5回退到4,访问节点5的右树也就是节点4的左子树为空,节点4就可以访问了,能访问6吗不能,先访问4的左子树的左子树空,再去访问节点6,最后访问节点6的右子树。
因此中序遍历打印的结果是:NULL 7 NULL 3 NULL 8 NULL 2 NULL 1NULL 5 NULL 4NUU 6 NULL

后序

后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。简单来记忆:左右根

二叉树后序遍历:
void PostOrder(BTNode* root)

void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->left);//d=递归左子树
	PostOrder(root->right);//递归右子树
	printf("%d ", root->data);//访问根节点
}

在这里插入图片描述

先访问节点7的左右2个空,再访问根->它自己7,节点7作为节点3的左,不能先访问节点3,先访问节点3的右8,节点8又作为根又不能直接访问,先访问节点8的左右2个空才能访问到节点8,节点3的左右访问完了才回退访问到节点3,节点3作为节点2的左又不能直接访问到节点2,得先访问节点2的右是空,然后访问到节点2,节点2作为节点1的左不能直接访问到节点1,先访问节点1的右为2个空,再访问到根,这个根是节点5了而不是节点1,因为访问的是节点5的左右,节点4的左是节点5访问完了再访问它的右节点6不能直接访问,访问节点6的左右2个空再访问节点6,节点4的左右都访问完了才访问它自己节点4,节点4又作为节点1的右树,此时访问到节点1就结束。
因此后序遍历打印的结果是:
NULL NULL 7 NULL NULL 8 3 NULL2 NULL NULL 5 NULL NULL 6 4 1
最终的结果是:
前序遍历的结果是:1 2 3 7 8 4 5 6
中序遍历的结果是:7 3 8 2 1 5 4 6
后续遍历的结果是:7 8 3 2 5 6 4 1

构建一棵树

BTNode* CreateTree()
{
	BTNode* n1 = (BTNode*)malloc(sizeof(BTNode));
	assert(n1);
	BTNode* n2 = (BTNode*)malloc(sizeof(BTNode));
	assert(n2);
	BTNode* n3 = (BTNode*)malloc(sizeof(BTNode));
	assert(n3);
	BTNode* n4 = (BTNode*)malloc(sizeof(BTNode));
	assert(n4);
	BTNode* n5 = (BTNode*)malloc(sizeof(BTNode));
	assert(n5);
	BTNode* n6 = (BTNode*)malloc(sizeof(BTNode));
	assert(n6);
	BTNode* n7 = (BTNode*)malloc(sizeof(BTNode));
	assert(n7);
	BTNode* n8 = (BTNode*)malloc(sizeof(BTNode));
	assert(n8);
	n1->data = 1;
	n2->data = 2;
	n3->data = 3;
	n4->data = 4;
	n5->data = 5;
	n6->data = 6;
	n7->data = 7;
	n8->data = 8;

	n1->left = n2;
	n1->right = n4;
	n2->left = n3;
	n2->right = NULL;
	n4->left = n5;
	n4->right = n6;

	n3->left = NULL;
	n3->right = NULL;
	n5->left = NULL;
	n5->right = NULL;
	n6->left = NULL;
	n6->right = NULL;

	n3->left = n7;
	n7->left = NULL;
	n7->right = NULL;

	n3->right = n8;
	n8->left = NULL;
	n8->right = NULL;





	return n1;//返回根
}

计算树节点总的个数

int TreeSize(BTNode* root)
{
	return root == NULL ? 0 :
		TreeSize(root->left) + TreeSize(root->right) + 1;
	//如果是空我就返回0,不是我就返回左子树节点的个数加右子树节点的个数加自己
}

二叉树节点总的个数=左子树节点的个数+右子树节点的个数+自己:左子树
节点1走到节点2,节点2走到节点3,节点3走到节点7和节点8,节点3的左边和右边都是1返回2给自己,节点2的左边是节点3右边是0,返回2+自己=3给节点2,节点2的左边是节点3,右边是0,返回3+自己=4给节点1,左子树的节点个数为4;右子树节点1走到节点4,节点4走到节点5和节点6返回2给节点4,节点4返回3给节点1,右子树的节点个数就是3,所以总的节点个数就是4+3+1=8
在这里插入图片描述

计算叶子节点的个数

int TreeLeafSzie(BTNode* root)
{
	if (root == NULL)
		return 0;
	if (root->left == NULL && root->right == NULL)
		return 1;
	return TreeLeafSzie(root->left) + TreeLeafSzie(root->right);
}

在这里插入图片描述

先计算左子树 ,节点1走到节点2,节点2走到节点3,节点3走到节点7和节点8,各返回1,,节点3拿到结果左右各返回一个1,一共返回2,节点3回退到节点2再回退到节点1,告诉节点1我的左子树有2个叶子节点,注意:return 1不是直接返回给最外面而是返回给上一层(也就是它调用的地方)然后计算右子树,节点1走到节点4,节点4走到节点5返回1,走到节点6又返回1,一共返回2,节点4再回退给节点1,告诉节点1我的右子树有2个叶子节点。

计算树的高度

int TreeHeight(BTNode* root)
{
	//父亲的高度=左右子树大的那个+1
	if (root == NULL)
		return 0;
	int lh = TreeHeight(root->left);
	int rh = TreeHeight(root->right);
	return lh > rh ? lh + 1 : rh + 1;
	
}

在这里插入图片描述

二叉树的高度=左右子树大的那颗树+1:这个问题我们还可以用分治法,把它看出子树问题,节点1走到节点2,节点2走到节点3,节点3走到节点7和节点8各返回都是1,所以还是返回1给节点3,节点3返回2给节点2,节点2再返回3给节点1,节点1最后返回4我们就求出了左子树的高度,在求右子树的高度:节点1走到节点4,节点4走到节点5和节点6各返回1,1和1一样大还是返回1给节点4,节点4返回2给节点1,节点1最后返回3,因为左子树的高度4大于右子树的高度,所以树的高度就是4,这里最后返回的时候加了1,所以就不加1了,有的小伙伴可能不清楚!

代码的实现:

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
//给int起一个BTDateType的别称,及BTDataType就是int
typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;//结构体的数据
	struct BinaryTreeNode* left;//结构体成员变量
	struct BinaryTreeNode* right;//结构体成员变量
}BTNode;//二叉树的别名,代表这个结构体
// 二叉树前序遍历
void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%d ", root->data);//访问根节点
	PreOrder(root->left);//d=递归左子树
	PreOrder(root->right);//递归右子树
}
// 二叉树中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);//访问根节点
	InOrder(root->right);
}
// 二叉树后序遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->left);//d=递归左子树
	PostOrder(root->right);//递归右子树
	printf("%d ", root->data);//访问根节点
}
//遍历计数
// int count = 0;
// //static局部定义的变量只有在第一次调用才会被初始化,因此放在全局变量
//void TreeSize(BTNode* root)
//{
//	if (root == NULL)
//		return;
//	++count;
//	TreeSize(root->left);
//	TreeSize(root->right);
//	return ;
//}
//子问题思路解决
//计算节树节点总的个数
int TreeSize(BTNode* root)
{
	return root == NULL ? 0 :
		TreeSize(root->left) + TreeSize(root->right) + 1;
	//如果是空我就返回0,不是我就返回左子树节点的个数加右子树节点的个数加自己
}
//计算叶子节点的个数
int TreeLeafSzie(BTNode* root)
{
	if (root == NULL)
		return 0;
	if (root->left == NULL && root->right == NULL)
		return 1;
	return TreeLeafSzie(root->left) + TreeLeafSzie(root->right);
}
//计算树的高度
int TreeHeight(BTNode* root)
{
	//父亲的高度=左右子树大的那个+1
	if (root == NULL)
		return 0;
	int lh = TreeHeight(root->left);
	int rh = TreeHeight(root->right);
	return lh > rh ? lh + 1 : rh + 1;
	
}
//构建一棵树
BTNode* CreateTree()
{
	BTNode* n1 = (BTNode*)malloc(sizeof(BTNode));
	assert(n1);
	BTNode* n2 = (BTNode*)malloc(sizeof(BTNode));
	assert(n2);
	BTNode* n3 = (BTNode*)malloc(sizeof(BTNode));
	assert(n3);
	BTNode* n4 = (BTNode*)malloc(sizeof(BTNode));
	assert(n4);
	BTNode* n5 = (BTNode*)malloc(sizeof(BTNode));
	assert(n5);
	BTNode* n6 = (BTNode*)malloc(sizeof(BTNode));
	assert(n6);
	BTNode* n7 = (BTNode*)malloc(sizeof(BTNode));
	assert(n7);
	BTNode* n8 = (BTNode*)malloc(sizeof(BTNode));
	assert(n8);
	n1->data = 1;
	n2->data = 2;
	n3->data = 3;
	n4->data = 4;
	n5->data = 5;
	n6->data = 6;
	n7->data = 7;
	n8->data = 8;

	n1->left = n2;
	n1->right = n4;
	n2->left = n3;
	n2->right = NULL;
	n4->left = n5;
	n4->right = n6;

	n3->left = NULL;
	n3->right = NULL;
	n5->left = NULL;
	n5->right = NULL;
	n6->left = NULL;
	n6->right = NULL;

	n3->left = n7;
	n7->left = NULL;
	n7->right = NULL;

	n3->right = n8;
	n8->left = NULL;
	n8->right = NULL;





	return n1;//返回根
}
int main()
{
	BTNode* root = CreateTree();//根节点
	PreOrder(root);
	printf("\n");

	InOrder(root);
	printf("\n");

	PostOrder(root);
	printf("\n");

	/*count = 0;
	TreeSize(root);
	printf("Tree size:%d\n", count);

	count = 0;
	TreeSize(root);
	printf("Tree size:%d\n", count);*/

	printf("Tree size:%d\n", TreeSize(root));
	printf("Tree size:%d\n", TreeSize(root));
	printf("Tree size:%d\n", TreeSize(root));

	printf("Tree leaf size:%d\n", TreeLeafSzie(root));
	printf("Tree Height size:%d\n", TreeHeight(root));

	return 0;

}

在这里插入图片描述
运行结果可以看出除了打印前序、中序和后序,我们构建的二叉树的高度是4,树叶子节点的个数是4,数叶子结点总的个数是8!
在这里插入图片描述

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

神之子-小佳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值