树(二叉树)

树应该是数据结构中最难的吧, 复习到这块做二叉树的练习题, 不是很好做, 大部分都是递归题, 树的考点也很多。

首先要区分树和森林, 其次树又分为二叉树和非二叉树。
我们主要以二叉树为主复习, 因为往年考点较高是二叉树。

一、树的存储分为两种,一种是线性存储, 一种是非线性存储。
1、线性存储我们在heap这章提及过, 它的实现很简单, 需要注意的是父结点、孩子结点、最后一个非叶子结点之间运算关系。 这块不作为重点讨论。
2、非线性存储, 也就是链式存储, 每个结点中间至少包含(值、左孩子指针、右孩子指针等)这些信息, 在逻辑上,左孩子指针指向左孩子结点地址, 有孩子指针指向右孩子结点。 这样一次类推, 形成一种逻辑上有树规律,但物理上没有树规律的结构。
在这里插入图片描述
二、如何构建一个树呢
如何构建一颗树,这是一个刚开始接触二叉树就必须要考虑的问题。
我这块不卖关子。 我第一次构建是看别人写的模板写的。
一般来说,构建一棵树, 我们会把构建的内容存放在一个数组中。 然后通过递归来构建。 听起来可能难, 其实看代码品一会就懂得了。我直接放代码。

这是给我们数组:
char a[] = { ‘1’, ‘5’, ‘#’, ‘#’, ‘3’, ‘#’, ‘#’ };
调用方式:
int pi = 0;
BTNodeInit(a, &pi);

在这里插入图片描述
最后BTNodeInit接口的返回值就是构建树的头结点head。

三、树的遍历方式
分别是: 先序遍历、中序遍历、后序遍历、层序遍历。
在这里插入图片描述
先序遍历, 口诀就是根左右; 中序遍历左根右; 后序遍历左右根。
层序遍历就是每一层一层的遍历。
一般来说对于先序、中序、后续序遍历的设计有两种方式。
1、递归实现。
2、非递归实现(需要借助栈)

层序遍历采用队列去实现。
(这些我在面试题总结会写实现过程)

面试当中一般常考给我们先序遍历,中序遍历,后序遍历当中的两个序列,
让我们判断给哪两个序列可以画出二叉树的问题?
如果想要通过两个序列去画二叉树, 这两个序列中一定要包含中序遍历, 如果只有先序、后序遍历画不出二叉树的。

四、二叉树的分类
1、满二叉树
在这里插入图片描述
其实我们发现树是分一层一层的, 而每一层最多的结点个数为2^(n-1), n为层数。
在这里如果每一层的结点个数都是最大个数2^(n-1), 则这个二叉树就是满二叉树。

2、完全二叉树
在这里插入图片描述
在满二叉树的基础上做一些区分, 就是在最后一层, 最后一层可以不用满足2^(n-1)个数, 但是有个条件,最后一层结点从左右往数都是连续存放的,不可能说两个之间有一个空结点。

下面这个就不满足条件(最后一层1和7之间有空结点), 不是完全二叉树。
在这里插入图片描述

二叉树还有一个有意思的,那就是排序二叉树。
所谓排序二叉树是按一定规律存放的树。
如果每个结点的值大于左孩子结点值, 但是小于右孩子结点值。
或者每个结点的值小于左孩子结点值, 大于右孩子结点值。
这种结构的树就是排序二叉树。
在这里插入图片描述
这种排序二叉树在中序遍历会得到有序的序列。
上图左边中序遍历后:1, 2, 4, 5, 6, 9从小到大的序列。
同理右边的是从大到小的序列。
并且排序二叉树的查找效率很高logn。 决定效率取决于高度n
这也是后面平衡二叉树、 红黑树诞生的起源。

下面是模拟写出来的树
BTNodeTree.h

#pragma once

/*
	project: 模拟实现二叉树
	date:	 2020-6-19
	name:	 HMW
*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

typedef char BTNDataType;

typedef struct BTNode
{
	BTNDataType val;				//数据域
	struct BTNode* _lchild;			//左孩子节点
	struct BTNode* _rchild;			//右孩子节点
}BTNode;

//初始化创建树
BTNode* BTNodeInit(BTNDataType* a, int* pi);
//树的销毁
void BTNodeDestroy(BTNode** root);
//先序遍历
void BinaryTreePrevOrder(BTNode* root);
//中序遍历
void BinaryTreeInOrder(BTNode* root);
//后序遍历
void BinaryTreePostOrder(BTNode* root);
//镜像转化
void MirrorRecursively(BTNode* root);
//判断是否为对称二叉树
int isSameTree(BTNode* root);
// 判断二叉树是否是完全二叉树 
int BinaryTreeComplete(BTNode* root);
//树的节点个数
int BinaryTreeSize(BTNode* root); 
//树的叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
//第k层节点的个数
int BinaryTreeLevelKSize(BTNode* root, int k);
//第k层节点的叶子个数
int BinaryTreeLevelKLeafSize(BTNode* root, int k);
//返回查找的节点的值
BTNode* BinaryTreeFind(BTNode* root, BTNDataType x);
//层序遍历
void BinaryTreeLevelOrder(BTNode* root);
//非递归实现的先序遍历
void BinaryTreePrevOrderNonR(BTNode* root);
//非递归实现的中序遍历
void BinaryTreeInOrderNonR(BTNode* root);
//非递归实现的后序遍历
void BinaryTreePostOrderNonR(BTNode* root);

BTNodeTree.c

//#include"BTNodeTree.h"
#include"Stack.h"
#include"Queue.h"

//初始化创建树  
BTNode* BTNodeInit(char* a, int* pi)
{	
	//a是传入构建数组首元素的地址, pi是一个全局的下标,用它来取构建的元素。
	if (!a)
		return NULL;	

	//'#' -- 代表着这个值为空

	if (a[*pi] != '#')										//这个元素不为空
	{
		BTNode* root = (BTNode*)malloc(sizeof(BTNode));		//创建一个结点
		root->val = a[*pi];									//把数组中的值赋给结点
		++(*pi);											//下标偏移

		root->_lchild = BTNodeInit(a, pi);					//结点左孩子指针 = 递归的返回值
		++(*pi);											//下标偏移
		root->_rchild = BTNodeInit(a, pi);

		return root;										//返回构建的结点
	}
	else
	{
		return NULL;										//这个元素为空,返回null指针
	}									
}

//树的销毁
void BTNodeDestroy(BTNode** root)
{
	//树的销毁采用后序遍历的方式释放, 采用二级指针来释放。
	if (*root)
	{
		BTNodeDestroy(&(*root)->_lchild);
		BTNodeDestroy(&(*root)->_rchild);
		free(*root);
		*root = NULL;
	}
	else
		return;
}

//先序遍历
void BinaryTreePrevOrder(BTNode* root)
{
	if (root)
	{
		printf("%c ", root->val);
		BinaryTreePrevOrder(root->_lchild);
		BinaryTreePrevOrder(root->_rchild);
	}
	else
		return;
}
//中序遍历
void BinaryTreeInOrder(BTNode* root)
{
	if (root)
	{
		BinaryTreeInOrder(root->_lchild);
		printf("%c ", root->val);
		BinaryTreeInOrder(root->_rchild);
	}
}

 //后序遍历
void BinaryTreePostOrder(BTNode* root)
{
	if (root)
	{
		BinaryTreePostOrder(root->_lchild);
		BinaryTreePostOrder(root->_rchild);
		printf("%c ", root->val);
	}
}

//非递归实现的先序遍历
void BinaryTreePrevOrderNonR(BTNode* root)
{
	if (!root)
		return;
	//需要栈实现.
	Stack st;
	StackInit(&st);
	BTNode* cur = root;

	while (cur || !StackEmpty(&st))
	{
		while (cur)
		{
			printf("%c ", cur->val);
			StackPush(&st, cur);
			cur = cur->_lchild;
		}
		//出来就是cur == NULL,  左到底

		BTNode* Top = StackTop(&st);
		StackPop(&st);

		if (Top->_rchild)
			cur = Top->_rchild;
	}

	StackDestroy(&st);
}

//非递归实现的中序遍历
void BinaryTreeInOrderNonR(BTNode* root)
{
	if (!root)
		return;

	Stack st;
	StackInit(&st);
	BTNode* cur = root;

	while (cur || !StackEmpty(&st))
	{
		while (cur)
		{
			StackPush(&st, cur);
			cur = cur->_lchild;
		}

		BTNode* Top = StackTop(&st);
		printf("%c ", Top->val);
		StackPop(&st);

		if (Top->_rchild)
			cur = Top->_rchild;
	}

}

//非递归实现的后序遍历
void BinaryTreePostOrderNonR(BTNode* root)
{
	if (!root)
		return;

	Stack st;
	StackInit(&st);
	BTNode* cur = root;
	BTNode* prev = NULL;

	while (cur || !StackEmpty(&st))
	{
		while (cur)
		{
			StackPush(&st, cur);
			cur = cur->_lchild;
		}

		BTNode* Top = StackTop(&st);
		if (!Top->_rchild || Top->_rchild == prev)
		{
			printf("%c ", Top->val);
			StackPop(&st);
			prev = Top;
		}
		else
			cur = Top->_rchild;
	}
}

//层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	if (!root)
		return;

	Queue q;
	QueueInit(&q);
	
	QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		printf("%c ", front->val);

		if (front->_lchild)
			QueuePush(&q, front->_lchild);
		if (front->_rchild)
			QueuePush(&q, front->_rchild);
	}
	QueueDestroy(&q);
}

// 判断二叉树是否是完全二叉树 
int BinaryTreeComplete(BTNode* root)
{
	//0代表不是完全二叉树的情况, 1代表是完全二叉树的情况
	if (!root)
		return 0;

	Queue q;
	QueueInit(&q);

	QueuePush(&q, root);

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);

		if (front == NULL)
			break;
		else
		{
			QueuePush(&q, front->_lchild);
			QueuePush(&q, front->_rchild);
		}
	}

	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		
		if (front != NULL)
		{
			QueueDestroy(&q);
			return 0;
		}
	}

	return 1;
}

//树的节点个数
int BinaryTreeSize(BTNode* root)
{
	if (root)
	{
		return BinaryTreeSize(root->_lchild) + BinaryTreeSize(root->_rchild)
			+ 1;
	}
	else
		return 0;
}

//树的叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (!root)
		return 0;
	if (root->_lchild == NULL && root->_rchild == NULL)
		return 1;
	return BinaryTreeLeafSize(root->_lchild) + BinaryTreeLeafSize(root->_rchild);
}

//第k层节点的个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	if (root == NULL || k <= 0)
		return 0;

	if (root && k == 1)
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->_lchild, k - 1) + BinaryTreeLevelKSize(root->_rchild, k - 1);
}

//第k层节点的叶子个数
int BinaryTreeLevelKLeafSize(BTNode* root, int k)
{
	if (!root || k <= 0)
		return 0;

	if (root && k == 1)
	{
		if (root->_lchild == NULL && root->_rchild == NULL)
			return 1;
		return 0;
	}

	return BinaryTreeLevelKLeafSize(root->_lchild, k - 1) +
		BinaryTreeLevelKLeafSize(root->_rchild, k - 1);
}

//返回查找的节点的值
BTNode* BinaryTreeFind(BTNode* root, BTNDataType x)
{
	if (!root)
		return NULL;
	if (root->val == x)
		return root;
	else
	{
		return BinaryTreeFind(root->_lchild, x);
		return BinaryTreeFind(root->_rchild, x);
	}
}

//镜像转化
void MirrorRecursively(BTNode* root)
{
	if (!root)
		return;
	if (root->_lchild == NULL && root->_rchild == NULL)	//可有效提高效率
		return;

	BTNode* tmp = root->_lchild;
	root->_lchild = root->_rchild;
	root->_rchild = tmp;

	if(root->_lchild)
		MirrorRecursively(root->_lchild);
	if(root->_rchild)
		MirrorRecursively(root->_rchild);
}


// -- two
int Judge_Same(BTNode* root1, BTNode* root2)
{
	if (root1 == NULL && root2 == NULL)
		return 1;
	else if (root1 != NULL && root2 == NULL)
		return 0;
	else if (root1 == NULL && root2 != NULL)
		return 0;

	if (root1->val == root2->val)
	{
		return Judge_Same(root1->_lchild, root2->_rchild)
			&& Judge_Same(root1->_rchild, root2->_lchild);
	}
	else
		return 0;
}

//判断一个数是否是另一棵树的子树。 - one
int isSameTree(BTNode* root)
{
	if (!root)
		return 0;	//0代表失败
	if (root->_lchild != NULL && root->_rchild != NULL)
	{
		return Judge_Same(root->_lchild, root->_rchild);
	}
	else if (root->_lchild == NULL && root->_rchild == NULL)
		return 1;
	else
		return 0;
}

test.c

#include"BTNodeTree.h"
#include"Stack.h"
#include"Queue.h"

void test_BTNodeTree()
{
	char str[] = { '8', '6', '5', '#', '4', '#', '#', '7', '#', '#', '6', '7',
				'#', '#', '9', '#', '#' };

	//char str[] = { '1', '5', '#', '#', '3', '#', '#' };
	//char str[] = { '1', '#', '#' };
	
	int pi = 0;

	BTNode* root = BTNodeInit(str, &pi);

	puts("先序遍历");
	BinaryTreePrevOrder(root);
	puts("\n中序遍历");
	BinaryTreeInOrder(root);
	puts("\n后序遍历");
	BinaryTreePostOrder(root);
	puts("\n层序遍历");
	BinaryTreeLevelOrder(root);

	puts("\n非递归实现的先序遍历");
	BinaryTreePrevOrderNonR(root);
	puts("\n非递归实现的中序遍历");
	BinaryTreeInOrderNonR(root);
	puts("\n非递归实现的后序遍历");
	BinaryTreePostOrderNonR(root);


	if (BinaryTreeComplete(root))
	{
		puts("\n完全二叉树");
	}
	else
		puts("\n非完全二叉树!");

	printf("二叉树节点的个数为%d\n", BinaryTreeSize(root));
	printf("二叉树叶子节点的个数为:%d\n", BinaryTreeLeafSize(root));

	printf("第k层的节点个数为:%d\n", BinaryTreeLevelKSize(root, 3));
	printf("第k层的节点叶子个数为:%d\n", BinaryTreeLevelKLeafSize(root, 3));
	
	BTNode* p = BinaryTreeFind(root, '3');
	if (p)
		printf("返回的值为:%c\n", p->val);
	else
		puts("没有这个值的节点");
	
	
	puts("");
	BTNodeDestroy(&root);
	if (root == NULL)
		puts("清空");
	else
		puts("内存泄漏!");
}

int main()
{
	test_BTNodeTree();

	//test_Swordoffer();
	system("pause");
	return 0;
}

测试结果:
在这里插入图片描述

那个, 这是以前还没有学习c++写的, 所以关于栈、队列没有使用STL标准库, 自己的接口, 这两个接口我就不在这里发了, 我在github上传, 有兴趣的朋友自己弄着玩。
https://github.com/haomengwudaydayup/learn_c_language/tree/master/PORT

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值