【数据结构初阶】----二叉树基本功能的实现

二叉树基本功能的实现基本都是递归的思路,因此虽然代码简单,但逻辑相对抽象。

1.二叉树结构的定义

二叉树的节点包括一个值,和指向左右孩子的两个同类型指针

typedef char BTDataType;
//二叉树节点的结构
typedef struct BinaryTreeNode
{
	BTDataType val;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

2.二叉树的前、中、后序遍历

前序遍历的顺序是:根、左子树、右子树
中序遍历的顺序是:左子树、根、右子树
后续遍历的顺序时:左子树、右子树、根
而左子树和右子树遍历的时候也得按相应的前中后序的遍历顺序走。
下面我画个图大家可以感受一下二叉树的前中后序遍历:
在这里插入图片描述

2.1二叉树的前序遍历

参考代码如下:

//二叉树的前序遍历
void BinaryTreePrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	printf("%c ", root->val);
	BinaryTreePrevOrder(root->left);
	BinaryTreePrevOrder(root->right);
}

以我们上面那颗二叉树为例子,我们来看一下这段代码是如何运行的,建议大家像我这样,第一次学的时候不妨先动手画画,后续知道他是怎么运行的就可以在脑子中走逻辑想简图了。
在这里插入图片描述

2.2二叉树的中序遍历

中序遍历无非是先调用左子树再打印根,然后调用右子树,逻辑上与前序一样,不过顺序不同。
参考代码如下:

//二叉树的中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	BinaryTreeInOrder(root->left);
	printf("%c ", root->val);
	BinaryTreeInOrder(root->right);
}

2.3二叉树的后序遍历

后序遍历也是顺序调换一下,先左右,最后根。
参考代码如下:

//二叉树的后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	BinaryTreePostOrder(root->left);
	BinaryTreePostOrder(root->right);
	printf("%c ", root->val);
}

3.二叉树的高度

二叉树的高度就是二叉树的层数
一颗二叉树的层数 = 根节点所在层 + 左右子树层数中较大的个层数,也就是1+max(lefLlevel,rightLevel);
参考代码如下:

int BinaryTreeHigh(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}

	int leftLevel = BinaryTreeHigh(root->left);
	int rightLevel = BinaryTreeHigh(root->right);

	return leftLevel > rightLevel ? 1 + leftLevel : 1 + rightLevel;

	//或者
	//return fmax(BinaryTreeLevelOrder(root->left), BinaryTreeLevelOrder(root->right)) + 1;
}

4.二叉树的层序遍历

二叉树的层序遍历我们是这样个思路:我们先创建一个空队列,然后我们将根节点放入空队列中,此时根节点在队头,我们再利用取队头元素的函数QueueFront()保存队头元素,为得是在我们pop掉队头元素后能找到根节点,然后将根节点的左右孩子一并导入队头,当然左孩子要先导进队列,注意如果孩子为空的不入队列。
按照这个思路,每pop掉一个队头的根,都将这个根的左右孩子从队尾入队列,直到队列变成空队列,也就意味着我们将二叉树中的所有非空元素全都遍历了一遍。
(注意:我们这边的队列及其基本功能的实现需要自己实现)
队列博客链接: 栈和队列基本功能实现
参考代码如下:

//二叉树的层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{https://blog.csdn.net/Wangshijie1015/article/details/146053878?spm=1001.2014.3001.5501
	Queue q;
	//初始化队列
	QueueInit(&q);

	//先将根放入队列
	if (root)
		QueuePush(&q, root);

	//不为空的话就继续遍历
	while (!QueueEmpty(&q))
	{
		//队头元素就是我们要pop的元素,先保存下来
		//为的是后续能找到该节点,然后将左右孩子入堆
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%c ", front->val);
		//左孩子为空不入队
		if (front->left)
			QueuePush(&q, front->left);
		//右孩子为空不入队	
		if (front->right)
			QueuePush(&q, front->right);
	}

	//销毁队列
	QueueDestory(&q);
}

5.判断二叉树是否是完全二叉树

思路:与层序遍历不同,我们这此pop掉队头元素后,不管它的左右孩子是否是NULL,我们都将他们从队尾入队列,最后结束的条件不再是判空,而是判断队头元素是否是NULL,如果是,我们接下来就判断队列中是否存在非空元素,如果存在,就不是完全二叉树,反之则是完全二叉树。
参考案例:
在这里插入图片描述
参考代码如下:

//判断二叉树是否是完全二叉树
int BinarTreeComplete(BTNode* root)
{
	//将二叉树中的节点入队列,空也入
	Queue q;
	QueueInit(&q);
	QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		//如果是完全二叉树,根是空也代表树走到最后一个节点了
		if (front == NULL)
		{
			break;
		}
		QueuePop(&q);
		//空节点也入队列
		QueuePush(&q, front->left);
		QueuePush(&q, front->right);
	}
	//如果是完全二叉树,此时队列中剩下的应该都是NULL才对,如果有非空元素,说明不是完全二叉树
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		if (front != NULL)
		{
			QueueDestory(&q);
			return 0;
		}
		QueuePop(&q);
	}
	QueueDestory(&q);
	return 1;
}

6.前序遍历的数组来构建二叉树

前面是我们已经得到了一颗二叉树,然后我们利用递归来求它的前、中、后序遍历结果,现在有一个数组,数组中存放的是我们二叉树前序遍历的结果,我们需要利用这个结果反推构建一颗二叉树。
思路:
首先我们给的这个数族中#表示这个位置是空
因为是前序遍历的数组,所以数组中的顺序也是根、左子树、右子树,所以我们先把第一个元素放入根的值中:root->val = a[(*pi)++];然后将下标++,使数组能找到下一个元素。
然后第二个值就是我们左子树的根节点的值,我们递归调用该函数,然后该函数走到root->val = a[(*pi)++];的时候就把这个值赋予左子树的根节点了,如果数组当前元素是#,代表这个节点应该是空,所以我们会在if那返回空。
参考代码如下:

//通过前序遍历的数组“ABD##E#H##CF##G##”构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
	if (a[*pi] == '#')
	{
		(*pi)++;
		return NULL;
	}

	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	root->val = a[(*pi)++];
	root->left = BinaryTreeCreate(a, pi);
	root->right = BinaryTreeCreate(a, pi);
	return root;
}

7.二叉树节点个数

二叉树节点个数=根节点+左子树节点个数+右子树节点个数
参考代码如下:

//二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
   //空树即意味着0个节点
	if (root == NULL)
	{
		return 0;
	}

	return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}

8.二叉树叶子节点个数

二叉树叶子节点个数=左子树叶子节点个数+右子树叶子节点个数
参考代码如下:

//二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	//空树即意味着0个叶子节点
	if (root == NULL)
	{
		return 0;
	}
	//如果这个节点没有左右孩子,那它就是叶子节点
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}

	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

9.二叉树第k层节点个数

二叉树第k层节点个数=左子树第k-1层节点个数+右子树第k-1层节点个数。
参考代码如下:

//二叉树第k层节点个数
int BinaryTreeLevelKOrder(BTNode* root, int k)
{
	//求一颗树第k层节点的个数就是求其左右子树第k-1层的节点个数
	//空树意味着0个节点
	if (root == NULL)
	{
		return 0;
	}
	//不是空树,求得又是这棵树的第1层,则这个根就是我们要求的节点
	if (k == 1)
	{
		return 1;
	}
	//左子树k-1层节点个数 + 右子树节点个数就是二叉树第k层节点个数
	return BinaryTreeLevelKOrder(root->left, k - 1) + BinaryTreeLevelKOrder(root->right, k - 1);
}

10.二叉树查找值为x的节点

我们先对比根是不是我们要找的节点,不是的话我们往左子树找,左子树找到的话返回该节点,左子树没有则去右子树找,右子树没有则代表没有,返回NULL。
参考代码如下:

//二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
	//子树为空了,返回根
	if (root == NULL)
	{
		return NULL;
	}
	if (root->val == x)
	{
		return root;
	}
	BTNode* node1 = BinaryTreeFind(root->left, x);
	//node1不是空就代表找到了
	if (node1)
		return node1;

	BTNode* node2 = BinaryTreeFind(root->right, x);
	//node2不是空就代表找到了
	if (node2)
		return node2;

    //走到这步说明前面都没找到
	return NULL;
}

11.二叉树销毁

二叉树销毁我们也要按照顺序进行销毁,我们销毁的顺序是左右孩子、根,我们按照这个顺序是应为我们先销毁根,我们还能找到左右孩子吗?我们无法通过孩子找到根,但我们可以通过根找到左右孩子。
参考代码如下:

//二叉树销毁
void BinaryTreeDestory(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}
	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值