树和二叉树<3>

一.二叉树的链式结构

当我们用链式存储二叉树时,常使用的底层结构是链表,不过和单链表不一样,但和循环双向链表相似,其存在数值区域和两个结点区域,后者用来存储左右子树的地址。其次二叉树独特的结构根-左结点-右结点以及其有层次的存储导致了递归的特殊性性质。我个人认为双向循环链表难以使用递归在于缺少第二个条件。最后这篇博客将从二叉树可以递归的角度进行展开。

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

1.二叉树的创建

typedef int BTDataType;
//二叉链
typedef struct BinaryTreeNode
{
	BTDataType data; // 当前结点值域	
	struct BinaryTreeNode* left; // 指向当前结点左孩子
	struct BinaryTreeNode* right; // 指向当前结点右孩子
}BTNode;

2.动态创立新结点

//动态创立新结点
BTNode* BuyNode(BTDataType x)
{
	BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
	assert(newnode);
	newnode->data = x;
	newnode->left = NULL;
	newnode->right = NULL;
	return newnode;
}

 堆空间开辟的内存空间不会随着函数的结束被系统回收,这是要注意的一点。

3.创建二叉树

//创建二叉树
BTNode* GreatBTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
 
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
 
	return node1;
}

手动构建的二叉树将使各算法的结果更加直观,以下为手动创建的二叉树的示意图:
 05155e1b3f8948759ecb26351b95a46c.png

4.前序遍历

//前序遍历
void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
 
	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

 我个人理解前序中序后序之所以叫这个是因为根结点的位置不同:根节点在前,左子树,右子树的顺序叫前序;左子树,根,右子树的顺序叫中序.......回到前序的实现思路上,可以理解为以一往直前的递归,然后遵循前序的打印顺序依次遍历。就是先打印根结点后,要划归好每一个小的二叉树,然后从小二叉树开始,再到大二叉树,完成左递归,同理右递归也是如此。如下图所示:

c445aac4186441038fcc94117f4b750b.png

5.中序遍历

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

6.后序遍历

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

三.二叉树的其他各类问题

1.接口函数

//结点个数
int	SumNode(BTNode* root);
//叶子结点个数
int LeafNode(BTNode* root);
//二叉树高度
int HeightTree(BTNode* root);
//二叉树第k层结点个数
int BTreeLeveSize(BTNode* root, int k);
//二叉树查找值为x的结点
BTNode* BTreeFine(BTNode* root, int x);

2.结点个数

//结点个数
int SumNode(BTNode* root)
{
	return root == NULL ? 0 : SumNode(root->left) + SumNode(root->right) + 1;
}

这个实现思路在于 第一次进行判断这个1代表的是根结点的个数,及最大的一棵二叉树的根结点,当递归进入root->left后,其可以认为该节点以下的左右结点以及它自己组成了一棵小树,这个1就是这棵小二叉树的根结点,只不过在大二叉树里,这个小树的根结点不过是大树根结点的左结点。依次递归,结束条件在于判断其小树的根结点是否为NULL。左右结点的处理思路皆是如此。

3.叶子节点个数

//叶子结点个数
int LeafNode(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->left==NULL && root->right==NULL)
	{
		return 1;
	}
	else
	{
		return LeafNode(root->left) + LeafNode(root->right);
	}
}

 这个实现思路是以递归的方式遍历数组,只需要判断左右子树是否为NULL即可,注意这个条件需要同时满足。

4.二叉树的高度

//二叉树高度
int HeightTree(BTNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	int left = HeightTree(root->left);
	int right = HeightTree(root->right);
	return left > right ? left + 1 : right + 1;
}

这个实现思路是在难以判断二叉树的形状下(不要想象其通为对称的),在最大二叉树的划分下,去以左右子树的最高值来确定二叉树的高度。同样是以递归的方式实现,这里的1表示了递归经过的每一层后高度加1,左右的不同加1是左右子树的区分。

5.二叉树第k层结点个数

//二叉树第k层结点个数
int BTreeLeveSize(BTNode* root, int k)
{
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return BTreeLeveSize(root->left, k - 1)  + BTreeLeveSize(root->right, k - 1);
}

这里的实现思路是将第k层结点转化成第k-1层结点,和确定叶子结点有异曲同工之妙。 

6.二叉树查找值为x的结点

//二叉树查找值为x的结点
BTNode* BTreeFine(BTNode* root, int x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	if (BTreeFine(root->left, x) == NULL)
	{
		return BTreeFine(root->right, x);
	}
	else
	{
		return BTreeFine(root->left, x);
	}
}

这里的实现思路是以递归的方式去依次遍访二叉树的值,要遍访的方式与以孩子兄弟存储方式进行层级遍历类似。这个问题和求二叉树节点的思路类似,可以理解为从左往右的,从上往下的查找。

以上就是二叉树递归算法的实现,递归最好的方式是画图。但是个人认为最重要的是思维,就像每一次递归,程序中的1的位置将决定了递归将以何种方式实现,是应该在遍历前就+1,还是在遍历后+1,将直接影响到我们算法的正确性。

以上就是树和二叉树<3>若有不足之处,还望不惜赐教。

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一只爱喝coke的小鳄鱼

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

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

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

打赏作者

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

抵扣说明:

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

余额充值