初阶数据结构之二叉树


前言

在前一篇数据结构博文中,我们已经了解了基本的二叉树只是,今天就来详细讲解链式二叉树的遍历,以及各方面的操作。
链式二叉树的学习能很好锻炼我们递归思想以及分治思想。

链式二叉树的定义

怎么定义链式二叉树呢?
我们用一个结构体来表示二叉树的一个节点,其中有一个变量存放二叉树节点的值,再来两个此结构体指针存放它的左右节点的地址。代码如下:

typedef int BTDataType;
typedef struct BinaryTreeNode
{
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

链式二叉树的遍历

前序遍历

前序遍历的口诀就是“根左右”,它的思想就是先访问树的根,再访问数的左孩子,最后访问树的右节点。例如如图这个二叉树:

在这里插入图片描述

遍历的过程:
我们是先访问它的根也就是4,然后再访问它的左孩子2,左孩子2从新作为根后先从自己2开始再访问2的左孩子1,左孩子1作为根,先访问自己1然后访问左孩子为NULL就返回到1,然后再访问1的右孩子为NULL返回到1,依次类推。就能得到遍历的顺序为:

4, 2, 1, 3, 6, 5
代码:

void PrevOrder(BTNode* root)
{
	if(root == NULL)
	{
		printf("NULL");
		return;
	}
	printf("%d ",root->data);
	PrevOrder(root->left);
	PrevOrder(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);
	PostOrder(root->right);
	printf("%d ",root->data);
}

层序遍历

层序遍历就是从第一层的第一个节点然后到第二层一次每层从左到右遍历。
在这里插入图片描述
以这个树为例,他的层序遍历结果就是:
4 2 6 1 3 5
那要怎么实现这个代码呢?
这里我们就要借助一种数据结构队列了。
我们的方法是从根节点开始,先把根节点入队,然后遍历一个节点就把它的左子树入队然后再把他的右子树入队,NULL不入队,直到队列为空为止。代码如下:

void LevelOrder(BTNode* root)
{
	if(root == NULL)
	{
		return;
	}
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while(!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%d ",front->data);
		if(front->left != NULL)
		{
			QueuePush(&q,root->left);
		}
		if(front->right!= NULL)
		{
			QueuePush(&q,root->right);
		}
	}
	QueueDestroy(&q);
}

求二叉树的节点个数

求节点个数直接分治就行了
如果父亲节点不为空加一为空加0;
代码如下:

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

求二叉树的叶子节点个数

首先我们的得知道怎么去判断叶子节点:
左右子树都为空的节点就是叶子节点。所以按照这个性质我们就可以判断了,代码如下:

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

求二叉树第K层的节点个数

思想就是:如果当前节点是第k层就加一,如果不是,就递归到下一层让k - 1.

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

求二叉树的最大深度

将这个问题简化就是把求左右子树较大的深度然后加1.因为根节点本身也是占用一个深度的。
代码如下:

int BinTreeDepth(BTNode* root)
{
	if(root == NULL)
	{
		return 0;
	}
	int leftDepth = BinTreeDepth(root->left);
	int rightDepth = BinTreeDepth(root->right);
	return leftDepth > rightDepth ? leftDepth + 1 : rigthDepth + 1;
}

给一个数让给返回该节点

这个思想就是节点不是然后就从它的左子树和右子树找,直到找到为止。
代码如下:

BTNode* BinTreeNodeFint(BTNode* root, BTNDataType x)
{
	if(root == NULL)
	{
		return NULL;
	}
	if(root->data == x)
	{
		return root;
	}
	BTNode* Left = BinTreeNodeFint(root->left, x);
	if(Left != NULL)
	{
		return Left;
	}
	BTNode* Right = BinTreeNodeFint(root->right, x);
	if(Left != NULL)
	{
		return Right ;
	}
	return NULL;
}

判断该数是不是完全二叉树

完全二叉树的判断就是层序遍历过程中,直到数结束为止中间不能有空节点的产生,就是完全二叉树。
代码如下:

bool BinTreeComplete(BTNode* root)
{
	if(root == NULL)
	{
		return true;
	}
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while(!QueueEmpty(&q))
	{
		BTNode* Front = QueueFront(&q);
		QueuePop(&q);
		if(Front == NULL)
		{
			break;
		}
		else
		{
			QueuePush(&q, front->left);
			QueuePush(&q, front->right);
		}
	}
	while(!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&a);
		QueuePop(&q);
		if(front == NULL)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

二叉树的销毁

二叉树的销毁必须以后序遍历,不然先把根销毁了就找不到左右节点了
代码如下:

void BinTreeDestroy(BTNode* root)
{
	if(root == NULL)
	{
		return;
	}
	BinTreeDestroy(root->left);
	BinTreeDestroy(root->right);
	free(root);
}
  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值