二叉树递归实现

本文详细介绍了二叉树的递归实现,包括创建、前序、中序、后序遍历,以及层次遍历、计算深度、双分支节点数、单分支节点数和叶子节点数等操作。提供了C、C++、Java三种语言的代码示例。
摘要由CSDN通过智能技术生成

==> 学习汇总(持续更新)
==> 从零搭建后端基础设施系列(一)-- 背景介绍


二叉树递归实现比较符合树的特点,也较容易理解,代码也较为简单。接下来进行图文详解。

C代码下载
C++代码下载
java代码下载
( 备用地址下载)

导航
1.创建二叉树
2.前序遍历二叉树
3.中序遍历二叉树
4.后序遍历二叉树
5.层次遍历二叉树
6.计算二叉树深度
7.计算二叉树双分支节点数
8.计算二叉树单分支节点数
9.计算二叉树叶子节点数
10.添加节点
11.查找二叉树中的节点

注:有一些重复的代码且多的就不重复贴出来了,需要的可以点上面的链接去下载。







一、创建二叉树

按照前序遍历来创建,给定一个串,其规则是空格代表空节点
例如给定串:ABC D EF G ;
创建步骤如下:

这个回溯就相当于出栈一样,当条件不满足了,就return,然后系统维护的栈就会执行出栈操作,这样就相当于回溯了。由此可以推出非递归的实现方式。

1.C语言实现

/*
* function				创建二叉树(前序创建)
* param     PBTree*		root(二级指针)
* param		char* s     根据给定字符串创建
* param     int i       指示当前s的下标
* return                无
*/
void CreateBTree(PBTree* root, char* s, bool newTree)
{
	static int i = -1;		//s下标
	if (newTree) i = -1;	//因为i是静态变量,所以只能手动修改它值,如果是新的二叉树则重置它的值
	++i;
	//如果s为空,即二叉树为空,若s=""或者到达末尾s[i]都等于'\0',这里空节点用空格表示
	if (!s || s[i] == '\0' || s[i] == ' ')
		(*root) = NULL;
	else
	{
		*root = (PBTree)malloc(sizeof(_binary_tree));	//创建节点
		(*root)->data = s[i];
		CreateBTree(&(*root)->left, s, false);			//递归创建左子树	
		CreateBTree(&(*root)->right, s, false);			//递归创建右子树
	}
}

1).为什么要用二级指针?
因为传进去当参数的根节点指针会改变,所以需要一个指针的指针来一直的指向根节点

2).为什么要用静态变量i?
因为如果i不是静态的,每次递归的时候都会重置i的值。

3).为什么要加个bool标志?
因为i是静态的,只能手动去修改它的值,而只有当新创建一棵树的时候才需要重置,递归的时候是不需要的。

2.C++实现

/*
* function				创建二叉树
* param		Node**		二级指针
* param		string		s
* return                无
*/
template<typename T>
void BTree<T>::CreateBtree(Node** root, string s)
{
	++m_i;
	//如果s为空,即二叉树为空,若s=""或者到达末尾s[i]都等于'\0',这里空节点用空格表示
	if (s.empty() || s[m_i] == ' ')
		(*root) = nullptr;
	else
	{
		*root = new Node(s[m_i]);			//创建节点
		CreateBtree(&(*root)->left, s);		//递归创建左子树	
		CreateBtree(&(*root)->right, s);	//递归创建右子树
	}
}

这里不再需要静态变量,因为已经将下标作为成员变量了,只当新创建树的时候才重置它的值。

3.java实现

/*
	* function				创建二叉树
	* param		Node		node
	* param		string		s
	* return                返回根节点
	*/
	private Node createBtree(Node node, String s) {
		++iPos;
		//如果s为空,即二叉树为空,若s=""或者到达末尾s[i]都等于'\0',这里空节点用空格表示
		if (s.isEmpty() || s.charAt(iPos) == ' ')
			return null;
		else {
			node = new Node(s.charAt(iPos));
			node.setLeft(createBtree(node.getLeft(),s));
			node.setRight(createBtree(node.getRight(),s));
			return node;
		}
	}

一开始写这个java递归创建二叉树的时候,我就卡在了,如何记录根节点的地址,因为java中没有指针这种概念,所以二级指针这种方法就不可能了,网上有一篇博客说是可以写一个类模拟二级指针,但是这样又太复杂,我不想这样写。然后转啊转的,就找到了一个写得比较好的。这个思想和C/C++的差不多,就是最后会返回根节点回去,也许你会想,它为什么会返回根节点?因为第一个进栈的就是根节点,所以最后一个出栈的就是根节点。





二、前序遍历二叉树
遍历顺序是:根节点 -> 左节点 -> 右节点
然后一个二叉树又可以分为很多子树,每一颗子树都会有根、左、右节点,所以递归的思想就很好的体现在这里了。

1.C代码

/*
* function				前序遍历
* param		PBTree      root
* return                无
*/
void PreOrder(PBTree root)
{
	if (root)
	{
		printf("%c", root->data);
		PreOrder(root->left);
		PreOrder(root->right);
	}
}

2.C++代码

/*
* function				前序遍历
* param		Node*		root
* return                无
*/
template<typename T>
void BTree<T>::PreOrder(Node* root)
{
	if (root)
	{
		cout << root->data << " ";
		PreOrder(root->left);
		PreOrder(root->right);
	}
}

3.java代码

	/*
	* function				前序遍历
	* param		Node		root
	* return                无
	*/
	private void preOrder(Node root) {
		if (root != null) {
			System.out.print(root.data + " ");
			preOrder(root.left);
			preOrder(root.right);
		}
	}





三、中序遍历二叉树
遍历顺序是:左节点 -> 根节点 -> 右节点

1.C代码

/*
* function				中序遍历
* param		PBTree      root
* return                无
*/
void InOrder(PBTree root)
{
	if (root)
	{
		InOrder(root->left);
		printf("%c", root->data);
		InOrder(root->right);
	}
}

2.C++代码

/*
* function				中序遍历
* param		Node*		root
* return                无
*/
template<typename T>
void BTree<T>::InOrder(Node* root)
{
	if (root)
	{
		InOrder(root->left);
		cout << root->data << " ";
		InOrder(root->right);
	}
}

3.java代码

	/*
	* function				中序遍历
	* param		Node		root
	* return                无
	*/
	private void inOrder(Node root) {
		if (root != null) {
			inOrder(root.left);
			System.out.print(root.data + " ");
			inOrder(root.right);
		}
	}





四、后序遍历二叉树
遍历顺序:左节点 -> 右节点 -> 根节点

1.C代码

/*
* function				后序遍历
* param		PBTree      root
* return                无
*/
void PostOrder(PBTree root)
{
	if (root)
	{
		PostOrder(root->left);
		PostOrder(root->right);
		printf("%c", root->data);
	}
}

2.C++代码

/*
* function				后序遍历
* param		Node*		root
* return                无
*/
template<typename T>
void BTree<T>::PostOrder(Node* root)
{
	if (root)
	{
		PostOrder(root->left);
		PostOrder(root->right);
		cout << root->data << " ";
	}
}

3.java代码

/*
	* function				后序遍历
	* param		Node		root
	* return                无
	*/
	private void postOrder(Node root) {
		if (root != null) {
			postOrder(root.left);
			postOrder(root.right);
			System.out.print(root.data + " ");
		}
	}





五、层次遍历二叉树
从左到右遍历,这个需要一个辅助函数,这个函数负责遍历指定层数,然后主函数负责循环遍历层数。这个缺点就是,每一次的遍历都要从根节点开始,当层数很大的时候,遍历就会非常消耗时间。

/*
* function				层次遍历辅助函数
* param		PBTree      root
* param		int			level
* return                无
*/
void PrintNodeAtLevel(PBTree root, int level)
{
	// 空树或层级不合理
	if (NULL == root || level < 1)
		return;

	if (1 == level) //相当于输出根节点,因为每一个节点都可以左为子树的根节点
	{
		printf("%c", root->data);
		return;
	}

	// 左子树的 level - 1 级
	PrintNodeAtLevel(root->left, level - 1);

	// 右子树的 level - 1 级
	PrintNodeAtLevel(root->right, level - 1);
}





六、计算二叉树深度
先计算左子树深度,再计算右子树深度,然后返回较大的那个+1

/*
* function				计算二叉树深度
* param		PBTree      root
* return                返回二叉树深度
*/
int BTreeDepth(PBTree root)
{
	if (!root)
		return 0;
	else
	{
		int lDepth = BTreeDepth(root->left);   //递归计算左子树的深度
		int rDepth = BTreeDepth(root->right);  //递归计算右子树的深度
											//返回较深的那个+1
		if (lDepth >= rDepth)
			return lDepth + 1;
		else
			return rDepth + 1;
	}
}





七、计算二叉树双分支节点数
用前序遍历的方法,一个个节点遍历过去,如果该节点不为空,则判断它是否有左孩子和有孩子,如果两个都有,则count + 1

/*
* function				计算二叉树双分支节点数
* param		PBTree      root
* return                返回二叉树双分支节点数
*/
int GetN2(PBTree root, bool newTree)
{
	static int count = 0;
	if (newTree) count = 0;
	if (root == NULL)   //如果二叉树为空,则返回0
		return 0;
	else
	{
		if (root->left && root->right)  //当该节点有两个分支的时候+1
			++count;
		GetN2(root->left, false);    //遍历左子树
		GetN2(root->right, false);   //遍历右子树
	}
	return count;
}





八、计算二叉树单分支节点数
和计算双分支节点的方法一样,只需要把判断语句改一下即可

if ((root->left && !root->right) || (!root->left && root->right))  //当该节点仅且只有一个分支的时候+1
			++count;





九、计算二叉树叶子节点数
这个就简单了,有一个公式: n0 = n2 + 1

/*
* function				计算二叉树终端节点数
* param		PBTree      root
* return                二叉树终端节点数
*/
int GetN0(PBTree root)
{
	return GetN2(root, true) + 1; //计算公式n0 = n2 + 1;
}





十、添加节点
可以用前序、中序、后序、层次遍历的方法来添加,前三个的缺点很明显,最后添加后可能会退化成一个长长的单链表。所以这里采用层次遍历的方法添加,一层层扫描,遇到空节点就添加在它那里。

/*
* function				添加节点的辅助函数
* param		PBTree      root
* param		int			level
* param		char		ch
* param		bool*		标记是否添加成功
* return                无
*/
void LevelAdd(PBTree root, int level,char ch,bool* bAdd)
{
	//用来标记新的节点是否已经添加,如果添加了就退出了,避免重复添加
	if (*bAdd)return;
	//如果该节点为空,则可以将ch赋值给该节点
	if (!root->left || !root->right)
	{
		PBTree node = (PBTree)malloc(sizeof(_binary_tree));	//创建节点
		node->data = ch;
		node->left = NULL;
		node->right = NULL;
		if(!root->left)
			root->left = node;
		else
			root->right = node;
		*bAdd = true;
		return;
	}
	//层级不合理
	if (level < 1)
		return;
	//递归结束条件
	if (1 == level)
		return;

	// 左子树的 level - 1 级
	LevelAdd(root->left, level - 1, ch, bAdd);

	// 右子树的 level - 1 级
	LevelAdd(root->right, level - 1, ch, bAdd);
}

/*
* function				添加节点值(添加的位置是不确定的)
* param		PBTree      root
* param		char	    ch
* return                无
*/
void AddValue(PBTree root,char ch)
{
	//采用层次遍历的办法,一层层扫描,若有空的地方,则添加到该地方
	if (NULL == root) //如果此时二叉树为空,则创建根节点
	{
		root = (PBTree)malloc(sizeof(_binary_tree));	//创建节点
		root->data = ch;
		root->left = NULL;
		root->right = NULL;
		return;
	}	
	int depth = BTreeDepth(root);

	bool bAdd = false;   //标记是否添加成功,避免重复添加
	for (int i = 1; i <= depth; i++)
	{
		if (bAdd)   //如果已经添加成功,则退出
			break;
		LevelAdd(root, i, ch, &bAdd);
	}		
}

十一、查找二叉树中的节点

用前序遍历的方法查找

/*
* function				查找该值
* param		PBTree      root
* param		char	    ch
* param		bool	    标志是否是第一次查找,如果是第一次要将标志重置,因为静态变量要手动重置它的值
* return                若存在则返回true,否则返回false
*/
bool Search(PBTree root, char ch,bool first)
{
	static bool bFind = false;
	if (first) bFind = false;
	if (bFind)return true;    //如果已经找到了就不需要继续查找了
	//利用前序遍历来查找
	if (root)
	{
		if (root->data == ch)
			bFind = true;
		Search(root->left, ch, false);
		Search(root->right, ch, false);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值