二叉树非递归实现

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


二叉树非递归实现会比较难理解一点,不过只要理解了非递归的,那么递归的就非常好理解了。接下来进行图文详解。

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

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

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







一、创建二叉树

按照前序遍历来创建,给定一个串,其规则是空格代表空节点
例如给定串:ABC D EF G ;
创建步骤如下:
二叉树非递归实现_第1张图片
二叉树非递归实现_第2张图片

1.C代码

/*
* function				创建二叉树(前序创建)
* param		char* s     根据给定字符串创建	
* return                返回二叉树的根节点
*/
PBTree CreateBTree(char* s)
{
	if (!s || s[0] == '\0')  //如果s为空 则树也为空
		return NULL;
	Stack stack = CreateStack();  //创建一个栈
	int i = 0;
	// 1.先创建根节点
	PBTree root = CreateNode(s[i++]);
	PBTree btree = root;		//用一个临时的指针代替root,因为最后要返回root指针,所以它不能改变
	Push(&stack,root);			//将根节点压入栈中
	int len = _csize(s);
	while (i < len)  
	{
		// 2.如果当前读到的字符不为空,并且当前节点的左孩子不存在,则创建它
		if (s[i] != ' ' && btree->bLCExits == false)
		{
			btree->left = CreateNode(s[i]);	//创建左孩子
			btree->bLCExits = true;			//左孩子存在
			btree = btree->left;
			Push(&stack, btree);  //入栈
			++i;
		}
		// 3.如果当前读到的字符不为空,并且当前节点的右孩子不存在,则创建它
		else if (s[i] != ' ' && btree->bRCExits == false)
		{
			btree->right = CreateNode(s[i]);//创建右孩子
			btree->bRCExits = true;			//右孩子存在
			btree = btree->right;
			Push(&stack, btree);  //入栈
			++i;
		}
		// 4.如果当前读到的字符为空,并且当前节点的左孩子不存在,则将当前节点的左孩子置为存在(空也算孩子)
		else if (s[i] == ' ' && btree->bLCExits == false) //空节点
			btree->bLCExits = true, ++i;  //左孩子存在
		// 5.如果当前读到的字符为空,并且当前节点的右孩子不存在,则将当前节点的右孩子置为存在(空也算孩子)
		else if (s[i] == ' ' && btree->bRCExits == false)
			btree->bRCExits = true, ++i;  //右孩子存在		

		// 6.回溯回去,当该节点的度为2的时候(就是左右孩子都存在的情况)
		if (btree->bLCExits && btree->bRCExits)
			Pop(&stack),btree = Top(&stack);
	}
	DestroyStack(&stack);  //最后销毁栈
	return root;
}

2.C++代码

/*
* function				创建二叉树(前序创建)
* param		char* s     根据给定串创建
* return                无
*/
template
void NonRecursionBTree::Create(string s)
{
	if (s.empty())  //如果s为空 则树也为空
		return ;
	stack st;	//创建一个栈
	int i = 0;
	// 1.先创建根节点
	m_root = new Node(s[i++]);
	Node* btree = m_root;		//用一个临时的指针代替root,因为最后要返回root指针,所以它不能改变
	st.push(btree);				//将根节点压入栈中
	int len = s.size();
	while (i < len)
	{
		// 2.如果当前读到的字符不为空,并且当前节点的左孩子不存在,则创建它
		if (s[i] != ' ' && btree->bLCExits == false)
		{
			btree->left = new Node(s[i]);	//创建左孩子
			btree->bLCExits = true;			//左孩子存在
			btree = btree->left;
			st.push(btree);  //入栈
			++i;
		}
		// 3.如果当前读到的字符不为空,并且当前节点的右孩子不存在,则创建它
		else if (s[i] != ' ' && btree->bRCExits == false)
		{
			btree->right = new Node(s[i]);	//创建右孩子
			btree->bRCExits = true;			//右孩子存在
			btree = btree->right;
			st.push(btree);  //入栈
			++i;
		}
		// 4.如果当前读到的字符为空,并且当前节点的左孩子不存在,则将当前节点的左孩子置为存在(空也算孩子)
		else if (s[i] == ' ' && btree->bLCExits == false) //空节点
			btree->bLCExits = true, ++i;  //左孩子存在
		// 5.如果当前读到的字符为空,并且当前节点的右孩子不存在,则将当前节点的右孩子置为存在(空也算孩子)
		else if (s[i] == ' ' && btree->bRCExits == false)
			btree->bRCExits = true, ++i;  //右孩子存在		

		// 6.回溯回去,当该节点的度为2的时候(就是左右孩子都存在的情况)
		if (btree->bLCExits && btree->bRCExits)
			st.pop(), btree = st.top();
	}
	while (!st.empty())  //最后销毁栈
		st.pop();
}

3.java代码

/*
	* function				创建二叉树(前序创建)
	* param		String s    根据给定串创建
	* return                无
	*/
	public void create(String s) {
		if (s.isEmpty())  //如果s为空 则树也为空
			return ;
		Stack st = new Stack();	//创建一个栈
		int i = 0;
		// 1.先创建根节点
		root = new Node(s.charAt(i++));
		Node btree = root;		//用一个临时的指针代替root,因为最后要返回root指针,所以它不能改变
		st.push(btree);				//将根节点压入栈中
		int len = s.length();
		while (i < len) {
			// 2.如果当前读到的字符不为空,并且当前节点的左孩子不存在,则创建它
			if (s.charAt(i) != ' ' && btree.bLCExits == false) {
				btree.left = new Node(s.charAt(i));	//创建左孩子
				btree.bLCExits = true;			//左孩子存在
				btree = btree.left;
				st.push(btree);  //入栈
				++i;
			}
			// 3.如果当前读到的字符不为空,并且当前节点的右孩子不存在,则创建它
			else if (s.charAt(i) != ' ' && btree.bRCExits == false) {
				btree.right = new Node(s.charAt(i));	//创建右孩子
				btree.bRCExits = true;			//右孩子存在
				btree = btree.right;
				st.push(btree);  //入栈
				++i;
			}
			// 4.如果当前读到的字符为空,并且当前节点的左孩子不存在,则将当前节点的左孩子置为存在(空也算孩子)
			else if (s.charAt(i) == ' ' && btree.bLCExits == false) {//空节点
				btree.bLCExits = true;
				++i;  //左孩子存在
			}	
			// 5.如果当前读到的字符为空,并且当前节点的右孩子不存在,则将当前节点的右孩子置为存在(空也算孩子)
			else if (s.charAt(i) == ' ' && btree.bRCExits == false) {
				btree.bRCExits = true;
				++i;  //右孩子存在		
			}
				
			// 6.回溯回去,当该节点的度为2的时候(就是左右孩子都存在的情况)
			if (btree.bLCExits && btree.bRCExits) {
				st.pop();
				btree = st.peek();
			}
				
		}
		st.clear(); //最后销毁栈
	}

1).为什么要用两个标志位bLCExits和bRCExits?
这样子比较容易找到回溯点,就是当左右两个孩子都存在了就回溯,不能根据是否为空来判断是否回溯。






二、前序遍历二叉树
参考出处,点击跳转

遍历顺序是:根节点 -> 左节点 -> 右节点
然后一个二叉树又可以分为很多子树,每一颗子树都会有根、左、右节点。

二叉树非递归实现_第3张图片

/*
* function				前序遍历
* param		PBTree      root
* return                无
*/
void PreOrder(PBTree root)
{
	Stack stack = CreateStack();   //创建一个栈
	PBTree btree = root;           //创建临时指针指向root
	// 1. 若当前节点不为空,或者栈中元素不为空(相当于还没有遍历完所有节点)
	while (btree || !StackIsEmpty(&stack))
	{
		// 2. 先遍历左子树,一直到左子树为空为止。
		while (btree)
		{
			printf("%c", btree->data);
			Push(&stack,btree);
			btree = btree->left;
		}

		// 3.若栈中还有元素,则将当前btree赋值为它的右子树,然后再重复 1~2步骤
		if (!StackIsEmpty(&stack))
		{
			btree = Top(&stack);
			Pop(&stack);         
			btree = btree->right;
		}
	}
	printf("\n");
}





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

/*
* function				中序遍历
* param		PBTree      root
* return                无
*/
void InOrder(PBTree root)
{
	Stack stack = CreateStack();   //创建一个栈
	PBTree btree = root;           //创建临时指针指向root
	// 1. 若当前节点不为空,或者栈中元素不为空(相当于还没有遍历完所有节点)
	while (btree || !StackIsEmpty(&stack))
	{
		// 2. 先遍历左子树,一直到左子树为空为止。
		while (btree)
		{	
			Push(&stack, btree);
			btree = btree->left;
		}

		// 3.若栈中还有元素,则将当前btree赋值为它的右子树,然后再重复 1~2步骤
		if (!StackIsEmpty(&stack))
		{
			btree = Top(&stack);
			printf("%c", btree->data);   //遍历完左子树后输出它们的根节点
			Pop(&stack);
			btree = btree->right;
		}
	}
	printf("\n");
}





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

/*
* function				后序遍历
* param		PBTree      root
* return                无
*/
void PostOrder(PBTree root)
{
	Stack stack = CreateStack();	//创建一个栈
	PBTree cur;						//当前结点 
	PBTree pre = NULL;				//前一次访问的结点 
	Push(&stack,root);              //先将根节点入栈
	while (!StackIsEmpty(&stack))
	{
		cur = Top(&stack);          //这里的cur就像是每颗子树的根节点,然后重复这些步骤
		if ((!cur->left && !cur->right) ||
			(pre && (pre == cur->left || pre == cur->right)))
		{
			printf("%c", cur->data);  //如果当前结点没有孩子结点或者孩子节点都已被访问过 
			Pop(&stack);
			pre = cur;
		}
		else
		{
			if (cur->right != NULL)      //这里先将右孩子入栈,这样出栈的时候,左孩子就先出栈
				Push(&stack,cur->right);
			if (cur->left != NULL)
				Push(&stack,cur->left);
		}
	}
	printf("\n");
}





五、层次遍历二叉树

层次遍历就简单了,这个要用到队列
步骤为:
1.将根节点入队
2.将当前节点置为队头节点 (cur = front)
3.出队
4.访问当前节点
5.如果当前节点的左孩子不为空,左孩子入队
6.如果当前节点的右孩子不为空,有孩子入队
7.重复2~6步骤即可

这个就相当于访问上一层的节点的时候,将下一层的结点依次入队以待访问

/*
* function				层次遍历
* param		PBTree      root
* return                无
*/
void translevel(PBTree root)
{
	if (!root) return;
	Queue queue = CreateQueue();		//创建一个队列
	PBTree cur = root;					//当前节点
	QPush(&queue, root);				//先将根节点加入队列中
	while (!QueueIsEmpty(&queue))		//当队列中没有元素时,遍历完成 
	{
		cur = Front(&queue);			//获取当前队头元素
		QPop(&queue);                   //遍历过后就出队
		printf("%c", cur->data);		//先输出该子树的根节点
		if (cur->left)					//如果左孩子不为空,则入队等待遍历
			QPush(&queue, cur->left);   
		if (cur->right)				    //如果右孩子不为空,则入队等待遍历
			QPush(&queue, cur->right);
	}
	printf("\n");
}





六、计算二叉树深度
利用层次遍历来求,遍历的最大层数即深度

/*
* function				计算二叉树深度
* param		PBTree      root
* return                无
*/
int BTreeDepth(PBTree root)
{
	if (!root) return 0;
	Queue queue = CreateQueue();		//创建一个队列
	PBTree cur = root;					//当前节点
	int iDepth = 0;
	QPush(&queue, root);					//先将根节点加入队列中
	while (!QueueIsEmpty(&queue))			//当队列中没有元素时,遍历完成 
	{
		++iDepth;							//外层循环控制层数
		int curLevelNodes = queue.count;	//当前层数的节点数
		int temp = 0;						//临时记录已经遍历的节点数

		while (temp++ < curLevelNodes)		//当遍历完当前层数后,退出内层循环,继续遍历下一层
		{
			cur = Front(&queue);			//获取当前队头元素
			QPop(&queue);                   //遍历过后就出队
			if (cur->left)					//如果左孩子不为空,则入队等待遍历
				QPush(&queue, cur->left);

			if (cur->right)				    //如果右孩子不为空,则入队等待遍历
				QPush(&queue, cur->right);
		}
	}
	return iDepth;
}





七、计算二叉树双分支节点数
利用前序遍历来求

/*
* function				计算二叉树双分支节点数
* param		PBTree      root
* return                无
*/
int GetN2(PBTree root)
{
	Stack stack = CreateStack();   //创建一个栈
	PBTree btree = root;           //创建临时指针指向root
	int count = 0;
	//利用前序遍历来访问所有的节点
	while (btree || !StackIsEmpty(&stack))
	{	
		while (btree)
		{
			Push(&stack, btree);
			btree = btree->left;
		}
		if (!StackIsEmpty(&stack))
		{
			btree = Top(&stack);
			if (btree)                            //再这里能保证所有的节点都能被遍历到
				if (btree->left && btree->right)  //当该节点有两个分支的时候+1
					++count;
			Pop(&stack);
			btree = btree->right;
		}
	}
	return count;
}





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

if (btree)                            //再这里能保证所有的节点都能被遍历到
				if ((btree->left && !btree->right) || (!btree->left && btree->right))  //当该节点仅且只有一个分支的时候+1
					++count;





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

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





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

/*
* function				添加节点值(添加的位置是不确定的)
* param		PBTree      root
* param		char	    ch
* return                无
*/
void AddValue(PBTree root, char ch)
{
	//采用层次遍历的办法,一层层扫描,若有空的地方,则添加到该地方
	if (!root)
	{
		root = CreateNode(ch);
		return;
	}	
	Queue queue = CreateQueue();		//创建一个队列
	PBTree cur = root;					//当前节点
	QPush(&queue, root);				//先将根节点加入队列中
	while (!QueueIsEmpty(&queue))		//当队列中没有元素时,遍历完成 
	{
		cur = Front(&queue);			//获取当前队头元素
		QPop(&queue);                   //遍历过后就出队
		if (cur->left)					//如果左孩子不为空,则入队等待遍历
			QPush(&queue, cur->left);
		else                            //否则就添加值,然后退出
		{
			cur->left = CreateNode(ch);
			break;
		}
		if (cur->right)				    //如果右孩子不为空,则入队等待遍历
			QPush(&queue, cur->right);
		else                            //否则就添加值,然后退出
		{
			cur->right = CreateNode(ch);//否则就添加值,然后退出
			break;
		}
	}
}

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

用层次遍历的方法查找

/*
* function				查找值
* param		PBTree      root
* param		char	    ch
* return                成功返回true,否则返回false
*/
bool Search(PBTree root, char ch)
{
	//利用层次遍历来查找
	if (!root) return false;
	Queue queue = CreateQueue();		//创建一个队列
	PBTree cur = root;					//当前节点
	QPush(&queue, root);				//先将根节点加入队列中
	while (!QueueIsEmpty(&queue))		//当队列中没有元素时,遍历完成 
	{
		cur = Front(&queue);			//获取当前队头元素
		if (cur->data == ch)
			return true;
		QPop(&queue);                   //遍历过后就出队
		if (cur->left)					//如果左孩子不为空,则入队等待遍历
			QPush(&queue, cur->left);
		if (cur->right)				    //如果右孩子不为空,则入队等待遍历
			QPush(&queue, cur->right);
	}
	return false;
}

你可能感兴趣的:(数据结构)