二叉树链式结构的实现

简单构建一颗二叉树

在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在我们对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。
 


typedef char BTDataTypa;
typedef struct BinaryTreeNode
{
	BTDataTypa data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;

}TreeNode;

//创建节点
TreeNode* BuyTreeNode(int x)
{
	TreeNode* node = (TreeNode*)malloc(sizeof(TreeNode));
	assert(node);

	node->data = x;
	node->left = NULL;
	node->right = NULL;
	return node;
}

//初始化
TreeNode* CreateTree()
{
	TreeNode* node1 = BuyTreeNode(1);
	TreeNode* node2 = BuyTreeNode(2);
	TreeNode* node3 = BuyTreeNode(3);
	TreeNode* node4 = BuyTreeNode(4);
	TreeNode* node5 = BuyTreeNode(5);
	TreeNode* node6 = BuyTreeNode(6);

	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	return node1;
}

 注意:上述代码并不是创建二叉树的方式,真正创建二叉树方式后序详解重点讲解

二叉树的概念

 再看二叉树基本操作前,再回顾下二叉树的概念。

二叉树是:

1. 空树

2. 非空:根节点,根节点的左子树、根节点的右子树组成的

 

任何一颗二叉树拆解成三部分:

  • 左子树
  • 右子树

 

 从概念中可以看出,二叉树定义是递归式的,因此后序基本操作中基本都是按照该概念实现的。


二叉树的遍历

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

按照规则二叉树的遍历有:前序、中序以及后序遍历的递归遍历:

  • 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。    (根    左子树   右子树)
  • 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。(左子树 根   右子树)
  • 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。        (左子树  右子树   根)
//前序遍历
void PrevOread(TreeNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}

	printf("%c ", root->data);
	PrevOread(root->left);
	PrevOread(root->right);

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

}

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

}

 

 前序遍历结果:1 2 3 4 5 6

 中序遍历结果:3 2 1 5 4 6

 后序遍历结果:3 2 5 6 4 1

在这里我们的结果就出来了,但是为了防止陷入误区,理解不深刻,以及更好的理解子问题分治的过程所以我们将空树也算上,便于更好的理解

 

 前序遍历结果:1 2 3 N N N 4 5 N N 6 N N

 中序遍历结果:N 3 N 2 N 1 N 5 N 4 N 6 N

 后序遍历结果:N N 3 N 2 N N 5 N N 6 4 N

 二叉树的创建

二叉树的构建


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

	TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
	if (root == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	root->data = a[(*pi)++];

	root->left = TreeCreate(a, pi);
	root->right = TreeCreate(a, pi);
	return root;
}
int main()
 {
	char a[] = "ABD##E#H##CF##G##";
	//可以创建全局变量,存在数据段全局变量不受栈帧影响//但是可能会多次调用,需要重置
	///局部变量,传地址,形参不影响实参
	int i = 0;
	TreeNode* root = TreeCreate(a, &i);

在二叉树中我们主要是通过递归的方法来实现各种操作
在递归的时候我们主要要掌握子问题分治的思路:分而治之 ,将一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题

二叉树的常规题目 

  求二叉树节点个数:
我们首先分析子问题:
1.返回条件  root为NULL
2.分治:左子树节点+右子树节点+1(根)
代码实现如下

int TreeSize(TreeNode* root)
{
	return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}

求二叉树叶子节点的个数 
我们首先分析子问题:
1.  返回条件  节点为空就返回
2. 分治:叶子节点没有孩子,所以我们可以把节点为空返回0,如果节点自身不为空并且该节点的左孩子为空,右孩子也为空,我们可以返回1,最后我们左子树+右子树就可以得到叶子节点的个数.
代码实现如下
 

//二叉树叶子节点个数
int TreeLeafSize(TreeNode*root)
{
	if (root == NULL)
	{
		return 0;
	}
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	return TreeLeafSize(root->left)+TreeLeafSize(root->right);

}

 求二叉树的高度,深度

首先子问题分治:
1.为空 返回;
2.非空 左子树的高度和右子树的高度大的加1;

//二叉树求深度
int TreeHeight(TreeNode* root)
{
	if (root == NULL)
	{
		return 0;
	}
	//左树,右树大的加根
	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);
	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	//fmax也阔以头文件<math.h>

}

但是这里还有一种下意识会使用的方法,

int TreeHeight(TreeNode* root)
{
	
	
	return root==NULL?0:
          TreeHeight(root->left)>TreeHeight(root->right)?
          TreeHeight(root->left)+1:
          TreeHeight(root->right)+1;
	

}

这种方法存在大量的重复调用,得到的数据,没有被记录,每一次都需要从头开始调用,在数量稍多的情况就会出现崩溃。 

二叉树第k层节点个数

子问题分治:
1.为空 返回;
2.不为k,k==1,返回1;
3.不为空,且k>1,返回左子树的k-1层+右子树的k-1层。


 

代码实现如下

//求第k层节点个数
int TreeLeveLK(TreeNode* root,int k)
{
	if (root == NULL)
		return 0;

	if (k == 1)
		return 1;
	assert(k > 0);

	return TreeLeveLK(root->left, k - 1) + TreeLeveLK(root->right, k - 1);

}

二叉树的销毁  
我们在对二叉树进行free释放节点时最方便的方法,是使用后序遍历free节点,代码实现如下
 

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

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

  • 24
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值