二叉树的遍历(前 中 后序 )递归 非递归算法

    机会是留个有准 备的人的,如果你没有得到机会,那只能说明你准备不足。 

    又一次的机会失掉了,好像已经错过了太多的机会,后悔也是没用的,好好总结,继续往前走吧。

    这个二叉树,总是感觉并不是难的,尤其是前中后序的遍历,可是人们总是想当然的以为,真正的自己写时,才发现,原来一切都高估自己了,尤其是用笔写在纸上,如果你能一次写正确,而且不需要涂改,那么,你真的很牛了(个人比较菜)。二叉树,多么经典的数据结构,为什么自己没有早点认认真真的写一遍呢!好了,不罗嗦了,还是进入正题吧。 二叉树的性质,我就不提了,应该都知道。我在这里主要是写了一下二叉树的遍历,包括递归算法和非递归算法。递归算法非常的简单,几行的代码。其实,将递归的算法转变成非递归的算法,其实就是用一个数据结构来压栈,递归,本来就是一个压栈的过程,所以说栈这种数据结构是有记忆性的。所以在写非递归算法时,我们需要额外的空间来压栈。在这里我直接使用了STL中的stack,其实也可以自己建立一个数组,通过压栈过程中调整top指针的位置也能实现栈的功能。 我先顶一个两个struct,一个是Tree,一个是node其中Tree就是二叉树结构。node包含一个二叉树节点和一个标记值,此值代表的意思是此节点的右孩子是否访问过,若访问过则为true,否则为false。定义如下:

struct Tree
{
	int data;
	Tree *left;
	Tree *right;
};
struct node
{
	Tree *root;
	bool mask;
};

    接着,我定义了一个创建二叉树的函数,也是用递归来实现的。代码如下:

Tree *CreateBinaryTree()
{
	Tree *root;
	int n;
	cin >> n;
	if(0 == n)
	{
		return NULL;
	}
	else
	{
		root = new Tree;
		root->data = n;
		cout << "输入节点" << root->data << "的左孩子:";
		root->left = CreateBinaryTree();
		cout << "输入节点" << root->data << "的右孩子:";
		root->right = CreateBinaryTree();

		return root;
	}
}

   此函数,在输入时,当输入为0时,表示孩子为空,我测试时的输入序列为:1 2 4 8 0 0 9 0 0 5 10 0 0 0 3 6 0 0 7 0 11 0 0,呵呵,挺麻烦的。

   下面是递归算法,实现,前序 中序 后序的遍历。代码如下:

// 递归
void PreOrderTravel(Tree *root)
{
	if(root == NULL)
		return;
	printf("%d ",root->data);
	PreOrderTravel(root->left);
	PreOrderTravel(root->right);
}
void InOrderTravel(Tree *root)
{
	if(root == NULL)
		return;
	InOrderTravel(root->left);
	printf("%d ",root->data);
	InOrderTravel(root->right);
}
void BackOrderTravel(Tree *root)
{
	if(root == NULL)
		return;
	BackOrderTravel(root->left);
	BackOrderTravel(root->right);
	printf("%d ",root->data);
}

    递归是算法简单吧,就是短短的几行,而且前序 中序 后序,只是调整了一下输出时的位置。

    下面是非递归算法,对于前序和中序的非递归算法,对于我来说不算太难,主要是写的认真,而且要考虑清楚整个流程,这样应该会很快写出来。我在写后序遍历的非递归算法时,着实花费了好长时间,主要是我不清楚这个标记怎么来做。在这里,记录一下:根据后序遍历的特点,根节点肯定是最后输出,而且根节点输出位置在右孩子后面,所以,我们标记时,可以这样,如果当前节点有右孩子,而且没有被访问过,那么当前节点肯定不能输出,只有当当前节点没有右孩子,或者右孩子已经访问过了以后才可以输出当前节点,主要思想就是这样,标记的右孩子是否被访问过。代码如下:

// 非递归
void PreOrderTravel(Tree *root,stack<Tree *> st)
{
	// 依次沿左孩子压栈
	while(root)
	{
		printf("%d ",root->data); // 输出头结点
		st.push(root);
		root = root->left;
	}

	while(st.size() != 0)
	{
		root = st.top();
		st.pop();
		if(root->right)			 // 判断栈顶元素是否有右孩子
		{				 // 如果右孩子存在,则沿左孩子依次压栈
			root = root->right;
			while(root)
			{
				printf("%d ",root->data);
				st.push(root);
				root = root->left;
			}
		}
	}
}
void InOrderTravel(Tree *root,stack<Tree*> st)
{
	while(root)
	{
		st.push(root);
		root = root->left;
	}

	while(st.size() != 0)
	{
		root = st.top();
		printf("%d ",root->data);
		st.pop();
		if(root->right)
		{
			root = root->right;
			while(root)
			{
				st.push(root);
				root = root->left;
			}
		}
	}
}

void BackOrderTravel(Tree *root,stack<node *> st)// 后序遍历的非递归算法,需要一个标记,来指示右孩子是否被访问过
{						// 如果右孩子存在,且访问过,或者,右孩子不存在,那么输出当前节点
	while(root)				// 如果右孩子存在,但没有访问过,那么标记当前为1,依次沿左孩子压栈
	{
		node *tmp = new node;
		tmp->root = root;
		tmp->mask = 0;
		st.push(tmp);
		root = root->left;
	}

	while(st.size()!= 0)
	{
		node *t2 = st.top();
		root = t2->root;
		if((root->right && t2->mask) || (root->right == NULL) )
		{
			printf("%d ",root->data);
			st.pop();
		}
		else
		{
			t2->mask = 1;
			root = root->right;
			while(root)
			{
				node *t3 = new node;
				t3->root = root;
				t3->mask = 0;
				st.push(t3);
				root = root->left;
			}
		}
	}
}

    上面就是完整的代码,写一遍,好让自己记忆吧,如果来访者发现错误,可以留言啊,大家共同学习吧,一起向着大牛前进。。。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值