数据结构:树(3)【二叉树链式结构实现】【二叉树的前序,中序,后序遍历】【求二叉树全部结点个数】【求二叉树叶子结点个数】【求二叉树的深度】【单值二叉树】

一.二叉树链式结构的实现

二叉树的链式结构的实现相对于顺序结构的实现就没有那么多的讲究了。就是普通的链表,只不过多了一个指向的指针。

具体结构如下:

typedef int BTDataType;
typedef struct BinaryTreeNode
{
 BTDataType data;
 struct BinaryTreeNode* left;//指向左子树
 struct BinaryTreeNode* right;//指向右子树
}BTNode;

我们其实就可以直接简单的手搓一个链式结构的二叉树了。

先把单个的节点申请出来,跟单链表一样一样的:

BTNode* BuyNode(int x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	node->data = x;
	node->left = NULL;
	node->right = NULL;

	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);
	BTNode* node7 = BuyNode(6);


	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	node5->right = node7;

	return node1;
}

逻辑结构就是这样的: 

 这里链式结构比较简单,就不过多的介绍了。主要是后面的遍历,那些题目需要用到递归思想,有些难。

二.二叉树的遍历

1.前序遍历

开始前序遍历之前,我们需要先认识一下什么是前序遍历。前序又叫前根,意思就是在我们遍历二叉树的时候按照  根->左子树->右子树的顺序进行遍历。

依旧是这个图我先来解释一下,如果按照前序遍历的话我们的顺序是怎么样的(这里的N代表的是空树)。我们首先从1开始,这里的1就是根,所以它就是第一个被遍历的,然后到左子树,左子树又可以拆分成根和子树,所以它也是根,算是第二个被遍历的。然后又到左子树,这里是3,它同时被分成根和子树,他自己算是第三个被遍历的,然后再到左子树,这里就是已经是空树了,所以第四个被遍历的是N。到这里就没有左子树了,所以我们要到3的右子树开始遍历,这里也是N,所以这里右子树的N就是第五个被遍历的。

文字太多,换图再看一下:

按照这种遍历的方式,完全遍历完的过程其实就是1,2,3,N,N,N,4,5,N,6,N,N,6,N,N.

当然肯定要用代码来实现一下:

void PreOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");//如果树为NULL,就打印N
		return;
	}
	printf("%d ", root->data);//先把N打印出来

	PreOrder(root->left);//运用递归实现,再次调用一下这个函数
	PreOrder(root->right);
}

再加上主函数,我们可以打印到屏幕上:

int main()
{
	BTNode* root = CreatBinaryTree();
	PreOrder(root);
	printf("\n");
	return 0;
}

这也跟上面我们手动遍历的一样。

2.中序遍历

中序遍历的遍历顺序是左子树->根->右子树。还是上面的二叉树,如果用中序遍历的话会是什么结果呢?

 

 遍历的过程也就是:N,3,N,2,N,1,N,5,N,6,N,4,N,6,N.

当然也要用代码实现一下:

void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

加上主函数后我们可以打印出来我们预测的结果:

int main()
{
	BTNode* root = CreatBinaryTree();
	InOrder(root);
	printf("\n");
	return 0;
}

3.后序遍历 

知道了前面的两种遍历规律,那么这一种当然就是左子树->右子树->根。它的遍历我们也可以很轻松的推测出来:

N,N,3,N,2,N,N,N,6,5,N,N,6,4,1

代码也很简单:

void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

打印出来也就是

这些遍历只是一些简单的递归。 

三.求二叉树全部结点个数

这个题目一看可能就会有思路,比如我在遍历的时候添加一个计数器count。但是要注意一下,我们在遍历的时候用的是递归,不是循环。我们的count如果是局部变量那么这个值不能存储。如果用静态或者全局变量了话也是可以的,我们可以先看一下用静态变量或者全局变量写的样子:

全局:

int size = 0;
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	else
		++size;
	BinaryTreeSize(root->left);
	BinaryTreeSize(root->right);
	return size;
}

静态:

int BinaryTreeSize(BTNode* root)
{
	static int size = 0;
	if (root == NULL)
		return 0;
	else
		++size;
	BinaryTreeSize(root->left);
	BinaryTreeSize(root->right);
	return size;
}

当然指针也可以。 

这样写当然可以,但是有一个问题,如果我多调用这个函数,那么size的值就会递增,不会自己清零,我们就需要自己手动置为0。如果是这样的话,是不是有点麻烦了。

那么我们可以想一想其他的方式来写。既然我们要求全部结点个数,那么递归肯定是离不开的。如果是空树,我们就返回0,如果不是空树,我们就把左子树的结点个数加上右子树的节点个数再加上1(这个1就是根).也就是说,我们就只求左右子树的结点个数,根就是我们加的那个1.注意:根都可以分为左子树和右子树,左子树和右子树同时也是根。

我们就可以写代码了:

int BinaryTreeSize(BTNode* root)
{
	return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

虽然只是一行,但是它的递归的运用的十分巧妙,它会沿着左子树的方向一直到空树,然后再去右子树,如果右子树也是空树,那么它返回的结果就是1.也就代表这是1个结点。

加上主函数就可以很轻易的求出结点个数:

int main()
{
	BTNode* root = CreatBinaryTree();
	printf("TreeSize:%d\n", BinaryTreeSize(root));
	printf("TreeSize:%d\n", BinaryTreeSize(root));
	printf("TreeSize:%d\n", BinaryTreeSize(root));
	return 0;
}

具体左子树的遍历规则就在这里: 

 

四.求二叉树叶子结点个数 

叶子结点其实就是没有子树的节点。就是求二叉树最后一层的结点个数,这个就比较简单了,直接看代码:

int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	if (root->left == NULL && root->right == NULL)//左右子树都是空树,就说明这是个叶子结点
		return 1;
	return BinaryTreeLeafSize(root->left)+
	       BinaryTreeLeafSize(root->right);
}

 加上主函数把结果打印在屏幕上:

int main()
{
	BTNode* root = CreatBinaryTree();
	printf("TreeLeafSize:%d\n", BinaryTreeLeafSize(root));
	return 0;
}

根据上图我们也可以看出,这个二叉树的叶子结点的个数就是3.

 五.求二叉树的高度

既然我们要找二叉树的高度,我们就应该知道我们找的是二叉树的最大高度,我们只需要找到左右子树的的最大高度就行。

int TreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;
	int TreeLeft = TreeHeight(root->left);
	int TreeRight = TreeHeight(root->right);
	return TreeLeft > TreeRight ? TreeLeft + 1 : TreeRight + 1;
}

有一点需要注意的是,我们需要提前把每个树的高度给存起来。如果不存的话,后面用三目表达式求值的时候就会重复的调用这个函数,代码的效率会大大降低。

六.单值二叉树

这是一道OJ题,链接为单值二叉树

其实就是判断一个二叉树里面的值是不是都是一样的,如果一样返回true,不一样返回false.

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */
bool isUnivalTree(struct TreeNode* root) {
    if(root==NULL)
    return true;
    
    if(root->left && root->left->val!=root->val)
    return false;

    if(root->right && root->right->val!=root->val)
    return false;

    return isUnivalTree(root->left)&&
           isUnivalTree(root->right);
}

到这里我介绍了二叉树的遍历,还有二叉树的一些简单题。感谢大家的观看,如有错误还请多多指出。

  • 31
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值