二叉树的基本运算和基本操作的接口实现(C语言)

二叉树的概念

二叉树的组成:

  1. 空树
  2. 非空:根节点,根节点的左子树,根节点的右子树

二叉树是递归形式的,所以接下来也是的一些基本操作都是按递归进行。

二叉树的遍历 (递归)

二叉树遍历:就是按某种特定的规则,依次对二叉树的节点进行操作。
因为前、中、后序遍历比较相似,我选一个中序遍历进行画图分析,做递归展开图。
建议:可以先观察递归展开图 ,每次函数的调用都是一次栈帧的创建,每一次函数的返回都是栈帧的销毁。

前(先)序遍历(Preorder Traversal)

定义:先访问根节点,然后访问左右子树

void PreOrder(BTNode* root)
{
	if (NULL == root)
	{
		printf("NULL ");
		return;
	}
	
	//先访问根节点,在访问左子树和右子树
	printf("%d ", root->data);
	PreOrder(root->left);
	PreOrder(root->right);
}

中序遍历(Inorder Traversal)

定义:先遍历左子树,然后访问根节点,最后访问右子树

void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}

	//先访问左子树,然后访问根节点,最后访问右子树
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);

} 

后序遍历(Postorder Traversal)

定义:先访问左右子树,然后访问根节点。

void PostOrder(BTNode* root)
{
	if (NULL == root)
	{
		printf("NULL ");
		return;
	}

	//像=先访问左右子树,再访问根节点
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%d ", root->data);
}

中序遍历递归展开图

中序遍历递归展开图

小结

  1. 二叉树的遍历,都是以递归的形式实现。二叉树的遍历是很重要的运算,也是二叉树其他运算的基础。
  2. 因为访问的节点一定是某子树的根。所以又称N(Node)、L(Left subtree)、R(Right subtree)。NLR:先根遍历。LNR:中根遍历。LRN:后根遍历。

二叉树的节点个数以及高度等接口的实现

选一部分接口画递归展开图。

这些基本运算都是采用分治的思想完成。分治和遍历是有区别的,我在求二叉树的节点个数接口里,会展示一次用遍历求节点个数,下面别的接口就不再展示遍历的求法了。

遍历和分治的区别

遍历通常是没有返回值的,分治是有返回值的。
遍历:通过根节点把每个节点都走个遍。
分治:分开治理,谁的孩子谁管着的原则。逻辑更清晰。

遍历:
遍历思想

分治:
分治思想

建议: 可以对照代码,在不懂的接口画递归展开图,就如同我在上面中序遍历那样,便于理解。

二叉树的节点个数

遍历

//二叉树节点个数   遍历
void BinaryTreeSize(BTNode* root, int* psize)
{
	if (NULL == root)
		return;
	//使用指针,改变数值。如果之间使用数值,可能会造成数值随着栈帧的销毁而销毁
	++(*psize);
	BinaryTreeSize(root->left, psize);
	BinaryTreeSize(root->right, psize);
}

分治

//二叉树的节点个数
int BinaryTreeSize(BTNode* root)
{
	if (NULL == root)
		return 0;
	
	//可以看我在分治和遍历的区别里介绍的分治思想的图。就如:最后一层的双亲节点(图中就是小组长),去计算下面有几个叶子节点(组员)。然后班长计算小组长汇报的情况合在一起,依次直到根节点。
	//建议画递归展开图,我在这里介绍可能还会帮读者绕进去。
	return BinaryTreeSize(root->left)
		 + BinaryTreeSize(root->right) + 1;
}

二叉树的高度

//二叉树的高度
int BinaryTreeHeight(BTNode* root)
{
	if (NULL == root)
		return 0;
	
	//利用好函数的返回值,既方便下次使用,又减少重复开辟栈帧的情况。
	int leftHeight = BinaryTreeHeight(root->left);
	int rightHeight = BinaryTreeHeight(root->right);
	
	//选左右子树较大的值,然后+1返回
	return leftHeight > rightHeight ? 
		   leftHeight + 1 : rightHeight + 1;
}

递归展开图

二叉树高度的递归展开图

二叉树的叶子节点个数

//二叉树的叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
	if (NULL == root)
		return 0;
		
	//判断是否为叶子节点,如果是返回1,
	if (root->left == NULL && root->right == NULL)
		return 1;

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

二叉树的k层节点个数

//二叉树的k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
	assert(k > 0);

	if (NULL == root)
		return 0;
		
	//如果不为空,能到第k层,在这里就要截停
	if (k == 1)
		return 1;
		
	//每次递归节点都要向下走一层,所以距离第k层也逐步减一。注意:递归要向递归结束的条件靠拢
	return BinaryTreeLevelKSize(root->left, k - 1)
		 + BinaryTreeLevelKSize(root->right, k - 1);
}

二叉树查找值为x的节点

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

	//再次强调一下,函数的返回值一定要妥善利用
	BTNode* l_ret = BinaryTreeFind(root->left, x);
	//l_ret要么是空要么就是所要查找的节点的返回值
	if (l_ret)
		return l_ret;

	BTNode* r_ret = BinaryTreeFind(root->right, x);
	if (r_ret)
		return r_ret;

	return NULL;
}

递归展开图

查找结点的递归展开图

二叉树的销毁

void BinaryTreeDestroy(BTNode* root)
{
    if (root == NULL)
        return 0;

	//访问左右子树,再销毁。
    BinaryTreeDestroy(root->left);
    BinaryTreeDestroy(root->right);
    free(root);
}

总结

这一块内容需要思考一下,小编在刚学的时候也有点迷茫。为什么这样做就可以计算出二叉树的节点数什么的。多画几遍递归展开图,然后思考总结。

注意: 要注意在递归的过程中函数的返回值,妥善利用。

我当初思考的时候就在想,是先有相关代码的还是先有二叉树的,两种间的联系为什么会那么巧妙。也可能是小编的脑洞太大的原因。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
下面是用C语言实现二叉树基本运算算法的示例代码: ```c #include <stdio.h> #include <stdlib.h> // 定义二叉树结构体 typedef struct Node { int data; // 数据域 struct Node *left; // 左子节点指针 struct Node *right; // 右子节点指针 } Node; // 创建节点 Node *createNode(int data) { Node *node = (Node *) malloc(sizeof(Node)); node->data = data; node->left = NULL; node->right = NULL; return node; } // 插入节点 Node *insertNode(Node *root, int data) { // 如果根节点为空,则新建一个节点作为根节点 if (root == NULL) { root = createNode(data); } // 如果数据小于根节点的数据,则插入到左子树 else if (data < root->data) { root->left = insertNode(root->left, data); } // 如果数据大于等于根节点的数据,则插入到右子树 else { root->right = insertNode(root->right, data); } return root; } // 查找节点 Node *findNode(Node *root, int data) { // 如果根节点为空,则返回NULL if (root == NULL) { return NULL; } // 如果数据等于根节点的数据,则返回根节点 else if (data == root->data) { return root; } // 如果数据小于根节点的数据,则在左子树中查找 else if (data < root->data) { return findNode(root->left, data); } // 如果数据大于根节点的数据,则在右子树中查找 else { return findNode(root->right, data); } } // 删除节点 Node *deleteNode(Node *root, int data) { Node *temp; if (root == NULL) { return NULL; } else if (data < root->data) { root->left = deleteNode(root->left, data); } else if (data > root->data) { root->right = deleteNode(root->right, data); } else { if (root->left == NULL) { temp = root->right; free(root); return temp; } else if (root->right == NULL) { temp = root->left; free(root); return temp; } else { temp = root->right; while (temp->left != NULL) { temp = temp->left; } root->data = temp->data; root->right = deleteNode(root->right, temp->data); } } return root; } // 中序遍历 void inorderTraversal(Node *root) { if (root != NULL) { inorderTraversal(root->left); printf("%d ", root->data); inorderTraversal(root->right); } } int main() { Node *root = NULL; // 插入节点 root = insertNode(root, 5); root = insertNode(root, 3); root = insertNode(root, 8); root = insertNode(root, 2); root = insertNode(root, 4); root = insertNode(root, 7); root = insertNode(root, 9); // 中序遍历 printf("Inorder traversal: "); inorderTraversal(root); printf("\n"); // 查找节点 Node *node = findNode(root, 4); if (node != NULL) { printf("Node found: %d\n", node->data); } else { printf("Node not found\n"); } // 删除节点 root = deleteNode(root, 5); // 中序遍历 printf("Inorder traversal: "); inorderTraversal(root); printf("\n"); return 0; } ``` 以上代码实现二叉树基本操作,包括创建节点、插入节点、查找节点、删除节点和中序遍历等。你可以根据自己的需求进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

kpl_20

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

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

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

打赏作者

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

抵扣说明:

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

余额充值