【数据结构初阶】二叉树---链式存储


    由于前面一篇文章《二叉树—堆》已经讲解过二叉树的一些定义了,在这篇文章就不过多的讲解了,我们直接开始链式结构的讲解!

正文开始:

    前置说明:在学习二叉树链式结构之前,需要先创建一棵树。由于大家可能对二叉树结构的了解不是那么深,为了降低大家理解的难度,我们就先手搓一棵二叉树方便大家接下来的学习。待讲得差不多了再回来讲二叉树的创建。

一、手搓一棵二叉树

typedef struct BinaryTreeNode
{
	BTDataType data; // 当前结点值域
	struct BinaryTreeNode* left; // 指向当前结点左孩子
	struct BinaryTreeNode* right; // 指向当前结点右孩子
}BTNode;
//给节点申请空间
BTNode* BuyNode(int x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		return NULL;
	}
	node->data = x;
	node->left = NULL;
	node->right = NULL;

	return node;
}
BTNode* CreatBinaryTree()
{
	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;
}
int main()
{
    BTNode* root = CreatBinaryTree();
    return 0;
}

注:上述代码并不是创建二叉树的正确方式,仅用于讲解
再看二叉树基本操作前,再回顾下二叉树的概念,二叉树是

  • 空树
  • 非空:根结点,根结点的左子树、根结点的右子树组成的
    在这里插入图片描述
    接下来的讲解是以递归的形式进行的

二、二叉树的遍历


    学习二叉树的结构,最基础的就是遍历二叉树。所谓二叉树遍历是按照某种特定的规则,依次对二叉树中的结点进行相应的操作,并且每个结点只操作一次。访问结点所做的操作依赖于具体的应用问题。遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。
按照规则,二叉树的遍历方式有:前序、中序和后续遍历(递归结构)

2.1 前序遍历

     前序遍历(亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。简单地说就是先访问根节点再访问左右子树的节点:
在这里插入图片描述

代码展示:

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

    中序遍历——访问根结点的操作发生在遍历其左右子树之中。也就是说先访问左子树再访问根节点最后访问右子树:
在这里插入图片描述
代码展示:

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

    后序遍历——访问根结点的操作发生在遍历其左右子树之后。这里就不多解释了,和以上两种相似。
代码展示:

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

    层序遍历,顾名思义就是按照顺序一层一层地遍历二叉树,而不是按照树的父子关系去遍历。但是不通过父子关系又该如何把二叉树都遍历一遍呢?这时候我们就要摒弃我们遍历的一贯思维,想想还有什么办法去遍历二叉树。这时我们可以引入队列的思想,将二叉树的节点进入一个出去一个(队列数据先进先出)
在这里插入图片描述
代码展示:

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);//队列的初始化
	if (root != NULL)
		QueuePush(&q, root);//将数据插入队列
	while (!QueueEmpty(&q))//当队列为空的时候就说明二叉树已经遍历完成了
	{
		BTNode* front = QueueFront(&q);//取队列的队头数据
		QueuePop(&q);//删除队头数据

		printf("%d ", front->data);
		if (root->left)
			QueuePush(&q, root->left);
		if (root->right)
			QueuePush(&q, root->right);
	}
	QueueDestroy(&q);//销毁队列
}

注:队列的实现代码我就不在这展示了,有老铁不清楚的就可以去了解并实践一遍

三、二叉树的销毁


    这篇文章的主题是链式结构的存储,那么既然是链式结构存储,想必大家对销毁链式结构的方式并不陌生吧?没错,就是遍历二叉树依次销毁节点!那么销毁链表应该使用哪种遍历方式呢?答案是都可以,不过在这里推荐使用后序遍历因为前序遍历会优先将根节点释放掉导致找不到左右子树,所以想要找到左右子树还要先将根节点储存起来;而中序遍历呢会导致找不到右子树

代码展示:

void BinaryTreeDestory(BTNode* root)//后序遍历销毁
{
	if (root == NULL)
		return;

	BinaryTreeDestory(root->left);
	BinaryTreeDestory(root->right);
	free(root);
}

四、二叉树的节点个数


    求二叉树的节点个数也是一个比较基础的问题,这里我们同样使用遍历法来依次累加得出最终的个数:
在这里插入图片描述

代码展示:

// 求二叉树结点的个数
int BinaryTreeSize(BTNode* root)
{
	if (root == NULL)
		return 0;

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

运行结果:
在这里插入图片描述

五、二叉树的高度


    求二叉树的高度,也就是求二叉树的层数:
在这里插入图片描述


代码展示:

//求二叉树的高度
int BinaryTreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;
	int leftheight = BinaryTreeHeight(root->left);
	int rightheight = BinaryTreeHeight(root->right);

	return leftheight > rightheight ?
		leftheight + 1 : rightheight + 1;
}

六、二叉树叶子节点的个数


    求叶子结点的个数,首先要知道什么是叶子节点,叶子节点的判定条件是什么?叶子节点就是当它没有孩子,也就是左右孩子都为空时,那么该节点就是叶子节点。
代码展示:

//求二叉树叶子结点的个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	if (root->left == NULL && root->right == NULL)//当
		return 1;

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

七、查找二叉树的某个节点


    查找二叉树中是否有存在某个节点,如果一棵树有多个相同数据的节点,那么只需要找到第一个就可以了,其他的就不用找了:

代码展示:

//查找二叉树值为x的节点
BTNode* BinaryTreeFind(BTNode* root,BTDataType x)
{
	if (root == NULL)
		return NULL;

	if (root->data == x)//如果值相同直接返回上一层
		return root;

	BTNode* ret1 = BinaryTreeFind(root->left, x);
	if (ret1)//返回的值需要一个变量去接收
		return ret1;

	BTNode* ret2 = BinaryTreeFind(root->right, x);//左子树找到了就不用到右子树找了
	if (ret2)
		return ret2;

	return NULL;//都没找到就要返回空
}

八、将数组"ABD##E#H##CF##G##"构建成二叉树


    前面我们为了方便讲解和调试,可以通过手搓创建一棵二叉树,这种方式是比较原始的;现在我们通过前序遍历来将以上数组构建成一棵完整的二叉树。注:这里的“#”表示空。

通过前序遍历将数组转换为如下图解:
在这里插入图片描述
代码展示:

// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
	if (a[*pi] == '#')//如果当前元素为“#”,则返回NULL
	{
		(*pi)++;//这里的“++”不能放在判断条件上,因为每次递归都会进行判断,即便不是空也会进行“++”,导致创建出的二叉树与数组不一致
		return NULL;
	}
	//不为空则先创建一个节点
	BTNode* root = (BTNode*)malloc(sizeof(BTNode));
	root->data = a[(*pi)++];//将数组的数据插入到树的节点中
	root->left = BinaryTreeCreate(a, pi);
	root->right = BinaryTreeCreate(a, pi);
	return root;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值