c语言数据结构——二叉树的实现与递归

二叉树的实现与递归

上期说到了二叉树的一些概念和定义。还对一种特殊的完全二叉树——堆进行了实现。本片文章我们将对二叉树的一些简单的基础的功能进行实现

二叉树结构

来简单回顾一下:

二叉树分为顺序存储和链式存储。当一个树为完全二叉树的时候,使用顺序存储是十分方便的,因为可以通过下标找出对应的孩子节点和双亲节点。但是如果不是完全二叉树,那很容易造成空间的浪费。所以可以将二叉树定义成一个个节点组成的树。每个节点存储着其左右孩子(如果有)节点的地址:

typedef char BTDataType;

typedef struct BinaryTreeNode {
   //树的节点
	BTDataType data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

而之前又说到过,二叉树本身就是一个递归的结构:

因为一个树可以由根和左右子树组成,而左右子树又可以拆分为根和子树,直到拆分到空节点,即不能再拆分。这是天然的递归结构。所以对于很多功能,使用递归虽然不好理解,但是实现的代码却十分简介简单。下面让我们来看看如何使用递归来完成一些功能。

递归部分

这个部分将重点对如何使用递归来进行功能的实现,并且加深对递归的理解。

再实现功能之前,先来想想递归的重要条件:
1.一个问题可以被拆分成子问题
单看这个概念是十分抽象的。但是其实我们早就接触过递归了,就比如高中学过的数列递推公式:an+1 = an + 1,又或是著名的斐波那契数列 an = an-1 + an-2

假设使用第一个公式,告诉a1 = 1,求a4

过程如下:
a4 = a3 + 1
a3 = a2 + 1
a2 = a1 + 1
代入a1 = 1,求出a4 = 4

想必这个过程大伙都做过。这就是递归。我们要求一个值,需要不断代入这个公式,直到走到终止条件再返回。所以对于拆分成子问题这个条件,我们只需要明确这个问题能不能有所谓的“递推公式”。

2.需要有终止条件
就像刚刚上面提到的例子,如果没有终止条件a1 = 1,那是算不出来a4 = 4的,会无限递推下去。所以需要给出条件判断合适停止递归。

总的来说,我认为递归就是这两个要素,在后续的功能实现中,将基于这两个原则进行实现。

二叉树创建

这个部分将分两个部分来讲:
1.手搓一个
何为手搓?就是根据二叉树的形状手动造一个:
在这里插入图片描述
就比如要创建出这么一个二叉树用来测试,我们可以自己开辟这么多个节点,手动的将其指针指向对应位置:

BTNode* BinaryTreeBuyNode(BTDataType x) {
   
	BTNode* NewNode = (BTNode*)malloc(sizeof(BTNode));
	if (!NewNode) {
   
		perror("BinaryTreeNode Malloc");
		exit(-1);
	}
	NewNode->data = x;
	NewNode->left = NULL;
	NewNode->right = NULL;
	return NewNode;
}

BTNode* CreateTree() {
   
	BTNode* Node1 = BinaryTreeBuyNode('A');
	BTNode* Node2 = BinaryTreeBuyNode('B');
	BTNode* Node3 = BinaryTreeBuyNode('C');
	BTNode* Node4 = BinaryTreeBuyNode('D');
	BTNode* Node5 = BinaryTreeBuyNode('E');
	BTNode* Node6 = BinaryTreeBuyNode('F');
	BTNode* Node7 = BinaryTreeBuyNode('G');
	BTNode* Node8 = BinaryTreeBuyNode('H');

	Node1->left = Node2; 
	Node1->right = Node3; 
 	Node2->left = Node4;
	Node2->right = Node5;
 	Node3->left = Node6;
 	Node3->right = Node7;
	Node5->right = Node8;   

	return Node1;
}

但是这样看起来就很麻烦,很繁杂。但是这样非常适合调试的时候使用。比如在Leetcode上面做题,输入某个用例答案错误的时候,就可以这样子手搓一个进行调试。

但有没有办法根据什么办法来根据输入的要求来进行创建二叉树呢?

答案是有的:
2.根据前序遍历来创建

在这里先讲一下何为前序遍历:
前序遍历的定义是:一棵树和在其内的子树,遍历的时候总是遵循着:
先遍历根节点,再遍历左子树,再遍历右子树(这是一个递归概念)

举个例子解释一下:
在这里插入图片描述
这就是这个简单的二叉树的前序遍历过程,对于任意一个子树都需要按照根、左、右的顺序。

所以最后前序遍历的结果序列是(遍历到空以#代替):ABD###CF###

根据根访问顺序的不同,还可以分为中序遍历,后序遍历。这一点就不再赘述了,原理和上面类似,只不过是根节点访问顺序不同罢了。

假设想要根据前序序列**ABD##E#H##CF##G##**创建一个二叉树,可以使用递归。

思路:
声名一个变量i,用作遍历这个序列。
如果不为‘#’,就创建一个节点,插入到当前的根上。然后依次创建当前根的左子树和右子树。
而后让i的位置指向下一个位置。
如果为‘#’,就直接让i指向下一个位置。

终止条件:当i的值大于数组长度时候,就退出

代码所示:

BTNode* BinaryTreeCreate(BTDataType* a, int n, int* pi) {
   
//函数的作用:开一个节点 赋值然后返回这个节点
	if(*pi >= n || a[*pi] 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值