非递归实现二叉树先序、中序、后序遍历(栈实现)

本篇文章主要详解利用栈的方式二叉树先序、中序、后序遍历的非递归写法

首先我们需要实现一颗二叉树。

以下是通过先序序列建树的代码

例如:先序序列{1,2,4,10,'#','#',6,11,'#','#',7'#','#','#',3,8,'#','#',9}代表以下的树


typedef struct Node
{
	int _data;
	Node* _right;
	Node* _left;
	Node()
	{}
	Node(int x)
	{
		_data = x;
		_left = NULL;
		_right = NULL;
	}
}*pNode;

//参数类型arr 数组指针,index 下标,size 数组元素个数,invalid代表先序遍历时遇到的NULL
pNode createTree_PrevOrder( const int* arr, size_t& index,const size_t size,int invalid){Node* root = NULL;if (index < size && arr[index] != invalid){root = new Node(arr[index]);root->_left = createTree_PrevOrder(arr, ++index, size, invalid);root->_right = createTree_PrevOrder(arr, ++index, size, invalid);}return root;}

先序非递归:

先序遍历时,每当我们压入一个结点,我们压入结点前对其进行访问。

void prevOrder_NR(pNode root)
{
	if (NULL == root)
		cout << "empty!" << endl;
	stack<pNode> s;
	pNode cur = root;
	while (cur || !s.empty())
	{
		while (cur) //判断:cur为空,遍历栈顶结点的右子树结点,cur不为空;访问cur,并遍历cur的左子树结点
		{
			cout << cur->_data << "->";//遍历一个结点则进行访问
			s.push(cur);     
			cur = cur->_left;
		}

		cur = s.top(); //此时cur为空,因此需要对cur进行处理
		s.pop();    //将栈顶结点的右子树结点压入栈前,将栈顶结点弹出,避免死循环
		cur = cur->_right;//在下一个while时将右子树结点压入栈
	}

	cout << "over"<<endl;
}

中序非递归:

中序时我们需要在遍历完左子树后访问根节点,再去遍历右子树,因此我们仅需依照先序遍历修改部分代码。

void inOrder_NR(pNode root)
{
	if (NULL == root)
		cout << "empty" << endl;
	stack<pNode> s;

	pNode cur = root;

	while (cur || !s.empty())
	{
		while (cur)//压入左子树结点
		{
			s.push(cur);
			cur = cur->_left;
		}

		cur = s.top();//取栈顶结点
		cout << cur->_data << "->" ;//访问左结点,此时栈顶节点有两种情况:1.左子树结点为空;2.左子树结点已被访问弹出
		s.pop();                   //弹出访问过的结点
		cur = cur->_right;         //将栈顶结点的右子树结点入栈
	}
	cout << "over" << endl;
}

后序遍历:

后序遍历时由于访问完左右子树后才能访问根结点,因此需要将根结点在栈内保留到左右子树被访问后,但同时会出现一个问题,当右子树弹出后遇到根结点又会将右子树结点压入栈中,造成死循环,因此我们需要在定义一个变量last代表最后一个访问的结点,当last与栈顶结点的右子树结点相同时,则不再将右子树结点压入栈中。

void pastOrder_NR(pNode root)
{
	if (NULL == root)
		cout << "empty" << endl;

	pNode cur = root;
	pNode last = NULL;
	stack<pNode> s;

	while (cur || !s.empty())
	{
		while (cur)//压入左子树结点
		{
			s.push(cur);
			cur = cur->_left;
		}

		cur = s.top();

		if (cur->_right && last != cur->_right)//考虑栈顶结点的右子树结点。存在且没被访问过,将右子树结点压入栈中
		{
			cur = cur->_right;
		}
		else if ((NULL == cur->_right) || (last == cur->_right))
			//右子树结点为空或者已经被访问过,则访问栈顶结点并弹出
		{
			cout << cur->_data << "->";
			last = cur;//更新last值
			s.pop(); 
			//cur置空作用在于当原栈顶结点被访问并弹出后,下一层while是将当前栈顶结点的左子树入栈,当前栈顶结点的左子树已经被遍历过,		
			//因此会造成死循环,所以将cur置空,直接考虑当前栈顶结点的右子树            
			//一旦某个结点入栈,首先会遍历这个结点的左子树,然后考虑右子树的情况
			cur = NULL;
		}
	}cout << "over" << endl;
}


附带递归写法用于测试正确性

void prevOrder(pNode root)
{
	if (root == NULL)
		return;
	cout << root->_data << "->";
	prevOrder(root->_left);
	prevOrder(root->_right);
}
void pastOrder(pNode root)
{
	if (root == NULL) return;
	pastOrder(root->_left);
	pastOrder(root->_right);
	cout << root->_data << "->";
}

void inOrder(pNode root)
{
	if (root == NULL) return;
	inOrder(root->_left);
	cout << root->_data << "->";
	inOrder(root->_right);
	
}
void test()
{
	int arr[] = { 1, 2, 4, 10, '#', '#', 6, 11, '#', '#', 7,'#', '#', '#', 3, 8, '#', '#', 9 };
	size_t index = 0;
	pNode root=createTree_PrevOrder(arr, index, sizeof(arr)/sizeof(arr[0]), '#');
	
	cout << "prevOrder_NR:" ;
	prevOrder_NR(root);
	cout << "prevOrder   :";
	prevOrder(root);
	cout << endl;
	cout << "inOrder_NR:";
	inOrder_NR(root);
	cout << "inOrder   :";
	inOrder(root);
	cout << endl;
	cout << "pastOrder_NR:";
	pastOrder_NR(root);
	cout << "pastOrder   :";
	pastOrder(root);
	cout << endl;
}


测试结果:

  • 14
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值