C语言之二叉树实现

摘选自百度百科的解释:
在计算机科学中,二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。

个人理解:
二叉树算是相对较难的一个槛,一般的实现方式为,当前节点 大于左子树的节点,小于右子树的节点。由于其特性,在查找的时候有则较快的效率。
我尽量在代码中进行了大量的注释,自己画图理解会更快一点。

代码实现,以 int 整型为元素进行 示例

#include<stdio.h>
#include<stdlib.h>

/*
\ brief 二叉树节点声明
*/
typedef struct TreeNode
{
	int element;							// 元素
	struct TreeNode* leftChild;				// 左子树
	struct TreeNode* rightChild;			// 右子树
}TreeNode,* TreeNodePtr;

/*
\ brief     清空整颗二叉树
\ param 	节点指针
*/
void makeEmpty(const TreeNodePtr node)
{
	// 如果节点为NULL
	if (!node)
	{
		return;
	}
	// 删除当前节点的左子树
	makeEmpty(node->leftChild);
	// 删除当前节点的右子树
	makeEmpty(node->rightChild);
	// 释放当前节点
	free(node);
}

/*
\ brief 	通过值查找指定节点
\ param 	value 要找的值
\ param 	node 节点指针
\ return 	成功返回节点,失败返回NULL
*/
TreeNodePtr findNode(const int value, TreeNodePtr node)
{
	// 如果当前节点为NULL
	if (!node)
	{
		return NULL;
	}
	// 如果要查找的值 小于 当前节点
	if (value < node->element)
	{
		// 尾递归 查找左子树
		return findNode(value, node->leftChild);
	}
	// 如果要查找的值 大于 当前节点
	if (value > node->element)
	{
		// 尾递归 查找右子树
		return findNode(value, node->rightChild);
	}
	// 等于当前节点,返回当前节点
	return node;
}

/*
\ brief 	查找二叉树最小节点
\ param 	节点指针
\ return 	返回二叉树中最小节点
*/
TreeNodePtr findMin(TreeNodePtr node)
{
	// 如果当前节点为NULL
	if (!node)
	{
		return NULL;
	}
	// 如果当前节点的左子树为NULL,说明这个已经是最小节点
	if (!node->leftChild)
	{
		// 返回当前节点
		return node;
	}
	// 尾递归 左子树
	return findMin(node->leftChild);
}

/*
\ brief 	查找二叉树最大节点
\ param 	节点指针
\ return 	返回二叉树最大节点
*/
TreeNodePtr findMax(TreeNodePtr node)
{
	// 如果当前节点为NULL
	if (!node)
	{
		return NULL;
	}
	// 如果当前节点的右子树为NULL,说明这个已经是最大节点
	if (!node->rightChild)
	{
		return node;
	}
	// 尾递归右子树
	return findMax(node->rightChild);
}

/*
\ brief 	插入节点
\ param 	element 要插入的元素
\ param 	node 节点指针
\ return 	返回节点指针
*/
TreeNodePtr insert(const int element, TreeNodePtr node)
{
	// 如果当前节点为NULL
	if (!node)
	{
		// 分配内存
		node = (TreeNodePtr)malloc(sizeof(TreeNode));
		// 将值赋值给 新节点
		node->element = element;
		// 将新节点的左子树 和 右子树 置空
		node->leftChild = NULL;
		node->rightChild = NULL;
	}	
		// 如果要插入的元素 比当前节点的元素要小 
	else if (element < node->element)
	{
		// 往左边插入,当前节点的左子树可能会更新    ps : 这里有点绕,不好解释,只能意会一下。
		node->leftChild = insert(element, node->leftChild);
	}
	 // 如果要插入的元素 比当前节点的元素要大
	else if (element > node->element)
	{
		// 往右边插入,当前节点的左子树可能会更新    ps : 这里有点绕,不好解释,只能意会一下。
		node->rightChild = insert(element, node->rightChild);
	}
	// 既然不大于也不小于,节点元素已存在,返回当前节点即可,不需要做更新。
	return node;
}

/*
\ brief 	擦除一个节点
\ param 	element 要擦除的元素
\ param 	node 节点指针
\ return 	返回节点指针
*/
TreeNodePtr eraseNode(const int element, TreeNodePtr node)
{
	// 分配一个临时节点
	TreeNodePtr tempNode;
	// 如果当前节点为NULL
	if (!node)
	{
		// 返回NULL
		return NULL;
	}
	// 如果要擦除的元素比当前元素要小
	if (element < node->element)
	{
		// 递归左子树进行擦除
		node->leftChild = eraseNode(element, node->leftChild);
	}
	// 如果要擦除的元素比当前元素要大
	else if (element > node->element)
	{
		// 递归右子树进行擦除
		node->rightChild = eraseNode(element, node->rightChild);
	}
		// 如果要擦除的元素就是当前元素,那么判断 当前节点的左子树 和 右子树 是否都不为NULL
	else if (node->leftChild && node->rightChild)
	{
		// ps : 左右 子树都不为NULL的时候,一般从 右子树中寻找最小的节点,与 当前节点交换值,
		// 然后在删除右子树中最小的节点。
		// 获取 当前节点的 右子树最小的元素节点
		tempNode = findMin(node->rightChild);
		// 将右子树最小的元素的值 赋值给当前节点
		node->element = tempNode->element;
		// 擦除 右子树中最小的元素
		node->rightChild = eraseNode(tempNode->element, node->rightChild);
	}
	else
	{
		// ps : 执行到这里  那么说明 要删除的节点就是当前节点,而且左子树或者右子树
		// 必定有一个是为NULL,或者两个都为NULL
		// 保存当前节点到临时节点变量中
		tempNode = node;
		// 如果左子不为空
		if (node->leftChild)
		{
			//当前节点 赋值为 左子树节点
			node = node->leftChild;
		}
		else
		{
			// 否则当前节点赋值为右子树节点
			node = node->rightChild;
		}
		// 释放当前节点
		free(tempNode);
	}

	// 返回新的当前节点 
	return node;
}

/*
\ brief 	求树高
\ param 	节点指针
*/
size_t treeHight(TreeNodePtr node)
{
	// 如果当前节点为NULL
	if (!node)
	{
		// 返回当前节点的树高为 0
		return 0;
	}
	// 求出 左子树的树高
	size_t leftHight = treeHight(node->leftChild);
	// 求出右子树的树高
	size_t rightHight = treeHight(node->rightChild);
	// 返回当前节点的高度
	return (leftHight > rightHight ? leftHight: rightHight) + 1;
}

/*
\ brief 	中序遍历二叉树
\ param 	节点指针
*/
void forEach(TreeNodePtr node)
{
	if (!node)
	{
		return NULL;
	}
	forEach(node->leftChild);
	printf("%d\n", node->element);
	forEach(node->rightChild);
}

int main(int argc, char* argv[])
{
	TreeNodePtr tree = insert(6, NULL);
	insert(78, tree);
	insert(45, tree);
	insert(26, tree);
	insert(10, tree);
	insert(166, tree);
	forEach(tree);
	printf(" hight = %u", treeHight(tree));
	makeEmpty(tree);
	return 0;
}

文章时间 2019年12月4日11:00:26

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值